在日常工作中,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:
先构建一下查询条件的参数体:
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.
测试代码:
@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
Student{id='b6ef1f42e8a64367abc251af22246870', name='张三', address='菜市场', tel='12314512312311', school='西城中学', birthday='2001-03-11'}
Student{id='bc56bad3fd4e4604abe8a1c4ee2c1f66', name='张三', address='菜市场', tel='12314512312311', school='明湖中学', birthday='2002-02-11'}
Student{id='ffe6c2942c574d118fcfc4836580791d', name='张三', address='菜市场', tel='12314512312311', school='实验中学', birthday='2002-01-11'}
在执行中,我们发现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
and (
studentent0_.school=?
or studentent0_.address=?
)
Student{id='b6ef1f42e8a64367abc251af22246870', name='张三', address='菜市场', tel='12314512312311', school='西城中学', birthday='2001-03-11'}
Spring Data JPA 支持的动态查询也非常强大,实际工作中可以根据情况灵活配置。希望我的分享能给需要的朋友一点帮助,不足之处,敬请斧正。