我是否可以强制JPA / Hibernate在SELECT语句

时间:2018-05-18 15:18:22

标签: hibernate jpa spring-data spring-data-jpa

我公司开发了一个方便的系统来生成动态查询(过滤,排序,分页)。它最终将SpecificationPageable发送到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) {}

等效。但是,这仍然是大量的构造函数。

0 个答案:

没有答案