【问题标题】:How to use natural sort with Spring Data Jpa如何在 Spring Data Jpa 中使用自然排序
【发布时间】:2017-01-19 23:53:00
【问题描述】:

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

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 个结果的 Page。

我不相信 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) 方法?

提前致谢。

【问题讨论】:

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


    【解决方案1】:

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

    【讨论】:

    • 这不是一个真正的选项,因为名称是由用户设置的。所以我对该信息的内容没有影响。
    • 有没有办法将原生 SQL 添加到 findAll 方法生成的查询中?
    • @RobinHermans 那么,输入文本没有预定义的格式吗?意思是,该用户可以输入 "This is 2nd group" 并且它必须在排序顺序中的 "This is 10th group" 之前?
    • 是的。您在 Windows 资源管理器中获得的排序方式
    • @RobinHermans 抱歉,我不知道你可以在这种情况下使用什么。
    【解决方案2】:

    在深入研究了 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 方面遇到相同或衍生问题的人。如果您有任何问题或改进,请告诉我。

    【讨论】:

      猜你喜欢
      • 2014-05-20
      • 2016-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-06
      • 1970-01-01
      • 2016-12-10
      相关资源
      最近更新 更多