jpa多条件查询重写Specification的toPredicate方法(转)

Spring Data JPA支持JPA2.0的Criteria查询,相应的接口是JpaSpecificationExecutor。Criteria 查询:是一种类型安全和更面向对象的查询 。

这个接口基本是围绕着Specification接口来定义的, Specification接口中只定义了如下一个方法:

Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);

要理解这个方法,以及正确的使用它,就需要对JPA2.0的Criteria查询有一个足够的熟悉和理解,因为这个方法的参数和返回值都是JPA标准里面定义的对象。

Criteria查询基本概念

Criteria 查询是以元模型的概念为基础的,元模型是为具体持久化单元的受管实体定义的,这些实体可以是实体类,嵌入类或者映射的父类。

CriteriaQuery接口:代表一个specific的顶层查询对象,它包含着查询的各个部分,比如:select 、from、where、group by、order by等注意:CriteriaQuery对象只对实体类型或嵌入式类型的Criteria查询起作用 
Root接口:代表Criteria查询的根对象,Criteria查询的查询根定义了实体类型,能为将来导航获得想要的结果,它与SQL查询中的FROM子句类似

1:Root实例是类型化的,且定义了查询的FROM子句中能够出现的类型。

2:查询根实例能通过传入一个实体类型给 AbstractQuery.from方法获得。

3:Criteria查询,可以有多个查询根。

4:AbstractQuery是CriteriaQuery 接口的父类,它提供得到查询根的方法。CriteriaBuilder接口:用来构建CritiaQuery的构建器对象Predicate:一个简单或复杂的谓词类型,其实就相当于条件或者是条件组合。

Criteria查询基本对象的构建

1:通过EntityManager的getCriteriaBuilder或EntityManagerFactory的getCriteriaBuilder方法可以得到CriteriaBuilder对象2:通过调用CriteriaBuilder的createQuery或createTupleQuery方法可以获得CriteriaQuery的实例

3:通过调用CriteriaQuery的from方法可以获得Root实例过滤条件

A:过滤条件会被应用到SQL语句的FROM子句中。在criteria 查询中,查询条件通过Predicate或Expression实例应用到CriteriaQuery对象上。

B:这些条件使用 CriteriaQuery .where 方法应用到CriteriaQuery 对象上

C:CriteriaBuilder也作为Predicate实例的工厂,通过调用CriteriaBuilder 的条件
方( equalnotEqual, gt, ge,lt, le,between,like等)创建Predicate对象。

D:复合的Predicate 语句可以使用CriteriaBuilder的and, or andnot 方法构建。 

    构建简单的Predicate示例:
复制代码
            Predicate p1=cb.like(root.get(“name”).as(String.class), “%”+uqm.getName()+“%”);

            Predicate p2=cb.equal(root.get("uuid").as(Integer.class), uqm.getUuid());

            Predicate p3=cb.gt(root.get("age").as(Integer.class), uqm.getAge());

        构建组合的Predicate示例:

           Predicate p = cb.and(p3,cb.or(p1,p2));
复制代码

下面我们用两个示例代码来更深入的了解:

1.复杂条件多表查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//需要查询的对象
public  class  Qfjbxxdz {
     @Id
     @GeneratedValue(generator =  "system-uuid" )
     @GenericGenerator(name =  "system-uuid" , strategy =  "uuid.hex" )
     private  String id;
     @OneToOne
     @JoinColumn(name =  "qfjbxx" )
     private  Qfjbxx qfjbxx;  //关联表
     private  String fzcc;
     private  String fzccName;
     @ManyToOne
     @JoinColumn(name =  "criminalInfo" )
     private  CriminalInfo criminalInfo; //关联表
     @Column(length=800)
     private  String bz;
     //get/set......
}
 
//创建构造Specification的方法
//这里我传入两个条件参数因为与前段框架有关,你们写的时候具体自己业务自行决断
private  Specification<Qfjbxxdz> getWhereClause(final JSONArray condetion,final JSONArray search) {
         return  new  Specification<Qfjbxxdz>() {
             @Override
             public  Predicate toPredicate(Root<Qfjbxxdz> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                 List<Predicate> predicate =  new  ArrayList<>();
                 Iterator<JSONObject> iterator = condetion.iterator();
                 Predicate preP =  null ;
                 while (iterator.hasNext()){
                     JSONObject jsonObject = iterator.next();
                     //注意:这里用的root.join 因为我们要用qfjbxx对象里的字段作为条件就必须这样做join方法有很多重载,使用的时候可以多根据自己业务决断
                     Predicate p1 = cb.equal(root. join ( "qfjbxx" ). get ( "id" ). as (String. class ),jsonObject. get ( "fzId" ).toString());
                     Predicate p2 = cb.equal(root. get ( "fzcc" ). as (String. class ),jsonObject. get ( "ccId" ).toString());
                     if (preP!= null ){
                         preP = cb.or(preP,cb.and(p1,p2));
                     } else {
                         preP = cb.and(p1,p2);
                     }
                 }
                 JSONObject jsonSearch=(JSONObject) search. get (0);
                 Predicate p3= null ;
                 if ( null !=jsonSearch. get ( "xm" )&&jsonSearch. get ( "xm" ).toString().length()>0){
                    p3=cb.like(root. join ( "criminalInfo" ). get ( "xm" ). as (String. class ), "%" +jsonSearch. get ( "xm" ).toString()+ "%" );
                 }
                 Predicate p4= null ;
                 if ( null !=jsonSearch. get ( "fzmc" )&&jsonSearch. get ( "fzmc" ).toString().length()>0){
                     p4=cb.like(root. join ( "qfjbxx" ). get ( "fzmc" ). as (String. class ), "%" +jsonSearch. get ( "fzmc" ).toString()+ "%" );
                 }
                 Predicate preA;
                 if ( null !=p3&& null !=p4){
                     Predicate  preS =cb.and(p3,p4);
                     preA =cb.and(preP,preS);
                 } else  if ( null ==p3&& null !=p4){
                     preA=cb.and(preP,p4);
                 } else  if ( null !=p3&& null ==p4){
                     preA=cb.and(preP,p3);
                 } else {
                     preA=preP;
                 }
                 predicate.add(preA);
                 Predicate[] pre =  new  Predicate[predicate.size()];
                 query. where (predicate.toArray(pre));
                 return  query.getRestriction();
             }

编写DAO类或接口 
dao类/接口 需继承

public interface JpaSpecificationExecutor<T>

接口; 
如果需要分页,还可继承

public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID>

JpaSpecificationExecutor 接口具有方法

Page<T> findAll(Specification<T> spec, Pageable pageable); //分页按条件查询

List<T> findAll(Specification<T> spec); //不分页按条件查询

方法。 我们可以在Service层调用这两个方法。 
两个方法都具有 Specification spec 参数,用于设定查询条件。 
Service 分页+多条件查询 调用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
studentInfoDao.findAll( new  Specification<StudentInfo> () { 
 
    public  Predicate toPredicate(Root<StudentInfo> root, 
      CriteriaQuery<?> query, CriteriaBuilder cb) { 
     Path<String> namePath = root. get ( "name" ); 
     Path<String> nicknamePath = root. get ( "nickname" ); 
     /**
          * 连接查询条件, 不定参数,可以连接0..N个查询条件
          */ 
     query. where (cb.like(namePath,  "%李%" ), cb.like(nicknamePath,  "%王%" ));  //这里可以设置任意条查询条件 
 
     return  null
   
 
   }, page); 
 
 

  这里通过CriteriaBuilder 的like方法创建了两个查询条件, 姓名(name)字段必须包含“李”, 昵称(nickname)字段必须包含“王”。 
然后通过 
连接多个查询条件即可。 这种方式使用JPA的API设置了查询条件,所以不需要再返回查询条件Predicate给Spring Data Jpa,故最后return null;即可。

猜你喜欢

转载自www.cnblogs.com/morganlin/p/12000165.html