[英]Can't access lazy annotated but initialized hibernate collection from JSF2
[英]Can't fetch join lazy initialized collections
由於 MultipleBagFetchException,我無法執行簡單的 fetchjoin。
@Entity
public class Person {
@OneToMany(mappedBy="person",fetch=FetchType.LAZY)
private List<Auto> autos;
}
@Entity
public class Auto {
@ManyToOne
@JoinColumn(name = "person_id", nullable = false)
private Person person;
@OneToMany(mappedBy="auto",fetch=FetchType.LAZY)
private List<Tool> tools;
}
@Entity
@Table(name="tool")
public class Tool {
@ManyToOne
@JoinColumn(name = "auto_id", nullable = false)
private Auto auto;
}
如您所見,我所有的關聯都使用默認的 fetchtype。
@Query("SELECT p FROM Person p JOIN FETCH p.autos a JOIN FETCH a.tools")
List<Person>findAll();
結果:
Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags: [com.example.entities.Person.autos, com.example.entities.Auto.tools]
我已閱讀有關此異常的信息,但在這些情況下,此異常的原因是 collections 使用了 EAGER 提取類型。 那這個呢? 這是最簡單的實體關系。
最重要的是,假設我們不允許觸摸實體。 如何僅在查詢端解決這個問題?
有一種方法可以在不觸及實體的情況下避免 n+1 次查詢,只需更改 findAll 的查詢即可。 我們可以編寫一個包裝器 function,它將首先加載帶有汽車的人員,然后他們在單個 select 中獲取所有工具。
個人資料庫
@Query("SELECT distinct p FROM Person p JOIN FETCH p.autos a")
List<Person> findAll();
包裝代碼
List<Person> persons = personRepository.findAll();
Session session = (Session) entityManager.getDelegate();
List<Auto> autos = new ArrayList<>();
for (Person person : persons) {
if(!CollectionUtils.isEmpty(person.getAutos())) {
autos.addAll(person.getAutos());
}
}
try{
autos = session.createQuery("select distinct a from Auto a Join fetch a.tools " +
" where a in :autos", Auto.class)
.setParameter("autos", autos)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
} catch (Exception ex) {
ex.printStackTrace();
}
第一個查詢將是:
SELECT DISTINCT
person0_.id AS id1_6_0_,
autos1_.id AS id1_0_1_,
person0_.name AS name2_6_0_,
autos1_.name AS name2_0_1_,
autos1_.person_id AS person_i3_0_1_,
autos1_.person_id AS person_i3_0_0__,
autos1_.id AS id1_0_0__
FROM
Person person0_
INNER JOIN
Auto autos1_
ON
person0_.id=autos1_.person_id
生成的第二個查詢將是:
SELECT
auto0_.id AS id1_0_0_,
tools1_.id AS id1_8_1_,
auto0_.name AS name2_0_0_,
auto0_.person_id AS person_i3_0_0_,
tools1_.auto_id AS auto_id3_8_1_,
tools1_.name AS name2_8_1_,
tools1_.auto_id AS auto_id3_8_0__,
tools1_.id AS id1_8_0__
FROM
Auto auto0_
INNER JOIN
Tool tools1_
ON
auto0_.id=tools1_.auto_id
WHERE
auto0_.id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
除此之外,我相信我們的選項是有限的,我們將不得不更改工具實體 FetchMode 或為默認 FetchMode.SELECT 添加 BatchSize 以便在單獨的查詢中獲取工具。
@OneToMany(mappedBy = "auto", fetch = FetchType.LAZY)
@Fetch(FetchMode.SUBSELECT)
private List<Tool> tools;
查詢將是
SELECT
tools0_.auto_id AS auto_id3_8_1_
, tools0_.id AS id1_8_1_
, tools0_.id AS id1_8_0_
, tools0_.auto_id AS auto_id3_8_0_
, tools0_.name AS name2_8_0_
FROM
Tool tools0_
WHERE
tools0_.auto_id IN
(
SELECT
autos1_.id
FROM
Person person0_
INNER JOIN
Auto autos1_
ON
person0_.id=autos1_.person_id
)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.