我公司开发了一个方便的系统来生成动态查询(过滤,排序,分页)。它最终将Specification
和Pageable
发送到JPA存储库,并且repo计算出完整的查询。它已经很好地工作了很多年。
最近我对其进行了更新,以便能够过滤子实体的属性(@OneToMany
或@ManyToMany
)。但是,当我这样做时,我需要强制生成的查询是不同的,否则,连接表可能会产生重复。如果你只是过滤,这可以很好用,但如果你过滤&对任何@OneToOne或@ManyToOne子属性进行排序,然后出现SQL错误。
Order by expression "HEADERCHILD3_.ID" must be in the result list in this case; SQL statement:
SELECT DISTINCT
header0_.HEADER_ID AS HEADER1_4_,
header0_.COL_STATUS_ID AS COL_STA10_4_,
header0_.CREATED_BY AS CREATED11_4_,
header0_.PRIORITY_ID AS PRIORIT16_4_,
header0_.REQUESTED_COMPLETION_DATE AS REQUESTE5_4_,
header0_.RESOLUTION_ID AS RESOLUT17_4_
FROM
DBO.REQUEST header0_
INNER JOIN
DBO.XREF_REQUEST_CHILD child1_
ON
header0_.HEADER_ID=child1_.HEADER_ID
INNER JOIN
DBO.CHILD headerchild2_
ON
child1_.DIVISION_ID=headerchild2_.ID
LEFT OUTER JOIN
DBO.CHILD headerchild3_
ON
header0_.PRIORITY_ID=headerchild3_.ID
LEFT OUTER JOIN
DBO.CHILD headerchild4_
ON
header0_.COL_STATUS_ID=headerchild4_.ID
LEFT OUTER JOIN
DBO.CHILD headerchild5_
ON
header0_.SL_STATUS_ID=headerchild5_.ID
WHERE
header0_.HEADER_ID=4
AND (
headerchild2_.ID IN (14 , 13 , 30))
AND (
header0_.CREATED_BY=10
OR header0_.COL_STATUS_ID<>4)
ORDER BY
header0_.REQUESTED_COMPLETION_DATE ASC,
headerchild3_.ID ASC,
headerchild4_.SHORT_TEXT ASC,
headerchild5_.SHORT_TEXT ASC,
header0_.HEADER_ID ASC
limit ?
因此,JPA不会自动将ORDER BY子句包含在select子句中。有没有办法强迫他们被包括在内?我错过了一些JPA“魔法”吗?
修改1
所以,如果我不能告诉JPA这样做,我可以重载SimpleJpaRepository
的方法getQuery()
@Override
protected <S extends T> TypedQuery<S> getQuery(Specification<S> spec, Class<S> domainClass, Sort sort) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<S> query = builder.createQuery(domainClass);
Root<S> root = applySpecificationToCriteria(spec, domainClass, query);
query.select(root);
if (sort != null) {
query.orderBy(toOrders(sort, root, builder));
if (query.isDistinct())
{
List<Selection<?>> selections = new ArrayList<>();
selections.add(root);
for (Order o : query.getOrderList())
{
Selection<?> s = o.getExpression();
selections.add(s);
}
query.multiselect(selections);
}
}
编辑3 这看起来应该是解决方案,除非我收到错误:
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: Unable to locate appropriate constructor on class [com.a.b.domain.Header].
Expected arguments are: com.a.b.domain.Header,
org.joda.time.LocalDate,
long,
java.lang.String,
java.lang.String,
long
我认为我不应该创建特定的构造函数,因为我不必为“普通”JPA操作这样做。但是,当我为每个可能的动态生成的排序创建适当的构造函数时,它似乎正常工作。 那么我如何摆脱创建构造函数的需要呢?
编辑4 实际上这不可扩展。对于动态排序,您需要创建所有可能的构造函数(参数的排序很重要),ballpark估计不超过2 ^ N个构造函数...其中N是您要排序的属性数。它小于那个,因为您可以删除具有相同参数类型列表的“重复”构造函数,并且您可以对分类器进行排序...
例如制作
public Header(String s1, LocalDate d1) {}
public Header(LocalDate d1, String s1) {}
等效。但是,这仍然是大量的构造函数。