【问题标题】:Hibernate custom query containing GROUPBY and HAVING包含 GROUP BY 和 HAVING 的 Hibernate 自定义查询
【发布时间】:2020-12-09 14:02:17
【问题描述】:

所以我有两个具有一对多关系的实体。一个form_collection 包含多个formform 包含一个列 version,它跟踪最新的表单。

form
+----+-----------------+---------+--------------------+
| id | description_key | version | form_collection_id |
+----+-----------------+---------+--------------------+
| 1  | desc1           | 1       | 1                  |
+----+-----------------+---------+--------------------+
| 2  | desc1           | 2       | 1                  |
+----+-----------------+---------+--------------------+
| 3  | desc2           | 1       | 1                  |
+----+-----------------+---------+--------------------+
| 4  | desc3           | 1       | 2                  |
+----+-----------------+---------+--------------------+
form_collection
+----+-------+
| id | name  |
+----+-------+
| 1  | coll1 |
+----+-------+
| 2  | coll2 |
+----+-------+

在我的 java 代码中,我只希望一对多关系仅包含具有相同描述键的每个表单的最新版本(类似于this article 中解释的软删除)。为了检索最新的表单,我想出了这个按预期工作的查询:

SELECT *
FROM form AS a
INNER JOIN (
    SELECT description_key, max(version) AS version
    FROM form
    GROUP BY description_key
) AS b
ON a.description_key = b.description_key AND a.version = b.version;

这只会返回第 2、3、4 行。

但是,我在将其应用于具有以下实体的 Hibernate 架构时遇到问题。这种过滤可以用注解来完成吗?

@Entity
@Table(name = "form", schema = "public")
public class FormDO extends BaseDO {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "form_collection_id")
    private FormCollectionDO formCollection;
}
@Entity
@Table(name = "form_collection", schema = "public")
public class FormCollectionDO extends BaseDO {

    @OneToMany(mappedBy = "formCollection")
    // can I add an annotation here to filter for only the once that have the highest versions?
    private List<FormDO> forms;
    
}

可以用类似的东西来完成吗?

@Where(clause = "version = SELECT max(version) FROM form GROUP BY description_key")

【问题讨论】:

标签: spring-boot hibernate jpa


【解决方案1】:

您可以尝试使用以下方法:

@Entity
@Table(name = "form_collection", schema = "public")
public class FormCollectionDO extends BaseDO {

    @ManyToOne
    @JoinColumnsOrFormulas({
        @JoinColumnOrFormula(column = @JoinColumn(name = "id", referencedColumn = "form_collection_id")),
        @JoinColumnOrFormula(formula = @JoinFormula(value = "(select max(version) from public.form sub_f where sub_f.form_collection_id = {alias}.id)", referencedColumnName = "version"))
    })
    private FormDO latestForm;
    
}

但一般来说,我建议您使用 DTO 方法,因为现在每次获取 FormCollectionDO 时都必须执行这个最大子查询。

我认为这是Blaze-Persistence Entity Views 的完美用例。

我创建了该库以允许在 JPA 模型和自定义接口或抽象类定义模型之间轻松映射,例如 Spring Data Projections on steroids。这个想法是您按照自己喜欢的方式定义目标结构(域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。

使用 Blaze-Persistence Entity-Views 的用例的 DTO 模型可能如下所示:

@EntityView(FormCollectionDO.class)
public interface FormCollectionDto {
    @IdMapping
    Long getId();
    String getName();
    @Limit(limit = "1", order = "version DESC")
    @Mapping("forms")
    FormDto getLatestForm();

    @EntityView(FormDO.class)
    interface FormDto {
        @IdMapping
        Long getId();
        String getDescription();
    }
}

查询是将实体视图应用于查询的问题,最简单的就是通过 id 进行查询。

FormCollectionDto a = entityViewManager.find(entityManager, FormCollectionDto.class, id);

Spring Data 集成让您可以像使用 Spring Data Projections 一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

它只会获取您实际需要的数据。此外,如果可能,使用 Blaze-Persistence Entity-Views 的解决方案将使用横向连接,这比连接谓词中的聚合更有效。

【讨论】:

    猜你喜欢
    • 2021-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-11
    • 1970-01-01
    • 2021-04-25
    • 2016-06-20
    相关资源
    最近更新 更多