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;即可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM