【一目了然】Spring Data JPA使用Specification动态构建多表查询、复杂查询及排序示例

前言
发这篇的目的是为了提供一篇涵盖Specification各种写法的备忘。
Specification算是JPA中比较灵活的查询方式了,
也少不了Criteria类型安全和面向对象的优点。
之前都是点到即止的简单使用,刚好最近系统地使用了一下,遂有本文。

示例
(先贴用法,再贴环境配置。有不明白的地方欢迎留言讨论。)

  1. 用法

     @Service
     public class StudentService implements IStudentService {
    
     @Autowired
     private IStudentRepository repository;
    
      //无关代码略
    
     @Override
     public List<Student> getStudent(String studentNumber,String name ,String nickName,
         Date birthday,String courseName,float chineseScore,float mathScore,
         float englishScore,float performancePoints) {
     Specification<Student> specification = new Specification<Student>(){
    
         @Override
         public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
             //用于暂时存放查询条件的集合
             List<Predicate> predicatesList = new ArrayList<>();
             //--------------------------------------------
             //查询条件示例
             //equal示例
             if (!StringUtils.isEmpty(name)){
                 Predicate namePredicate = cb.equal(root.get("name"), name);
                 predicatesList.add(namePredicate);
             }
             //like示例
             if (!StringUtils.isEmpty(nickName)){
                 Predicate nickNamePredicate = cb.like(root.get("nickName"), '%'+nickName+'%');
                 predicatesList.add(nickNamePredicate);
             }
             //between示例
             if (birthday != null) {
                 Predicate birthdayPredicate = cb.between(root.get("birthday"), birthday, new Date());
                 predicatesList.add(birthdayPredicate);
             }
             
             //关联表查询示例
             if (!StringUtils.isEmpty(courseName)) {
                 Join<Student,Teacher> joinTeacher = root.join("teachers",JoinType.LEFT);
                 Predicate coursePredicate = cb.equal(joinTeacher.get("courseName"), courseName);
                 predicatesList.add(coursePredicate);
             }
             
             //复杂条件组合示例
             if (chineseScore!=0 && mathScore!=0 && englishScore!=0 && performancePoints!=0) {
                 Join<Student,Examination> joinExam = root.join("exams",JoinType.LEFT);
                 Predicate predicateExamChinese = cb.ge(joinExam.get("chineseScore"),chineseScore);
                 Predicate predicateExamMath = cb.ge(joinExam.get("mathScore"),mathScore);
                 Predicate predicateExamEnglish = cb.ge(joinExam.get("englishScore"),englishScore);
                 Predicate predicateExamPerformance = cb.ge(joinExam.get("performancePoints"),performancePoints);
                 //组合
                 Predicate predicateExam = cb.or(predicateExamChinese,predicateExamEnglish,predicateExamMath);
                 Predicate predicateExamAll = cb.and(predicateExamPerformance,predicateExam);
                 predicatesList.add(predicateExamAll);
             }
             //--------------------------------------------
             //排序示例(先根据学号排序,后根据姓名排序)
             query.orderBy(cb.asc(root.get("studentNumber")),cb.asc(root.get("name")));
             //--------------------------------------------
             //最终将查询条件拼好然后return
             Predicate[] predicates = new Predicate[predicatesList.size()];
             return cb.and(predicatesList.toArray(predicates));
         }
    
     
     };
     return repository.findAll(specification);
     }
    
    }
    
  2. 环境配置
    pom文件引入依赖(对此尚不了解的请自行补充maven相关知识)

     <!-- jpa -->
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-data-jpa</artifactId>
     </dependency>
    

Dao层文件继承JpaSpecificationExecutor。这边多继承了一个JpaRepository,不需要的可以自行去除。

@Repository
public interface IStudentRepository extends JpaRepository<Student, String>,JpaSpecificationExecutor<Student>{

}
  1. 一个第三方库
    如果你觉得每次实现接口重写toPredicate太麻烦了,也可以使用这个第三方库:jpa-spec
    ,能够减少一些代码量。

示例(更多示例请移步该库github查看)

public Page<Person> findAll(SearchRequest request) {
Specification<Person> specification = Specifications.<Person>and()
        .eq(StringUtils.isNotBlank(request.getName()), "name", request.getName())
        .gt(Objects.nonNull(request.getAge()), "age", 18)
        .between("birthday", new Range<>(new Date(), new Date()))
        .like("nickName", "%og%", "%me")
        .build();

return personRepository.findAll(specification, new PageRequest(0, 15));

}
maven依赖

<dependency>
<groupId>com.github.wenhao</groupId>
<artifactId>jpa-spec</artifactId>
<version>3.1.1</version>
4. 推荐阅读 SpringDataJpa的Specification查询 jpa-spec教程 5. 扩展阅读 SpringBoot环境下QueryDSL-JPA的入门及进阶 以上。 希望我的文章对你能有所帮助。 我不能保证文中所有说法的百分百正确,但我能保证它们都是我的理解和感悟以及拒绝复制黏贴。 有什么意见、见解或疑惑,欢迎留言讨论。
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值