Spring Data JPA第1章动态查询(4)
Specifications动态查询介绍
在查询某个实体的时,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象。
-
JpaSpecificationExecutor接口
是复杂查询的主要入口,传入Specification对象
这个接口里的方法全部是围绕着上面的接口Specification写的
想使用Specification对象构造复杂查询语句,Dao层需要先继承该接口,然后实现Specification的toPredicate方法。import java.util.List; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; /** * JpaSpecificationExecutor中定义的方法 **/ public interface JpaSpecificationExecutor<T> { //根据条件查询一个对象 T findOne(Specification<T> spec); //根据条件查询集合 List<T> findAll(Specification<T> spec); //根据条件分页查询 Page<T> findAll(Specification<T> spec, Pageable pageable); //排序查询查询 List<T> findAll(Specification<T> spec, Sort sort); //统计查询 long count(Specification<T> spec); }
对于JpaSpecificationExecutor,这个接口基本是围绕着Specification接口来定义的。我们可以简单的理解为,Specification构造的就是查询条件。
-
AbstractQuery接口
可以获取root,构建查询条件public interface AbstractQuery<T> extends CommonAbstractCriteria { //最关键的是from方法,可以得到语句的条件部分根对象 <X> Root<X> from(Class<X> entityClass); <X> Root<X> from(EntityType<X> entity); AbstractQuery<T> where(Expression<Boolean> restriction); AbstractQuery<T> where(Predicate... restrictions); AbstractQuery<T> groupBy(Expression<?>... grouping); AbstractQuery<T> groupBy(List<Expression<?>> grouping); AbstractQuery<T> having(Expression<Boolean> restriction); AbstractQuery<T> having(Predicate... restrictions); AbstractQuery<T> distinct(boolean distinct); Set<Root<?>> getRoots(); Selection<T> getSelection(); List<Expression<?>> getGroupList(); Predicate getGroupRestriction(); boolean isDistinct(); Class<T> getResultType(); }
-
CriteriaQuery接口
继承了AbstractQuery接口,用于构建查询条件public interface CriteriaQuery<T> extends AbstractQuery<T> { CriteriaQuery<T> select(Selection<? extends T> selection); CriteriaQuery<T> multiselect(Selection<?>... selections); CriteriaQuery<T> multiselect(List<Selection<?>> selectionList); CriteriaQuery<T> where(Expression<Boolean> restriction);//应用查询条件实例的方法 CriteriaQuery<T> where(Predicate... restrictions);//应用查询条件实例的方法 CriteriaQuery<T> groupBy(Expression<?>... grouping); CriteriaQuery<T> groupBy(List<Expression<?>> grouping); CriteriaQuery<T> having(Expression<Boolean> restriction); CriteriaQuery<T> having(Predicate... restrictions); CriteriaQuery<T> orderBy(Order... o); CriteriaQuery<T> orderBy(List<Order> o); CriteriaQuery<T> distinct(boolean distinct); List<Order> getOrderList(); Set<ParameterExpression<?>> getParameters(); }
-
CriteriaBuilder接口
能创建CriteriaQuery对象,还能创建查询条件(Expression表达式),定义了几乎所有的sql逻辑判断相关的表达式创建方法,功能非常全,因此一般都是用此接口中的方法。实现类是由hibernate提供
//这里列一下Expression的继承树,仅列出常见的 * Expression (定义了:isNull、isNotNull、in、as。。。) * Predicate(定义了:not,还有枚举对象{AND,OR}。。。) * Subquery(子查询接口,里面有select、where、groupBy、having。。。) * Path(JPA定义的,定义了一些获取方法,有和实体相关的,有和表达式相关的) * From(JPA定义的,定义了许多join相关的方法) * Join(连接查询,定义了join的条件on。。。) * Root(就一个获取实体的方法)
-
Specification接口
处理复杂查询,如多条件分页查询Specification接口使用和意义,里面定义了基本的连接语句动态添加的方法,Specifications是该接口的实现类。注意方法toPredicate(断言方法),此方法负责处理criteria语句的条件,需要实现此方法
//构造查询条件 /** * root:Root接口,代表查询的根对象,可以通过root获取实体中的属性 * Root:查询哪个表 CriteriaQuery:查询哪些字段,排序是什么 CriteriaBuilder:字段之间是什么关系,如何生成一个查询条件,每一个查询条件都是什么方式 Predicate(Expression):单独每一条查询条件的详细描述 query:代表一个顶层查询对象,用来自定义查询 **/ public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
Criteria 查询原理
是一种类型安全和更面向对象的查询
-
基本对象的构建
- 通过EntityManager的getCriteriaBuilder或EntityManagerFactory的getCriteriaBuilder方法可以得到CriteriaBuilder对象(该对象由hibernate提供)
- 通过调用CriteriaBuilder的createQuery或createTupleQuery方法可以获得CriteriaQuery的实例
- 通过调用CriteriaQuery的from方法(方法定义在AbstractQuery,但是由CriteriaQueryImpl实现)可以获得Root实例
-
过滤条件
- 过滤条件会被应用到SQL语句的FROM子句中。在criteria 查询中,查询条件通过Predicate或Expression实例应用到CriteriaQuery对象上。
- 这些条件使用 CriteriaQuery .where 方法应用到CriteriaQuery 对象上
- CriteriaBuilder也作为Predicate实例的工厂,通过调用CriteriaBuilder 的条件方法( equal,notEqual, gt, ge,lt, le,between,like等)创建Predicate对象。
- 复合的Predicate 语句可以使用CriteriaBuilder的and, or andnot 方法构建
Specifications动态实现
-
条件查询
//通过id查询客户信息 @Test public void getCustomerByName(){ Specification<Customer> spec = new Specification<Customer>() { public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { //1.获取比较的属性 Path<Object> custId = root.get("custId"); //2.构造查询条件 select * from cst_customer where cst_name = ? /*** * 第一个参数:需要比较的属性 * 第二个参数:当前需要比较的取值 */ Predicate predicate = criteriaBuilder.equal(custId,8l); return predicate; } }; Customer customer = customerDao.findOne(spec); System.out.println("查询到的客户信息:"+customer); }
-
多条件查询
@Test public void getCustomerByNameAndIndustry(){ Specification<Customer> spec = new Specification<Customer>() { public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { //1.获取比较的属性 Path<Object> custName = root.get("custName"); Path<Object> custIndustry = root.get("custIndustry"); //2.构造查询条件 select * from cst_customer where cst_name = ? /*** * 第一个参数:需要比较的属性 * 第二个参数:当前需要比较的取值 */ Predicate predicate1 = criteriaBuilder.equal(custName,"传智播客"); Predicate predicate2 = criteriaBuilder.equal(custIndustry,"教育"); Predicate p = criteriaBuilder.and(predicate1,predicate2); return p; } }; Customer customer = customerDao.findOne(spec); System.out.println("查询到的客户信息:"+customer);
}
-
-
模糊查询
@Test public void getCustomerByNameLike(){ Specification<Customer> spec = new Specification<Customer>() { public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { //1.查询属性 Path<Object> custName = root.get("custName"); //2.查询方式 模糊查询 Predicate predicate = criteriaBuilder.like(custName.as(String.class),"传智%"); return predicate; } }; List list = customerDao.findAll(spec); for (int i = 0; i < list.size(); i++) { System.out.println("查询到的客户信息:"+list.get(i)); } }
-
模糊查询并排序
@Test public void getCustomerBySort(){ Specification<Customer> spec = new Specification<Customer>() { public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { //1.查询属性 Path<Object> custName = root.get("custName"); //2.查询方式 模糊查询 Predicate predicate = criteriaBuilder.like(custName.as(String.class),"传智%"); return predicate; } }; //排序的属性名称 Sort sort = new Sort(Sort.Direction.DESC,"custId"); List list = customerDao.findAll(spec,sort); for (int i = 0; i < list.size(); i++) { System.out.println("查询到的客户信息:"+list.get(i)); } }
-
注:多个条件排序
-
构造多个sort对象,并合并
//第一个参数,排序类型:ASC/DESC,第二个参数:按照排序的字段,可以设置多个 Sort sort = new Sort(Sort.Direction.DESC,"adminId"); Sort sort1 = new Sort(Sort.Direction.ASC,"adminUsername"); final Sort mergeSort = sort1.and(sort);
-
直接构造Sort对象排序
Sort sortMulti = new Sort(Sort.Direction.DESC,"adminId","adminUsername");//可多字段
-
模糊查询排序,并分页
@Test public void getCustomerByPagin(){ Specification<Customer> spec = new Specification<Customer>() { public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { //1.查询属性 Path<Object> custName = root.get("custName"); //2.查询方式 模糊查询 Predicate predicate = criteriaBuilder.like(custName.as(String.class),"传智%"); return predicate; } }; //分页的属性名称 /** * 需要两个参数 * 第一个参数:当前页(从0 开始) * 第二个参数:每页显示的总数 */ Pageable pageable = new PageRequest(0,2); Page page = customerDao.findAll(predicate,pageable); System.out.println("总记录数:"+page.getTotalElements()); System.out.println("当前页数据集合:"+page.getContent()); System.out.println("总页数:"+page.getTotalPages()); List list = page.getContent(); for (int i = 0; i < list.size(); i++) { System.out.println("查询到的客户信息:"+list.get(i)); } }
-
in查询
@Test public void testIn(){ final Long[] ids={1l,2l}; Specification spec = new Specification() { public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder cb) { List<Predicate> list = new ArrayList<Predicate>(); CriteriaBuilder.In<Long> in = cb.in(root.get("custId")); for (Long id :ids) { in.value(id); } return cb.and(in); } }; List<Customer> list = customerDao.findAll(spec); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i).toString()); } }
-
方法对应关系
方法名称 | Sql对应关系 |
equle | filed = value |
gt(greaterThan ) | filed > value |
lt(lessThan ) | filed < value |
ge(greaterThanOrEqualTo ) | filed >= value |
le( lessThanOrEqualTo) | filed <= value |
notEqule | filed != value |
like | filed like value |
notLike | filed not like value |