JPA Specification中通过FIND_IN_SET实现CriteriaQuery IN查询自定义排序
JPA Specification适配FIND_IN_SET实现IN查询结果按传入ID顺序排序方案
改动步骤
- 第一步:扩展操作枚举,新增两类操作值
在你定义的Operation枚举中新增IN查询和自定义排序枚举:
public enum Operation { // 原有枚举保持不变 EQUAL, MATCH, SORT_ASC, SORT_DESC, // 新增枚举 IN, // 标准IN查询操作 SORT_BY_IN_LIST // 按传入列表顺序排序操作 }
- 第二步:调整
SearchCriteria参数逻辑,支持传入List类型的参数值,适配IN查询和ID列表入参。 - 第三步:修改
toPredicate方法,新增对应分支逻辑,完整修改后代码如下:
@Override public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) { List<Predicate> predicates = new ArrayList<>(); for (SearchCriteria criteria : list) { switch (criteria.getOperation()) { // 原有逻辑保持不变 case EQUAL: predicates.add((Objects.isNull(criteria.getJoinMap()) || criteria.getJoinMap().isEmpty()) ? builder.equal(root.get(criteria.getKey()), criteria.getValue()) : builder.equal(getOrCreateJoins(root, criteria.getJoinMap()).get(criteria.getKey()), criteria.getValue())); break; case MATCH: predicates.add((Objects.isNull(criteria.getJoinMap()) || criteria.getJoinMap().isEmpty()) ? builder.like(builder.lower(root.get(criteria.getKey())), "%" + criteria.getValue().toString().toLowerCase() + "%") : builder.like( builder.lower(getOrCreateJoins(root, criteria.getJoinMap()).get(criteria.getKey())), "%" + criteria.getValue().toString().toLowerCase() + "%")); break; case SORT_ASC: query.orderBy((Objects.isNull(criteria.getJoinMap()) || criteria.getJoinMap().isEmpty()) ? builder.asc(root.get(criteria.getKey())) : builder.asc(getOrCreateJoins(root, criteria.getJoinMap()).get(criteria.getKey()))); break; case SORT_DESC: query.orderBy((Objects.isNull(criteria.getJoinMap()) || criteria.getJoinMap().isEmpty()) ? builder.desc(root.get(criteria.getKey())) : builder.desc(getOrCreateJoins(root, criteria.getJoinMap()).get(criteria.getKey()))); // 新增IN查询分支 case IN: Path<Object> inPath = (Objects.isNull(criteria.getJoinMap()) || criteria.getJoinMap().isEmpty()) ? root.get(criteria.getKey()) : getOrCreateJoins(root, criteria.getJoinMap()).get(criteria.getKey()); predicates.add(inPath.in((List<?>) criteria.getValue())); break; // 新增按传入列表顺序排序分支 case SORT_BY_IN_LIST: Path<Object> sortPath = (Objects.isNull(criteria.getJoinMap()) || criteria.getJoinMap().isEmpty()) ? root.get(criteria.getKey()) : getOrCreateJoins(root, criteria.getJoinMap()).get(criteria.getKey()); // 把传入的列表转为逗号分隔字符串,兼容数字、字符串类型的ID String idListStr = ((List<?>) criteria.getValue()).stream() .map(String::valueOf) .collect(Collectors.joining(",")); // 调用MySQL原生FIND_IN_SET函数 Expression<Integer> findInSetExpr = builder.function( "FIND_IN_SET", Integer.class, sortPath.as(String.class), builder.literal(idListStr) ); // 按函数返回的序号升序排序,匹配传入列表顺序 query.orderBy(builder.asc(findInSetExpr)); break; default: break; } } return isSearchOperationOr ? builder.or(predicates.toArray(new Predicate[0])) : builder.and(predicates.toArray(new Predicate[0])); }
使用说明
当你需要实现IN查询返回结果与传入ID顺序一致时,构造两个SearchCriteria对象即可:
- 第一个参数:operation为
IN,key为ID字段名,value为待查询的ID列表 - 第二个参数:operation为
SORT_BY_IN_LIST,key为ID字段名,value为和IN查询完全相同的ID列表
注意:本方案依赖MySQL方言,切换数据库时需要替换为对应数据库的自定义排序函数实现。
内容的提问来源于stack exchange,提问作者Veer Panchal
相关产品推荐
相关产品推荐

