Spring Data JPA之动态查询

在日常工作中,Spring Data JPA的使用给我们带来了极大的方便,但是实际业务中很多场景需要支持动态查询。比如前端查询功能提供了很多查询条件,用户可以根据一部分条件进行查询,那么后端就需要支持可配置的查询服务。在使用mybatis等时,可以用动态查询的方式轻松搞定,但是对于初次使用Spring Data JPA的小白来说,着实有些困难。本人也是在实际工作中参考资料,一步步摸索,掌握了一定的方法,现跟大家分享。

通过了解发现,Spring Data JPA是支持动态查询的,需要我们的repo继承JpaSpecificationExecutor接口,使用的时候传入相应参数即可。先看一下JpaSpecificationExecutor接口:

public interface JpaSpecificationExecutor {
	
	T findOne(Specification spec);
	List findAll(Specification spec);
	Page findAll(Specification spec, Pageable pageable);
	List findAll(Specification spec, Sort sort);
	long count(Specification spec);
}

 其中,Pageable是分页查询用的,Sort是排序用的,比较简单,先不做介绍,本次主要介绍Specification。先看看它的定义:

public interface Specification {

	Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb);
}

Root是查询的根对象,CriteriaBuilder可以用来构建查询关系。下面以一个实例来介绍具体使用:

下面是一个保存学生信息的表t_student:

Spring Data JPA之动态查询_第1张图片

先构建一下查询条件的参数体:

public class StudentParam {
    private String name;   //姓名
    private String address;   //住址
    private String school;   //学校
    private String birthday;   //出生日期

    //省略get、set方法。
}

查询条件包含姓名、住址、学校、出生日期等。

看一下repo接口:

public interface StudentRepo extends CrudRepository, JpaSpecificationExecutor,
        PagingAndSortingRepository {
}

业务实现代码:

public List queryStudent(final StudentParam studentParam) {
        Specification specification = new Specification() {
            public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
                List predicates = new ArrayList();
                if (studentParam.getName() != null && !"".equals(studentParam.getName())) {
                    predicates.add(cb.equal(root.get("name"), studentParam.getName()));
                }
                if (studentParam.getStartDate() != null && !"".equals(studentParam.getStartDate())) {
                    predicates.add(cb.greaterThan(root.get("birthday"), studentParam.getStartDate()));
                }
                if (studentParam.getEndDate() != null && !"".equals(studentParam.getEndDate())) {
                    predicates.add(cb.lessThan(root.get("birthday"), studentParam.getEndDate()));
                }
                return cb.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
        List studentEntities = studentRepo.findAll(specification);
        List students = new ArrayList();
        for (StudentEntity studentEntity : studentEntities) {
            students.add(studentCopier.copy(studentEntity));
        }
        return students;
    }

说明:以其中一行代码:predicates.add(cb.equal(root.get("name"), studentParam.getName()));为例。"name"指定了实体的其中一个属性,studentParam.getName()是传入的姓名参数值,equal指的是等于。此段代码的含义是where条件,如:name='张三'。注意指定的属性名必须与查询实体的属性名一致,否则会报错。

测试代码:

@Test
    public void queryStudentTest() {
        StudentParam studentParam = new StudentParam();
        studentParam.setName("张三");
        studentParam.setStartDate("2001-03-01");
        studentParam.setEndDate("2002-03-01");
        List students = studentService.queryStudent(studentParam);
        for (Student student : students) {
            info(student.toString());
        }
    }

执行结果: 

Hibernate: 
    select
        studentent0_.id as id1_0_,
        studentent0_.address as address2_0_,
        studentent0_.birthday as birthday3_0_,
        studentent0_.name as name4_0_,
        studentent0_.school as school5_0_,
        studentent0_.tel as tel6_0_ 
    from
        t_student studentent0_ 
    where
        studentent0_.name=? 
        and studentent0_.birthday>? 
        and studentent0_.birthday

在执行中,我们发现Spring Data JPA根据配置的查询条件自动生成了条件语句:where
        studentent0_.name=?
        and studentent0_.birthday>?
        and studentent0_.birthday

上例中,将查询条件通过and关联起来,进一步,如果我们需要用的or,该如何实现?看下面:

public List queryStudent(final StudentParam studentParam) {
        Specification specification = new Specification() {
            public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
                List predicates = new ArrayList();
                if (studentParam.getName() != null && !"".equals(studentParam.getName())) {
                    predicates.add(cb.equal(root.get("name"), studentParam.getName()));
                }
                if (studentParam.getStartDate() != null && !"".equals(studentParam.getStartDate())) {
                    predicates.add(cb.greaterThan(root.get("birthday"), studentParam.getStartDate()));
                }
                if (studentParam.getEndDate() != null && !"".equals(studentParam.getEndDate())) {
                    predicates.add(cb.lessThan(root.get("birthday"), studentParam.getEndDate()));
                }
                //定义or的条件数组
                List orPredicates = new ArrayList();
                if (studentParam.getSchool() != null && !"".equals(studentParam.getSchool())) {
                    orPredicates.add(cb.equal(root.get("school"),  studentParam.getSchool()));
                }
                if (studentParam.getAddress() != null && !"".equals(studentParam.getAddress())) {
                    orPredicates.add(cb.equal(root.get("address"), studentParam.getAddress()));
                }
                //生成or的查询表达式
                Predicate orPredicate = cb.or(orPredicates.toArray(new Predicate[orPredicates.size()]));
                //与and结合起来
                predicates.add(orPredicate);
                return cb.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
        List studentEntities = studentRepo.findAll(specification);
        List students = new ArrayList();
        for (StudentEntity studentEntity : studentEntities) {
            students.add(studentCopier.copy(studentEntity));
        }
        return students;
    }

测试代码:

@Test
    public void queryStudentTest() {
        StudentParam studentParam = new StudentParam();
        studentParam.setName("张三");
        studentParam.setStartDate("2001-03-01");
        studentParam.setEndDate("2002-03-01");
        studentParam.setAddress("清江小区");
        studentParam.setSchool("西城中学");
        List students = studentService.queryStudent(studentParam);
        for (Student student : students) {
            info(student.toString());
        }
    }

执行结果:

Hibernate: 
    select
        studentent0_.id as id1_0_,
        studentent0_.address as address2_0_,
        studentent0_.birthday as birthday3_0_,
        studentent0_.name as name4_0_,
        studentent0_.school as school5_0_,
        studentent0_.tel as tel6_0_ 
    from
        t_student studentent0_ 
    where
        studentent0_.name=? 
        and studentent0_.birthday>? 
        and studentent0_.birthday

Spring Data JPA 支持的动态查询也非常强大,实际工作中可以根据情况灵活配置。希望我的分享能给需要的朋友一点帮助,不足之处,敬请斧正。

 

 

你可能感兴趣的:(Spring,Data,JPA)