如何在Spring Data Jpa中使用自然排序

时间:2016-09-12 13:57:18

标签: java oracle hibernate spring-data-jpa natural-sort

我有一个我要订购的表格列。问题是列值包含数字和文本。例如,结果现在按此顺序排序。

1. group one
10. group ten
11. group eleven
2. group two

但是我希望自然地订购结果,比如

1. group one
2. group two
10. group ten
11. group eleven

在查看Spring配置时,我似乎无法找到允许您执行此操作的选项。我使用Spring Pageable类设置我的订单字段和方向,另外使用JPA规范。该方法本身返回一个默认为前20个结果的页面。

我不相信Oracle支持开箱即用的自然订购,所以我有一个存储过程。使用此过程运行查询,我得到了所需的结果。

select ... from group order by NATURALSORT(group.name) asc

正如您所料,我希望通过将其包装在包含文本的每个有序列周围来使用该过程。同时保持使用可分页/页面和规范。我在这方面所做的研究指出了一个可能包括

的解决方案
  • 创建和实施自定义规范
  • 或者扩展SimpleJpaRepository以更改Sort对象的转换方式。

但我似乎没有找到允许我使用本机SQL设置顺序的方法。我找到设置顺序的唯一方法是调用orderBy并包含Order对象。

一般而言。

  1. 使用Spring Data Jpa,hibernate和Oracle数据库时,有没有办法全局启用自然排序
  2. 如果没有,如何在使用order by column方法的同时使用我的存储过程包装单个Page findAll(Pageable, Specifications)
  3. 提前致谢。

2 个答案:

答案 0 :(得分:2)

在深入研究Spring JPA和Hibernate的源代码之后,我设法找到了解决问题的方法。我很确定这不是解决它的好方法,但它是我唯一能找到的方法。

我最终通过扩展SingularAttributePath类为查询的“order by”部分实现了一个包装器。此类有一个render方法,用于生成插入实际查询的字符串。我的实现看起来像这样

@Override
public String render(RenderingContext renderingContext) {
  String render = super.render(renderingContext);
  render = "MYPACKAGE.NSORT(" + render + ")";
  return render;
}

接下来,我扩展了SimpleJpaRepository类中的Order转换功能。默认情况下,这是通过调用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种类型。

  • 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方面有相同或衍生问题的人。如果您有任何疑问或改进,请告诉我。

答案 1 :(得分:1)

我不知道有任何这样的功能。然而,您可以将数字存储在单独的列中,然后按该列排序,这样可以提供更好的排序性能作为额外的好处。