Spring Boot+JPA/Hibernate多列自定义排序分页查询方案咨询
自定义排序分页搜索API实现方案
实体类定义
首先对应数据库表创建JPA实体:
import jakarta.persistence.*; @Entity @Table(name = "TABLE_1") public class EntityRecord { @Column(name = "ENTITY_TYPE") private String entityType; @Column(name = "ENTITY_NAME") private String entityName; @Column(name = "PERSON_EID") private Long personEid; @Column(name = "PERSON_ROLE") private String personRole; // 生成无参/全参构造函数,以及所有字段的getter/setter }
核心实现:自定义排序的分页查询
针对ENTITY_TYPE的自定义顺序需求,推荐以下三种实现方式,优先选择数据库层面排序的方案(性能更优):
方式1:JPQL CASE表达式实现排序
通过JPQL的CASE语句为每个ENTITY_TYPE分配排序权重,直接在数据库层面完成排序,结合Spring JPA的分页能力:
import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; public interface EntityRecordRepository extends JpaRepository<EntityRecord, Long> { @Query(value = "SELECT er FROM EntityRecord er " + "ORDER BY " + "CASE er.entityType " + " WHEN 'Owner' THEN 1 " + " WHEN 'Exec' THEN 2 " + " WHEN 'President' THEN 3 " + "END ASC, " + "er.entityName ASC") Page<EntityRecord> findAllWithCustomSort(Pageable pageable); }
调用时,Pageable仅需传入分页参数(页码、每页数量),排序逻辑已固化在JPQL中。
方式2:Hibernate @Formula映射排序权重字段
在实体类中通过@Formula注解生成一个虚拟的排序权重字段,之后即可直接使用Spring JPA的标准排序机制:
修改实体类,添加权重字段:
import jakarta.persistence.*; @Entity @Table(name = "TABLE_1") public class EntityRecord { @Column(name = "ENTITY_TYPE") private String entityType; // 生成排序权重的虚拟字段 @Formula("CASE ENTITY_TYPE WHEN 'Owner' THEN 1 WHEN 'Exec' THEN 2 WHEN 'President' THEN 3 ELSE 4 END") private Integer entityTypeOrder; @Column(name = "ENTITY_NAME") private String entityName; @Column(name = "PERSON_EID") private Long personEid; @Column(name = "PERSON_ROLE") private String personRole; // 构造函数、getter/setter(需包含entityTypeOrder的getter) }
Repository层无需自定义查询,直接使用默认方法:
import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; public interface EntityRecordRepository extends JpaRepository<EntityRecord, Long> { }
调用时构造带排序规则的Pageable:
Sort sort = Sort.by(Sort.Order.asc("entityTypeOrder"), Sort.Order.asc("entityName")); Pageable pageable = PageRequest.of(pageNum, pageSize, sort); Page<EntityRecord> result = repository.findAll(pageable);
方式3:内存排序(仅适合小数据量)
如果数据量极小,可先分页查询数据,再在内存中完成自定义排序。此方式性能较差,不推荐大数据场景:
import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import java.util.Comparator; import java.util.List; import java.util.Map; @Service public class EntityRecordService { private final EntityRecordRepository repository; // 定义自定义排序权重映射 private static final Map<String, Integer> ENTITY_TYPE_SORT_WEIGHT = Map.of( "Owner", 1, "Exec", 2, "President", 3 ); public EntityRecordService(EntityRecordRepository repository) { this.repository = repository; } public Page<EntityRecord> search(Pageable pageable) { Page<EntityRecord> pageData = repository.findAll(pageable); List<EntityRecord> sortedList = pageData.getContent().stream() .sorted(Comparator.comparingInt(record -> ENTITY_TYPE_SORT_WEIGHT.getOrDefault(record.getEntityType(), 4)) .thenComparing(EntityRecord::getEntityName)) .toList(); return new PageImpl<>(sortedList, pageable, pageData.getTotalElements()); } }
搜索API实现
在Controller层暴露分页搜索接口:
import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class EntityRecordController { private final EntityRecordRepository repository; public EntityRecordController(EntityRecordRepository repository) { this.repository = repository; } @GetMapping("/api/entities") public Page<EntityRecord> getEntities( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size) { // 对应方式1的调用 // return repository.findAllWithCustomSort(PageRequest.of(page, size)); // 对应方式2的调用 Sort sort = Sort.by(Sort.Order.asc("entityTypeOrder"), Sort.Order.asc("entityName")); return repository.findAll(PageRequest.of(page, size, sort)); } }
注意事项
- 若
ENTITY_TYPE的可选值需动态调整,可将排序权重配置在application.yml中,通过@Value注入映射,避免硬编码。 - 方式1和方式2均为数据库层面排序,性能更优,推荐生产环境使用;方式3仅适合测试或数据量极小的场景。
- 若使用MySQL,可替换CASE表达式为
FIELD函数(如ORDER BY FIELD(ENTITY_TYPE, 'Owner', 'Exec', 'President'), ENTITY_NAME ASC),但此方式依赖数据库方言,跨数据库兼容性差。
内容的提问来源于stack exchange,提问作者MittulRazdan
相关产品推荐
相关产品推荐

