最近工做中因爲要求只能用hibernate+jpa 與數據庫進行交互,在簡單查詢中,jpa繼承CrudRepository接口 ,而後利用jpa的方法命名規範進行jpql查詢,然而在進行復雜查詢時,須要繼承JpaSpecificationExecutor接口 利用Specification 進行復雜查詢,因爲我本身就遇到了這一問題,查了好多資料,雖然有方法,可是都沒有一個詳細的講解,以致於知道方法而不能很好的利用jpa複雜查詢的方便之處。我將舉幾個栗子,來詳細的說一下我本身在使用jpa多表複雜查詢的場景和想法。
栗子1: java
以一個實體類User中的幾個屬性進行篩選。sql
1. 名字 2. ID 3. 手機號
這是一個單表的多條件複雜查詢,因爲是在幾個屬性中進行篩選,其中的屬性的個數不知道有多少個,因此只須要利用Specification 查詢就能夠很方便的實現這個需求。 下面請看代碼:
場景:頁面上經過條件篩選,查詢用戶列表 數據庫
這裏有3個條件 在頁面上我設置的id分別爲searchName,searchId,searchMobile。 因爲這個是user表 因此userRepository 繼承JpaSpecificationExecutor接口,隨後我建立了一個封裝條件的類mybatis
public class PageParam<T> { private Integer pageSize = 10; private Integer pageNumber = 1; private String searchName; private String searchMobile; private String searchId; }
因爲我這個方法是直接分頁的 因此pageNumber 和pageSize 也能夠直接寫入到這個類中,用於方便接收參數,主要是對下面3個參數的封裝ide
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對應所要查的 屬性的名字就好,屬性名 屬性名 重要的事情說三遍。
再接下來看一組多表的查詢 fetch
栗子2: ui
這裏有4張表hibernate
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。code
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 所須要的門檻,但願你們能夠經過個人經驗,更方便的與數據庫進行交互。