Spring Data JPA带条件分页查询实现原理
最新SpringDataJPA官方参考手册Version2.0.0.RC2,2017-07-25
https://docs.spring.io/spring-data/jpa/docs/2.0.0.RC2/reference/html/
JPA参考手册(找了半天,在线版的只找到这个)
https://www.objectdb.com/java/jpa
SpringDataJPA的Specification类,是按照EricEvans的《领域驱动设计》书中Specification的概念和语义来定义查询条件的API。
使用SpringDataJPA,我们一般将自己的dao接口继承CrudRepository接口和JpaSpecificationExecutor接口,由框架生成代理类来完成具体的调用,而不用自己写daoImpl实现类,因为这两个接口自带了很多方法,如果我们写实现类会发现一上来就需要实现十来个方法,比较麻烦。
其中CrudRepository接口主要负责增/删/改的操作,JpaSpecificationExecutor接口主要负责查询的操作,另外,框架还支持在dao接口的方法名上定义一些简单的语义来进行增删改查,底层会对应地做具体实现。
那如何封装具体的查询条件呢?
在service层调用dao接口从JpaSpecificationExecutor继承的抽象查询方法,它就会自动让你准备相关实参,其中Specification对象就是经常用在条件查询的方法的一个形参,也就是说,封装查询条件的过程转移到service层了。
我们一般以匿名内部类的方式new一个Specification对象,实现其中的toPredicate方法,举个例子,
Specificationspecification=newSpecification (){ @Override publicPredicatetoPredicate(Root root,CriteriaQuery>criteriaQuery,CriteriaBuildercriteriaBuilder){ ... };
其中,Predicate,Root,CriteriaQuery,CriteriaBuilder都是javax.persistence包中的接口,方法的这3个实参由框架交给我们。
Predicate意思是"描述语",就是我们封装完查询条件后要交给Specification一个清楚的描述,要怎么组合sql语句去查询。
Root表示为泛型里的Person对象描述一个根位置,可以从这个根位置去取该对象的属性,以及属性的属性,类似对象导航的意思,比如要取Person地址属性的城市,就可以root.get("address").get("city").as(String.class),其返回值是一个Expression对象;
CriteriaQuery代表条件查询,主要提供where、groupby、having、orderby等。
CriteriaBuilder用于构造筛选条件,主要提供equal、and、or、lt、gt、between、like等,以及获得CriteriaQuery、CriteriaUpdate、CriteriaDelete对象。构造每个筛选条件一般需要Expression类型作为实参,可以通过Root对象调用get()方法得到。如果有多个筛选条件,调用criteriaBuilder的and、or等方法连接起来,一般是链式调用的形式。
举个简单的实际例子:
//带条件的分页查询,根据person的first_name和last_name进行模糊查询 //为了直观,假设两个字段都存在且不为空串,省掉非空判断和对应的处理 publicPagefindSearch(Personperson,intpage,intsize){ Specification specification=newSpecification (){ @Override publicPredicatetoPredicate(Root root,CriteriaQuery>criteriaQuery,CriteriaBuildercriteriaBuilder){ Predicatepredicate1=criteriaBuilder.like(root.get("first_name").as(String.class),"%"+person.getFirstName()+"%"); Predicatepredicate2=criteriaBuilder.like(root.get("last_name").as(String.class),"%"+person.getLastName()+"%"); PredicatefinalPredicate=criteriaBuilder.and(predicate1,predicate2); returnfinalPredicate; } }; PageRequestpageRequest=PageRequest.of(page-1,size); returnpersonDao.findAll(specification,pageRequest); }
当然这都是JPQL的语法了,很多开发者也经常在dao接口中直接写SQL语句来让框架查询,使用起来感觉有点类似MyBatis,会显得清爽很多,只需定义一个抽象方法加上对应的注解@Modifying和@Query(value="sql语句",nativeQuery=true)即可,它还有一个好处,不用让封装查询条件这种事情跑到service层去。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。