Hibernate custom query containing GROUPBY and HAVING

旧城冷巷雨未停 提交于 2021-01-01 09:52:45

问题


So I have two entities with a one-to-many relationship. A form_collection contains multiple form. The form contains a column version which keeps track of the most current form.

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 |
+----+-------+

In my java code I only want that the one-to-many relation ship only contains the most recent versions of each form with the same description key (similar to a soft delete as explained in this article). For retrieving the most recent forms I came up with this query which works as expected:

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;

This only returns rows 2, 3, 4.

However, I have problems applying this to my Hibernate schema with the following entities. Can this filtering be done with annotations?

@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;
    
}

Can it be done with something similar to this?

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


回答1:


You could try to use the following:

@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;
    
}

But generally, I would recommend you to use a DTO approach instead as this max subquery now has to be executed every time you fetch a FormCollectionDO.

I think this is a perfect use case for Blaze-Persistence Entity Views.

I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.

A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:

@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();
    }
}

Querying is a matter of applying the entity view to a query, the simplest being just a query by id.

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

The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

It will only fetch the data that you actually need. Also, the solution with Blaze-Persistence Entity-Views will use a lateral join if possible, which is much more efficient than an aggregation in a join predicate.



来源:https://stackoverflow.com/questions/65218310/hibernate-custom-query-containing-groupby-and-having

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!