轉 JPA的多表復雜查詢:詳細篇
原文鏈接: https://mp.weixin.qq.com/s/7J6ANppuiZJccIVN-h0T3Q
最近工作中由於要求只能用hibernate+jpa 與數據庫進行交互,在簡單查詢中,jpa繼承CrudRepository
栗子1:
以一個實體類User中的幾個屬性進行篩選。
名字
ID
手機號
這是一個單表的多條件復雜查詢,由於是在幾個屬性中進行篩選,其中的屬性的個數不知道有多少個,所以只需要利用Specification 查詢就可以很方便的實現這個需求。 下面請看代碼: 場景:頁面上通過條件篩選,查詢用戶列表
這里有3個條件 在頁面上我設置的id分別為searchName,searchId,searchMobile。 由於這個是user表 所以userRepository 繼承JpaSpecificationExecutor接口,隨后我創建了一個封裝條件的類
public class PageParam<T> {
private Integer pageSize = 10;
private Integer pageNumber = 1;
private String searchName;
private String searchMobile;
private String searchId;
}
由於我這個方法是直接分頁的 所以pageNumber 和pageSize 也可以直接寫入到這個類中,用於方便接收參數,主要是對下面3個參數的封裝
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));
};
};
這里因為都是一個表,所以只要root.get('N ')這個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;
}
現在要根據userdetai 種的 sex actor中的actortype 還有 region的id 為條件查詢出滿足條件的living。
public class PageParam<Living> {
private Integer pageSize = 10;
private Integer pageNumber = 1;
private Sex sex;
private ActorType actortype;
private Long cityid;
}
首先我還是封裝了這樣一個類,但是這里的泛型 我是直接給到了想要的查詢結果的泛型,接下來 因為這里涉及到了一個 多表的查詢 所以上面的單表查詢的例子 已經不適合這個查詢了,但是Criteria 的join方法 給我們提供了一個模式
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));
};
};
這里是我對條件進行的封裝。jpa 的多條件查詢 主要是根據Criteria 為我們提供的方法封裝條件,然后根據 給條件定義的位置,再生成sql語句,之后完成查詢。 不得不說的地方,在這個多表的查詢中以下面這句為例
Join<UserDetail, Living> join = root.join("actor", JoinType.LEFT);
list.add(cb.equal(join.get("userDetail").get("sex"), sex ));
jointype.LEFT主要是說最終的這個屬性 是在哪個表中, 而前面的 “actor” 則表示 從living表中 查詢的 第一步的查詢,比如我給出的例子 是要查詢出 living 中的 actor 然后是actor 中的userdetail 之后才是 userdetail中的 sex屬性 所以下面的join.get("userDetail").get("sex") ,這里就是get出相應的屬性,一直到你得到想要的屬性為止。 接下來的兩個屬性 也同理, 許多人多jpa 有很大的誤解,認為jpa 的多表,多條件復雜查詢,不如mybatis的查詢,在之前我也是這么覺得,但自從通過jpa 實現了這個多表多條件的復雜查詢之后,我覺得hibernate的復雜查詢 不遜於mybatis ,尤其是對sql 語句不是很精通的碼農,雖然hibernate的門檻較高可jpa 恰恰降低了hibernate 所需要的門檻,希望大家可以通過我的經驗,更方便的與數據庫進行交互。
