Покопавшись в исходном коде Spring JPA и Hibernate, мне удалось найти решение моей проблемы. Я почти уверен, что это не лучший способ решить эту проблему, но это единственный способ, который я смог найти.
В итоге я реализовал оболочку для части запроса «порядок по», расширив класс SingularAttributePath
. У этого класса есть метод рендеринга, который генерирует строку, которая вставляется в фактический запрос. Моя реализация выглядит так
@Override
public String render(RenderingContext renderingContext) {
String render = super.render(renderingContext);
render = "MYPACKAGE.NSORT(" + render + ")";
return render;
}
Затем я расширил функциональность преобразования заказов в классе SimpleJpaRepository. По умолчанию это делается путем вызова QueryUtils.toOrders(sort, root, builder)
. Но поскольку вызов метода было невозможно переопределить, я сам вызвал метод toOrder
и изменил результат по своему вкусу.
Это означает замену всех заказов в результате моей собственной реализацией класса SingularAttributePath
. В качестве дополнения я расширил класс Sort
, который используется классом Pageable
, чтобы контролировать, что будет упаковано, а что нет (так называемый NaturalOrder). Но я вернусь к этому через секунду. Моя реализация похожа на это (некоторые проверки не учтены)
// Call the original method to convert the orders
List<Order> orders = QueryUtils.toOrders(sort, root, builder);
for (Order order : orders) {
// Fetch the original order object from the sort for comparing
SingularAttributePath orderExpression = (SingularAttributePath) order.getExpression();
Sort.Order originalOrder = sort.getOrderFor(orderExpression.getAttribute().getName());
// Check if the original order object is instantiated from my custom order class
// Also check if the the order should be natural
if (originalOrder instanceof NaturalSort.NaturalOrderm && ((NaturalSort.NaturalOrder) originalOrder).isNatural()){
// replace the order with the custom class
Order newOrder = new OrderImpl(new NaturalSingularAttributePathImpl(builder, expression.getJavaType(), expression.getPathSource(), expression.getAttribute()));
resultList.add(newOrder);
}else{
resultList.add(order);
}
}
return resultList;
Затем список возврата добавляется к запросу путем вызова query.orderBy(resultlist)
. Это все, что касается серверной части.
Чтобы контролировать условие переноса, я также расширил класс Sort
, используемый Pageable
(упомянул об этом несколько строк назад). Единственная функциональность, которую я хотел добавить, - это наличие 4 типов в перечислении Direction.
- ASC (по умолчанию по возрастанию)
- DESC (по умолчанию по убыванию)
- NASC (нормальный восходящий)
- NDESC (нормальный по убыванию)
Последние два значения действуют только как заполнители. Они устанавливают логическое значение isNatural
(переменная расширенного класса Order
), которое используется в условии. Когда они преобразуются в запрос, они снова сопоставляются с вариантами по умолчанию.
public Direction getNativeDirection() {
if (this == NaturalDirection.NASC)
return Direction.ASC;
if (this == NaturalDirection.NDESC)
return Direction.DESC;
return Direction.fromString(String.valueOf(this));
}
Наконец, я заменил SortHandlerMethodArgumentResolver
, используемый PageableHandlerMethodArgumentResolver
. Единственное, что это делает, - это создание экземпляров моего класса NaturalSort
и передача их в объект Pageable вместо класса Sort
по умолчанию.
В конце концов, я могу вызвать ту же конечную точку REST, но результат будет отличаться по способу сортировки.
Default Sorting
/api/v1/items?page=0&size=20&sort=name,asc
Natural Sorting
/api/v1/items?page=0&size=20&sort=name,nasc
Я надеюсь, что это решение может помочь тем, у кого есть такая же или производная проблема, связанная с естественной сортировкой и пружинным JPA. Если у вас есть какие-либо вопросы или улучшения, дайте мне знать.
person
Robin Hermans
schedule
19.09.2016