Spring Data JPA动态查询(4)

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 查询原理

是一种类型安全和更面向对象的查询

  • 基本对象的构建

    1. 通过EntityManager的getCriteriaBuilder或EntityManagerFactory的getCriteriaBuilder方法可以得到CriteriaBuilder对象(该对象由hibernate提供)
    2. 通过调用CriteriaBuilder的createQuery或createTupleQuery方法可以获得CriteriaQuery的实例
    3. 通过调用CriteriaQuery的from方法(方法定义在AbstractQuery,但是由CriteriaQueryImpl实现)可以获得Root实例
  • 过滤条件

    1. 过滤条件会被应用到SQL语句的FROM子句中。在criteria 查询中,查询条件通过Predicate或Expression实例应用到CriteriaQuery对象上。
    2. 这些条件使用 CriteriaQuery .where 方法应用到CriteriaQuery 对象上
    3. CriteriaBuilder也作为Predicate实例的工厂,通过调用CriteriaBuilder 的条件方法( equal,notEqual, gt, ge,lt, le,between,like等)创建Predicate对象。
    4. 复合的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));
              }
          }
    
  • 注:多个条件排序

  1. 构造多个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);
    
  2. 直接构造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对应关系
equlefiled = value
gt(greaterThan )filed > value
lt(lessThan )filed < value
ge(greaterThanOrEqualTo )filed >= value
le( lessThanOrEqualTo)filed <= value
notEqulefiled != value
likefiled like value
notLikefiled not like value
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值