JPA使用Specification调用findAll方法时如何实现自定义字段排序规则
异常原因
你遇到的PropertyReferenceException是因为Spring Data JPA处理带Specification的查询时,会将Pageable传入的Sort字段与实体属性做映射校验,即使使用JpaSort.unsafe定义原生排序函数,这个校验步骤依然会触发,因此会把你传入的FIELD(Status, '0','6','1','3','7')当成实体属性去查找,自然找不到对应属性报错。
解决方案
这里提供两种常用的可行方案:
方案1:在Specification中追加排序逻辑(推荐)
将自定义排序逻辑直接写在Specification的查询构造中,不走Pageable的Sort参数,即可绕过属性校验,同时保留动态过滤的能力:
// 原来的动态条件构造逻辑保持不变 Specification<BestPracticeAdminList> spec = Specification.where(null); if(searchCriteria.getSearchText() != null) spec = spec.and(BestPracticeAdminListFilter.titleLike(searchCriteria.getSearchText())); if(searchCriteria.getFilters() != null) { if(searchCriteria.getFilters().getSubjectID() != null) spec = spec.and(BestPracticeAdminListFilter.hasAlternativeSubjectID(searchCriteria.getFilters().getSubjectID())); if(searchCriteria.getFilters().getCategoryID() != null) spec = spec.and(BestPracticeAdminListFilter.hasAlternativeSubjectCategoryID(searchCriteria.getFilters().getCategoryID())); if(searchCriteria.getFilters().getStatus() != null) spec = spec.and(BestPracticeAdminListFilter.hasStatus(searchCriteria.getFilters().getStatus())); } // 追加自定义排序逻辑 spec = spec.and((root, query, criteriaBuilder) -> { // 构造FIELD函数,注意你的Status是ORDINAL存储的枚举,对应值为整数,不需要加引号 Expression<Integer> statusSortRule = criteriaBuilder.function( "FIELD", Integer.class, root.get("status"), criteriaBuilder.literal(0), criteriaBuilder.literal(6), criteriaBuilder.literal(1), criteriaBuilder.literal(3), criteriaBuilder.literal(7) ); // 按自定义规则升序,也可追加其他排序字段 query.orderBy(criteriaBuilder.asc(statusSortRule)); return null; }); // Pageable不需要再传入Sort参数 Pageable pageRequest = PageRequest.of(searchCriteria.getPageNo(), searchCriteria.getPageSize()); Page<BestPracticeAdminList> pagedResults = bestPracticeAdminListRepository.findAll(spec, pageRequest);
该方案跨数据库兼容性好,只要修改criteriaBuilder.function的第一个参数为对应数据库的排序函数即可。
方案2:新增虚拟映射字段
在实体类中用@Formula注解定义一个虚拟字段,映射你要的排序规则,直接对该字段排序即可:
- 修改实体类,新增虚拟字段:
@Entity @Table public class BestPracticeAdminList implements Serializable { // 原有属性保持不变 // 新增虚拟排序字段,对应你的自定义排序规则 @Formula("FIELD(Status, 0,6,1,3,7)") private Integer statusOrder; // 原有get/set方法保持不变,新增statusOrder的get/set }
- 直接对虚拟字段做排序:
Sort sort = Sort.by(Sort.Direction.ASC, "statusOrder"); Pageable pageRequest = PageRequest.of(searchCriteria.getPageNo(), searchCriteria.getPageSize(), sort); Page<BestPracticeAdminList> pagedResults = bestPracticeAdminListRepository.findAll(spec, pageRequest);
注意事项
你的Status字段使用@Enumerated(EnumType.ORDINAL)存储,数据库中存储的是枚举对应的整数值,因此排序函数中传入的枚举匹配值不要加引号,否则会出现类型不匹配导致排序不生效的问题。
内容的提问来源于stack exchange,提问作者ashmedai82
相关产品推荐
相关产品推荐

