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 findOne(Specification spec);	
         	//根据条件查询集合
       	List findAll(Specification spec);
         	//根据条件分页查询
       	Page findAll(Specification spec, Pageable pageable);
         	//排序查询查询
       	List findAll(Specification spec, Sort sort);
         	//统计查询
       	long count(Specification spec);
      }
    

对于JpaSpecificationExecutor,这个接口基本是围绕着Specification接口来定义的。我们可以简单的理解为,Specification构造的就是查询条件。

  • AbstractQuery接口
    可以获取root,构建查询条件

      public interface AbstractQuery extends CommonAbstractCriteria {
      	//最关键的是from方法,可以得到语句的条件部分根对象
           Root from(Class entityClass);
           Root from(EntityType entity);
          AbstractQuery where(Expression restriction);
          AbstractQuery where(Predicate... restrictions);
          AbstractQuery groupBy(Expression... grouping);
          AbstractQuery groupBy(List> grouping);
          AbstractQuery having(Expression restriction);
          AbstractQuery having(Predicate... restrictions);
          AbstractQuery distinct(boolean distinct);
          Set> getRoots();
          Selection getSelection();
          List> getGroupList();
          Predicate getGroupRestriction();
          boolean isDistinct();
          Class getResultType();
      }
    
  • CriteriaQuery接口
    继承了AbstractQuery接口,用于构建查询条件

      public interface CriteriaQuery extends AbstractQuery {
          CriteriaQuery select(Selection selection);
          CriteriaQuery multiselect(Selection... selections);
          CriteriaQuery multiselect(List> selectionList);
          CriteriaQuery where(Expression restriction);//应用查询条件实例的方法
          CriteriaQuery where(Predicate... restrictions);//应用查询条件实例的方法
          CriteriaQuery groupBy(Expression... grouping);
          CriteriaQuery groupBy(List> grouping);
          CriteriaQuery having(Expression restriction);
          CriteriaQuery having(Predicate... restrictions);
          CriteriaQuery orderBy(Order... o);
          CriteriaQuery orderBy(List o);
          CriteriaQuery distinct(boolean distinct);
          List getOrderList();
          Set> 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 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  spec = new Specification() {
              public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
              //1.获取比较的属性
              Path 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  spec = new Specification() {
              public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                  //1.获取比较的属性
                  Path custName = root.get("custName");
                  Path 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  spec = new Specification() {
                public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                    //1.查询属性
                    Path 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  spec = new Specification() {
                public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                       //1.查询属性
                       Path 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  spec = new Specification() {
                  public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                      //1.查询属性
                      Path 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 list = new ArrayList();
                     CriteriaBuilder.In in = cb.in(root.get("custId"));
                     for (Long id :ids) {
                         in.value(id);
                     }
                     return cb.and(in);
                 }
             };
             List 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

        你可能感兴趣的:(springboot)