Моя компания разработала удобную систему для генерации динамических запросов (фильтрация, сортировка, разбиение на страницы).В итоге отправляется 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) {}
.Но все же это большое количество конструкторов.