在深入研究了 Spring JPA 和 Hibernate 的源代码后,我设法找到了解决问题的方法。我很确定这不是解决问题的好方法,但这是我能找到的唯一方法。
我最终通过扩展SingularAttributePath 类为查询的“order by”部分实现了一个包装器。这个类有一个渲染方法,可以生成插入到实际查询中的字符串。我的实现是这样的
@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 类的自定义实现替换结果中的所有订单。另外,我扩展了 Pageable 类使用的 Sort 类,以控制哪些被包装,哪些不被包装(称为 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) 将返回列表添加到查询中。这就是后端。
为了控制换行条件,我还扩展了Pageable 使用的Sort 类(后面几行提到了这一点)。我想添加的唯一功能是在 Direction 枚举中有 4 种类型。
- 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));
}
最后我替换了PageableHandlerMethodArgumentResolver使用的SortHandlerMethodArgumentResolver。唯一要做的就是创建我的 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
我希望这个解决方案可以帮助那些在自然排序和 Spring JPA 方面遇到相同或衍生问题的人。如果您有任何问题或改进,请告诉我。