JPA的多表複雜查詢

最近工做中因爲要求只能用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 所須要的門檻,但願你們能夠經過個人經驗,更方便的與數據庫進行交互。

相關文章
相關標籤/搜索