Могу ли я заставить JPA / Hibernate включить предложения ORDER BY в оператор SELECT - PullRequest
0 голосов
/ 18 мая 2018

Моя компания разработала удобную систему для генерации динамических запросов (фильтрация, сортировка, разбиение на страницы).В итоге отправляется Specification и Pageable в репозиторий JPA, и репо вычисляет полный запрос.Он работал отлично в течение ряда лет.

Недавно я обновил его, чтобы иметь возможность фильтровать свойства дочерних объектов (@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 ?

Таким образом, предложения ORDER BY автоматически не включаются ввыберите пункты по JPA.Есть ли способ заставить их быть включенными?Есть ли какая-то «магия» 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 На самом деле это не масштабируется.Для динамической сортировки вам нужно создать все возможные конструкторы (упорядочение параметров важно), приблизительная оценка не более 2 ^ N конструкторов ... где N - количество свойств, по которым вы хотите отсортировать.Это меньше, чем это, так как вы можете удалить «дубликаты» конструкторов, которые имеют те же списки типов параметров, и вы можете сортировать сортировщики ...

, например, делая эквивалент

public Header(String s1, LocalDate d1) {}
public Header(LocalDate d1, String s1) {}

.Но все же это большое количество конструкторов.

...