Spring Boot 2中基于MSSQL与Hibernate Specification的自定义排序实现问询
实现自定义排序的方案(Hibernate Specification + MSSQL)
当然可以实现!而且完全能在数据库层面通过Hibernate Specification结合MSSQL的函数来搞定,不用在应用层拉取所有数据再做排序,性能更优。
核心排序逻辑拆解
先明确我们要达成的规则:
- 升序:先排
Q开头的条目(去掉Q后按数字升序),再排非Q开头的条目 - 降序:先排非
Q开头的条目,再排Q开头的条目(去掉Q后按数字降序)
要在数据库层面实现,我们需要用到MSSQL的几个关键函数配合Hibernate的Criteria API:
CASE:区分Q开头和非Q开头的条目,给不同排序优先级TRY_CAST:把Q后面的字符串转为整数(避免文本排序的坑,比如Q120不会排在Q19前面)SUBSTRING:截取Q后面的数字部分LIKE:判断字段是否以Q开头
具体代码实现
假设你的实体类是YourEntity,要排序的字段是code,下面是升序和降序的Specification实现:
1. 升序排序的Specification
public static Specification<YourEntity> orderByQCodeAsc() { return (root, query, cb) -> { // 第一步:给Q开头的条目设置更高优先级(0),非Q的设为1(确保排后面) Expression<Integer> sortPriority = cb.selectCase() .when(cb.like(root.get("code"), "Q%"), 0) .otherwise(1); // 第二步:提取Q后面的数字转成整数,非Q的设为最大整数(保证在Q类条目之后) Expression<Integer> numericCodePart = cb.selectCase() .when(cb.like(root.get("code"), "Q%"), cb.function("TRY_CAST", Integer.class, cb.substring(root.get("code"), 2), // 从第2位开始截取(跳过Q) cb.literal("int"))) .otherwise(Integer.MAX_VALUE); // 构建排序规则:先按优先级升序,再按数字部分升序,最后按原字段处理非Q条目的排序 query.orderBy(cb.asc(sortPriority), cb.asc(numericCodePart), cb.asc(root.get("code"))); return null; // 这里仅处理排序,无查询条件 }; }
2. 降序排序的Specification
public static Specification<YourEntity> orderByQCodeDesc() { return (root, query, cb) -> { // 第一步:给非Q开头的条目设置更高优先级(0),Q的设为1(确保排后面) Expression<Integer> sortPriority = cb.selectCase() .when(cb.not(cb.like(root.get("code"), "Q%")), 0) .otherwise(1); // 第二步:提取Q后面的数字转成整数,非Q的设为0(保证在Q类条目之前) Expression<Integer> numericCodePart = cb.selectCase() .when(cb.like(root.get("code"), "Q%"), cb.function("TRY_CAST", Integer.class, cb.substring(root.get("code"), 2), cb.literal("int"))) .otherwise(0); // 构建排序规则:先按优先级升序,再按数字部分降序,最后按原字段处理非Q条目的排序 query.orderBy(cb.asc(sortPriority), cb.desc(numericCodePart), cb.desc(root.get("code"))); return null; // 这里仅处理排序,无查询条件 }; }
关键细节说明
TRY_CAST的作用:相比CAST,TRY_CAST在转换失败时(比如Q后面不是数字)会返回null,而不是抛出错误,更安全。如果你的业务能保证Q后面全是数字,也可以换成CAST。substring的索引:Hibernate的substring和MSSQL一样是从1开始计数的,所以substring(root.get("code"), 2)会跳过第一个字符Q,直接截取后面的内容。- 非Q条目的排序:如果非Q开头的条目有多个,最后按原字段的升/降序排序,你可以根据业务需求调整这部分(比如去掉这部分排序,或者换成其他字段)。
使用方式
在调用findAll或者其他查询方法时,传入这个Specification即可:
List<YourEntity> sortedList = yourEntityRepository.findAll(orderByQCodeAsc());
这样生成的SQL会直接在MSSQL端执行排序,完全不需要在应用层处理数据,效率更高。
内容的提问来源于stack exchange,提问作者Vince V.
相关产品推荐
相关产品推荐

