[英]Efficient Hibernate criteria for join returning many partial duplicates
我正在獲取一長串實體,這些實體引用其他引用的實體......最后,通常所有實體都將單個user
稱為其owner
。 並不奇怪,因為所查詢的是屬於單個user
實體。 許多行中有更多的部分重復; 實際上,只有一小部分是唯一數據。 由於查詢似乎很慢,我雖然可以通過單獨使用來獲取一些東西
criteria.setFetchMode(path, FetchMode.SELECT);
這適用於我的上述情況,但是當查詢許多用戶(作為管理員)時,它變得很糟糕,因為hibernate為每個 user
發出單獨的查詢,而不是像
SELECT * FROM User WHERE id IN (?, ?, ..., ?)
或者根本不提取它們 (不能比每個實體的一個查詢更糟糕)。 我想知道我錯過了什么?
因此,我沒有獲取大量冗余數據,而是遇到了1 + N問題,顯然會有1 + 1個查詢。
fetch=FetchType.LAZY
;懶惰應該是查詢特定的)? 我認為這不重要,但我的課程就像
class Child {
@ManyToOne Father father;
@ManyToOne Mother mother;
...
}
class Father {
@ManyToOne User owner;
...
}
class Mother {
@ManyToOne User owner;
...
}
而查詢就像
createCriteria(Child.class)
.add(Restrictions.in("id", idList))
.add(Restrictions.eq("isDeleted", false))
.createAlias("Father", "f")
.add(Restrictions.eq("f.isDeleted", false))
.setFetchMode("f.owner", FetchMode.SELECT)
.createAlias("Mother", "m")
.add(Restrictions.eq("m.isDeleted", false))
.setFetchMode("m.owner", FetchMode.SELECT)
.list();
重要的是, owner
不會被使用並且可以被代理。 FetchMode.SELECT
的javadoc說
使用單獨的選擇急切地獲取
所以它基本上承諾我想要的1 + 1查詢,而不是“每個實體使用單獨的選擇”。
我寫了一個小項目來演示這種行為。 根據您的條件生成的SQL如下:
select
this_.id as id1_0_4_,
this_.father_id as father_i3_0_4_,
this_.isDeleted as isDelete2_0_4_,
this_.mother_id as mother_i4_0_4_,
f1_.id as id1_1_0_,
f1_.isDeleted as isDelete2_1_0_,
f1_.owner_id as owner_id3_1_0_,
user5_.id as id1_3_1_,
user5_.isDeleted as isDelete2_3_1_,
m2_.id as id1_2_2_,
m2_.isDeleted as isDelete2_2_2_,
m2_.owner_id as owner_id3_2_2_,
user7_.id as id1_3_3_,
user7_.isDeleted as isDelete2_3_3_
from
Child this_
inner join
Father f1_
on this_.father_id=f1_.id
left outer join
User user5_
on f1_.owner_id=user5_.id
inner join
Mother m2_
on this_.mother_id=m2_.id
left outer join
User user7_
on m2_.owner_id=user7_.id
where
this_.id in (
?, ?
)
and this_.isDeleted=?
and f1_.isDeleted=?
and m2_.isDeleted=?
如其他答案中所述,如果實體關系設置為EAGER
,即JPA默認值,則無法更改Criteria API中的提取模式。 需要將獲取模式更改為LAZY
。
你可以在這里看到它
總結一下我的挫敗感......在這方面,Hibernate充滿了驚喜(錯誤?):
@ManyToOne(fetch=FetchType.LAZY)
聲明屬性,否則無法更改任何內容 FetchType.EAGER
,這是愚蠢的,因為它無法被覆蓋 criteria.setFetchMode(path, FetchMode.SELECT)
是沒有意義的,因為它總是一個無操作(要么被忽略,因為屬性的不可覆蓋的熱切或屬性已經是懶惰的)! @BatchSize
注釋來控制 @BatchSize
注釋會被靜默忽略 為了得到我想要的東西(兩個SQL查詢),我只需要兩件事:
@ManyToOne(fetch=FetchType.LAZY)
聲明屬性@ManyToOne(fetch=FetchType.LAZY)
@BatchSize(size=aLot)
放在屬性的類上 這很簡單,但有點難以找到(因為上面所有被忽略的事情)。 我還沒有查看過fetch配置文件。
除非使用
@ManyToOne(fetch=FetchType.LAZY)
聲明屬性,否則無法更改任何內容
確實,至少在目前,直到獲取配置文件功能被擴展為提供將預先加載更改為延遲的能力。
默認值是
FetchType.EAGER
,這是愚蠢的,因為它無法被覆蓋
是的,我同意它很糟糕,但在Hibernate本機API中,默認情況下一切都是懶惰的; 除非另有明確規定,否則JPA要求一個協會渴望。
使用criteria.setFetchMode(path,FetchMode.SELECT)是沒有意義的,因為它總是一個無操作(要么被忽略,因為屬性的不可覆蓋的熱切或屬性已經是懶惰的)!
有了它,你應該能夠覆蓋其他懶惰的獲取模式。 見HHH-980和此評論約javadoc的混亂導致Hibernate的貢獻者之一。
默認情況下,懶惰地獲取1 + N問題
它與延遲加載無關,如果你沒有在同一個查詢中獲取急切加載的關聯,它也是預先加載的默認值。
它可以通過類級別的
@BatchSize
注釋來控制
您必須將它放在類級別才能使它與該實體的一個關聯生效; 這個答案很有幫助。 對於集合關聯(與其他實體中定義的實體的多對多關聯),您可以靈活地為每個關聯單獨定義它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.