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查詢有一個足夠的熟悉和理解,因為這個方法的參數(shù)和返回值都是JPA標準里面定義的對象。
Criteria查詢基本概念
Criteria 查詢是以元模型的概念為基礎的,元模型是為具體持久化單元的受管實體定義的,這些實體可以是實體類,嵌入類或者映射的父類。
- CriteriaQuery接口:代表一個specific的頂層查詢對象,它包含著查詢的各個部分,比如:select 、from、where、group by、order by等 注意:CriteriaQuery對象只對實體類型或嵌入式類型的Criteria查詢起作用
- Root接口:代表Criteria查詢的根對象,Criteria查詢的查詢根定義了實體類型,能為將來導航獲得想要的結果,它與SQL查詢中的FROM子句類似
1:Root實例是類型化的,且定義了查詢的FROM子句中能夠出現(xiàn)的類型。
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等)創(chuàng)建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));
下面我們用兩個示例代碼來更深入的了解
復雜條件多表查詢
//需要查詢的對象 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; //關聯(lián)表 private String fzcc; private String fzccName; @ManyToOne @JoinColumn(name = "criminalInfo") private CriminalInfo criminalInfo;//關聯(lián)表 @Column(length=800) private String bz; //get/set...... } //創(chuàng)建構造Specification的方法 //這里我傳入兩個條件參數(shù)因為與前段框架有關,你們寫的時候具體自己業(yè)務自行決斷 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方法有很多重載,使用的時候可以多根據(jù)自己業(yè)務決斷 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 參數(shù),用于設定查詢條件。
Service 分頁+多條件查詢 調用示例:
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"); /** * 連接查詢條件, 不定參數(shù),可以連接0..N個查詢條件 */ query.where(cb.like(namePath, "%李%"), cb.like(nicknamePath, "%王%")); //這里可以設置任意條查詢條件 return null; } }, page); }
這里通過CriteriaBuilder 的like方法創(chuàng)建了兩個查詢條件, 姓名(name)字段必須包含“李”, 昵稱(nickname)字段必須包含“王”。
然后通過連接多個查詢條件即可。 這種方式使用JPA的API設置了查詢條件,所以不需要再返回查詢條件Predicate給Spring Data Jpa,故最后return null;即可。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
微服務鏈路追蹤Spring Cloud Sleuth整合Zipkin解析
這篇文章主要為大家介紹了微服務鏈路追蹤Spring Cloud Sleuth整合Zipkin解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02Java(springboot) 讀取txt文本內容代碼實例
這篇文章主要介紹了Java(springboot) 讀取txt文本內容代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-02-02