问题
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