[英]How to use multiple JOIN FETCH in one JPQL query
我有以下實體:
public class Category {
private Integer id;
@OneToMany(mappedBy = "parent")
private List<Topic> topics;
}
public class Topic {
private Integer id;
@OneToMany(mappedBy = "parent")
private List<Posts> posts;
@ManyToOne
@JoinColumn(name = "id")
private Category parent;
}
public class Post {
private Integer id;
@ManyToOne
@JoinColumn(name = "id")
private Topic parent;
/* Post fields */
}
我想使用 JPQL 查詢獲取所有帶有連接topics
和連接posts
類別。 我寫了如下查詢:
SELECT c FROM Category c
JOIN FETCH c.topics t
JOIN FETCH t.posts p WHERE
但我得到了錯誤
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
我找到了有關此錯誤的文章,但這些文章僅描述了在一個實體中有兩個要加入的集合的情況。 我的問題有點不同,我不知道如何解決。
可以在一個查詢中完成嗎?
考慮到我們有以下實體:
並且,您想要獲取一些父Post
實體以及所有關聯的comments
和tags
集合。
如果您使用多個JOIN FETCH
指令:
List<Post> posts = entityManager.createQuery("""
select p
from Post p
left join fetch p.comments
left join fetch p.tags
where p.id between :minId and :maxId
""", Post.class)
.setParameter("minId", 1L)
.setParameter("maxId", 50L)
.getResultList();
Hibernate 將拋出MultipleBagFetchException
:
org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags [
com.vladmihalcea.book.hpjp.hibernate.fetching.Post.comments,
com.vladmihalcea.book.hpjp.hibernate.fetching.Post.tags
]
Hibernate 拋出此異常的原因是它不允許獲取多個包,因為這會生成笛卡爾積。
現在,您會發現很多答案、博客文章、視頻或其他資源都告訴您對Set
使用Set
而不是List
。
這是可怕的建議。 不要那樣做!
使用Sets
而不是Lists
將使MultipleBagFetchException
消失,但笛卡爾積仍然存在,實際上更糟,因為您會在應用此“修復”很久之后發現性能問題。
您可以執行以下技巧:
List<Post> posts = entityManager.createQuery("""
select distinct p
from Post p
left join fetch p.comments
where p.id between :minId and :maxId
""", Post.class)
.setParameter("minId", 1L)
.setParameter("maxId", 50L)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
posts = entityManager.createQuery("""
select distinct p
from Post p
left join fetch p.tags t
where p in :posts
""", Post.class)
.setParameter("posts", posts)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
在第一個 JPQL 查詢中,
distinct
不會轉到 SQL 語句。 這就是我們將PASS_DISTINCT_THROUGH
JPA 查詢提示設置為false
。DISTINCT 在 JPQL 中有兩個含義,在這里,我們需要它在 Java 端而不是 SQL 端對
getResultList
返回的 Java 對象引用進行去重。
只要您使用JOIN FETCH
最多獲取一個集合,就可以了。
通過使用多個查詢,您將避免使用笛卡爾積,因為任何其他集合都是使用輔助查詢獲取的,但第一個集合除外。
FetchType.EAGER
策略如果您在映射時為@OneToMany
或@ManyToMany
關聯使用FetchType.EAGER
策略,那么您很容易以MultipleBagFetchException
結束。
您最好從FetchType.EAGER
切換到Fetchype.LAZY
因為急切獲取是一個糟糕的想法,可能會導致關鍵的應用程序性能問題。
避免使用FetchType.EAGER
並且不要僅僅因為這樣做會使 Hibernate 將MultipleBagFetchException
隱藏在地毯下而從List
切換到Set
。 一次只取一個集合,你會沒事的。
只要您使用與要初始化的集合相同數量的查詢來執行此操作,就可以了。 只是不要在循環中初始化集合,因為這會觸發N+1
查詢問題,這也對性能不利。
這是復雜連接和多重條件的工作示例:
String query_findByProductDepartmentHospital = "select location from ProductInstallLocation location "
+ " join location.product prod " + " join location.department dep "
+ " join location.department.hospital hos " + " where prod.name = :product "
+ " and dep.name.name = :department " + " and hos.name = :hospital ";
@Query(query_findByProductDepartmentHospital)
ProductInstallLocation findByProductDepartmentHospital(@Param("product") String productName,@Param("department") String departName, @Param("hospital") String hospitalName);
一種解決方法是將@Query 和@EntityGraph 一起使用,就像這里提到的那樣將@Query 和@EntityGraph 一起使用
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.