首页 文章

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

提问于
浏览
1

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

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 对象 .

所以总的来说 .

  • 使用Spring Data Jpa,hibernate和Oracle数据库时,有没有办法全局启用自然排序

  • 如果没有,如何在仍然能够使用 Page findAll(Pageable, Specifications) 方法的同时用我的存储过程包装单个 order by column

提前致谢 .

2 回答

  • 1

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

  • 1

    在深入研究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 类使用它来控制被包裹的内容以及在一秒内得到的内容 . 我的实现看起来很接近(一些检查被遗漏)

    // 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 endpoints ,而结果的排序方式不同 .

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

相关问题