JPA的多表復(fù)雜查詢(xún)的方法示例
最近工作中由于要求只能用hibernate+jpa 與數(shù)據(jù)庫(kù)進(jìn)行交互,在簡(jiǎn)單查詢(xún)中,jpa繼承CrudRepository接口 ,然后利用jpa的方法命名規(guī)范進(jìn)行jpql查詢(xún),然而在進(jìn)行復(fù)雜查詢(xún)時(shí),需要繼承JpaSpecificationExecutor接口 利用Specification 進(jìn)行復(fù)雜查詢(xún),由于我自己就遇到了這一問(wèn)題,查了好多資料,雖然有方法,但是都沒(méi)有一個(gè)詳細(xì)的講解,以至于知道方法而不能很好的利用jpa復(fù)雜查詢(xún)的方便之處。我將舉幾個(gè)栗子,來(lái)詳細(xì)的說(shuō)一下我自己在使用jpa多表復(fù)雜查詢(xún)的場(chǎng)景和想法。
栗子1:
以一個(gè)實(shí)體類(lèi)User中的幾個(gè)屬性進(jìn)行篩選。
1. 名字
2. ID
3. 手機(jī)號(hào)
這是一個(gè)單表的多條件復(fù)雜查詢(xún),由于是在幾個(gè)屬性中進(jìn)行篩選,其中的屬性的個(gè)數(shù)不知道有多少個(gè),所以只需要利用Specification 查詢(xún)就可以很方便的實(shí)現(xiàn)這個(gè)需求。 下面請(qǐng)看代碼:
場(chǎng)景:頁(yè)面上通過(guò)條件篩選,查詢(xún)用戶(hù)列表
這里有3個(gè)條件 在頁(yè)面上我設(shè)置的id分別為searchName,searchId,searchMobile。 由于這個(gè)是user表 所以u(píng)serRepository 繼承JpaSpecificationExecutor接口,隨后我創(chuàng)建了一個(gè)封裝條件的類(lèi)
public class PageParam<T> {
private Integer pageSize = 10;
private Integer pageNumber = 1;
private String searchName;
private String searchMobile;
private String searchId;
}
由于我這個(gè)方法是直接分頁(yè)的 所以pageNumber 和pageSize 也可以直接寫(xiě)入到這個(gè)類(lèi)中,用于方便接收參數(shù),主要是對(duì)下面3個(gè)參數(shù)的封裝
Specification<T> specification = new Specification<T>() {
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> list = new ArrayList<Predicate>();
if (StringUtils.isNotBlank(searchName)) {
list.add(cb.like(root.get("name").as(String.class), "%" + searchName + "%"));
}
if (StringUtils.isNotBlank(searchId)) {
list.add(cb.equal(root.get("id").as(Long.class), searchId));
}
if (StringUtils.isNotBlank(searchMobile)) {
list.add(cb.like(root.get("mobile").as(String.class), "%" + searchMobile + "%"));
}
Predicate[] p = new Predicate[list.size()];
return cb.and(list.toArray(p));
};
};
這里因?yàn)槎际且粋€(gè)表,所以只要root.get(‘N ‘)這個(gè)N對(duì)應(yīng)所要查的 屬性的名字就好,屬性名 屬性名 重要的事情說(shuō)三遍。
再接下來(lái)看一組多表的查詢(xún)
栗子2:
這里有4張表
public class Living {
Long id;
@ManyToOne
@JsonIgnore
@JoinColumn(name = "actorId", foreignKey = @ForeignKey(name = "none", value =ConstraintMode.NO_CONSTRAINT))
public Actor actor;
@ManyToOne
@JsonIgnore
@JoinColumn(name = "regionId", foreignKey = @ForeignKey(name = "none", value =ConstraintMode.NO_CONSTRAINT))
public Region region;
}
public class Actor {
Long id;
@OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
@JoinColumn(name = "actorId")
@org.hibernate.annotations.ForeignKey(name = "none")
List<Living> livings = new ArrayList<>();
@OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
@org.hibernate.annotations.ForeignKey(name = "none")
@JoinColumn(name = "userDetailId", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
UserDetail userDetail;
@Column(nullable = false)
@Enumerated(value = EnumType.ORDINAL)
ActorType actorType = ActorType.A;
public enum ActorType{
A,B,C
}
}
public class UserDetail {
Long id;
@OneToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
@org.hibernate.annotations.ForeignKey(name = "none")
@JoinColumn(name = "actorId", foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
Actor actor;
String truename;
}
public class Region {
Long id;
String name;
@OneToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.LAZY)
@JoinColumn(name = "regionId")
@org.hibernate.annotations.ForeignKey(name = "none")
List<Living> Livings;
}
現(xiàn)在要根據(jù)userdetai 種的 sex actor中的actortype 還有 region的id 為條件查詢(xún)出滿(mǎn)足條件的living。
public class PageParam<Living> {
private Integer pageSize = 10;
private Integer pageNumber = 1;
private Sex sex;
private ActorType actortype;
private Long cityid;
首先我還是封裝了這樣一個(gè)類(lèi),但是這里的泛型 我是直接給到了想要的查詢(xún)結(jié)果的泛型,接下來(lái) 因?yàn)檫@里涉及到了一個(gè) 多表的查詢(xún) 所以上面的單表查詢(xún)的例子 已經(jīng)不適合這個(gè)查詢(xún)了,但是Criteria 的join方法 給我們提供了一個(gè)模式
Specification<Living> specification = new Specification<Living>() {
@Override
public Predicate toPredicate(Root<Living> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> list = new ArrayList<Predicate>();
if (null!=sex) {
Join<UserDetail, Living> join = root.join("actor", JoinType.LEFT);
list.add(cb.equal(join.get("userDetail").get("sex"), sex ));
}
if (null!=actortype) {
Join<Actor, Living> join = root.join("actor", JoinType.LEFT);
list.add(cb.equal(join.get("actorType"), actortype));
}
if (null!=cityid) {
Join<Region, Living> join = root.join("region", JoinType.LEFT);
list.add(cb.equal(join.get("id"), cityid));
}
//Join<A, B> join = root.join("bs", JoinType.LEFT);
//list.add(cb.equal(join.get("c").get("id"), id));
Predicate[] p = new Predicate[list.size()];
return cb.and(list.toArray(p));
};
};
這里是我對(duì)條件進(jìn)行的封裝。jpa 的多條件查詢(xún) 主要是根據(jù)Criteria 為我們提供的方法封裝條件,然后根據(jù) 給條件定義的位置,再生成sql語(yǔ)句,之后完成查詢(xún)。
不得不說(shuō)的地方,在這個(gè)多表的查詢(xún)中以下面這句為例
Join<UserDetail, Living> join = root.join("actor", JoinType.LEFT);
list.add(cb.equal(join.get("userDetail").get("sex"), sex ));
jointype.LEFT主要是說(shuō)最終的這個(gè)屬性 是在哪個(gè)表中, 而前面的 “actor” 則表示 從living表中 查詢(xún)的 第一步的查詢(xún),比如我給出的例子 是要查詢(xún)出 living 中的 actor 然后是actor 中的userdetail 之后才是 userdetail中的 sex屬性 所以下面的join.get(“userDetail”).get(“sex”) ,這里就是get出相應(yīng)的屬性,一直到你得到想要的屬性為止。 接下來(lái)的兩個(gè)屬性 也同理,許多人多jpa 有很大的誤解,認(rèn)為jpa 的多表,多條件復(fù)雜查詢(xún),不如mybatis的查詢(xún),在之前我也是這么覺(jué)得,但自從通過(guò)jpa 實(shí)現(xiàn)了這個(gè)多表多條件的復(fù)雜查詢(xún)之后,我覺(jué)得hibernate的復(fù)雜查詢(xún) 不遜于mybatis ,尤其是對(duì)sql 語(yǔ)句不是很精通的 碼農(nóng),雖然hibernate的門(mén)檻較高可jpa 恰恰降低了hibernate 所需要的門(mén)檻,希望大家可以通過(guò)我的經(jīng)驗(yàn),更方便的與數(shù)據(jù)庫(kù)進(jìn)行交互。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
如何解決IDEA使用Tomcat控制臺(tái)中文出現(xiàn)亂碼問(wèn)題
這篇文章主要介紹了如何解決IDEA使用Tomcat控制臺(tái)中文出現(xiàn)亂碼問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
Java中Socket實(shí)現(xiàn)數(shù)據(jù)通信的示例代碼
本文主要介紹了Java中Socket實(shí)現(xiàn)數(shù)據(jù)通信的示例代碼,Socket可以建立起客戶(hù)端和服務(wù)器之間的連接,實(shí)現(xiàn)數(shù)據(jù)的傳輸和交互,感興趣的可以了解一下2023-09-09
使用SpringSecurity 進(jìn)行自定義Token校驗(yàn)
這篇文章主要介紹了使用SpringSecurity 進(jìn)行自定義Token校驗(yàn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
java實(shí)現(xiàn)在SSM下使用支付寶掃碼支付功能
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)在SSM下使用支付寶掃碼支付功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02
Java類(lèi)型轉(zhuǎn)換valueOf與parseInt區(qū)別探討解析
這篇文章主要為大家介紹了Java類(lèi)型轉(zhuǎn)換valueOf與parseInt區(qū)別探討解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
java數(shù)據(jù)結(jié)構(gòu)基礎(chǔ):線性表
這篇文章主要介紹了Java的數(shù)據(jù)解構(gòu)基礎(chǔ),希望對(duì)廣大的程序愛(ài)好者有所幫助,同時(shí)祝大家有一個(gè)好成績(jī),需要的朋友可以參考下,希望能給你帶來(lái)幫助2021-07-07
IDEA常量字符串過(guò)長(zhǎng)問(wèn)題及解決方案
在編譯Java項(xiàng)目時(shí)遇到“常量字符串過(guò)長(zhǎng)”錯(cuò)誤,可以通過(guò)修改編譯器設(shè)置解決,具體方法是進(jìn)入IDE的設(shè)置(File>>Settings>>Build, Execution, Deployment>>Compiler>>Java Compiler),將使用的編譯器更改為Eclipse,如果問(wèn)題依舊2024-10-10

