[英]Lazy loading of @OneToOne relation with Hibernate
我有一個具有@OneToOne 關聯的實體。 像這樣:
@Entity
public class Person {
@Id Long id;
@OneToOne(fetch = FetchType.LAZY) Address address;
}
當我加載這樣的實體Hibernate 忽略 LAZY 。 弗拉德解釋說:
除了 @OneToOne 關聯的父方之外,延遲加載有效。 這是因為 Hibernate 沒有其他方法可以知道是將 null 還是代理分配給此變量。
類似的聲明在這里。 我不明白。 有 FK 列 PERSON.ADDRESS_ID 並且如果有任何值 Hibernate應該知道應該使用代理。 我錯過了什么嗎?
UPADTE:我的原始代碼在 Kotlin 中。 我試圖在 Java 中創建相同的示例,並且令人驚訝的是延遲加載在那里工作得很好。
嘗試使用@OneToMany
關系來理解它。
當你擁有它時,你指定一些集合,即列表,例如我們有一個實體
class A {
@OneToMany
List<B> bs;
public List<B> getBs() {
return bs;
}
}
因此,當 hibernate 加載A
時,它能夠識別出您有List<B>
並且您可以在加載 class 之后調用getBs()
,因此 hibernate 將創建一個沒有等待列表的任何B
直到您對列表執行任何操作,即。 迭代,添加等
執行操作后,hibernate 將發出查詢並將對象加載到集合中,因此延遲加載在這里可以正常工作。
這就是為什么一對多默認是惰性的
現在讓我們以@OneToOne
為例
class A {
@OneToOne
B b;
public B getB() {}
}
hibernate 加載 A 時,會看到用戶可能會在加載完A
后調用getB
,所以它也需要初始化B
現在,即使B
支持代理,hibernate 也必須使用代理或 null 初始化它以及如何做出該決定,它必須查詢B
以檢查它是否存在,但如果它查詢只是為了檢查,為什么只檢查只是,為什么不完全初始化它,因此它急切地做它,忽略Lazy
屬性1
但這不適用於子方,如果您在子方指定@One-To-One
class B {
@OneToOne(lazy)
A a;
public A getA() {}
}
因為這是持有A
實體表的外鍵的表的實體,所以 hibernate 將使用 A 的代理對其進行初始化,因為 hibernate 知道該實體是子實體並且具有關聯的外鍵,因此它可以在需要時延遲加載,如果它是 null,你會得到 null A
。
更正:
正如我已經解釋的那樣,上述行為對於可選關系( optional = true
)是顯而易見的,您可能會發現其他答案說明這一點,但是當您使用optional=false
時並不明顯。
對於非可選關系,我們會認為 hibernate 識別出父級將存在一個子級,因此 hibernate 將初始化代理,它應該描述延遲加載行為。
但是即使要初始化代理,hibernate 也需要最少的信息,如標識符,並且需要從子表中查詢,因此它與可選關系相同,並且急切地加載。
有一種解決方案仍然可以使它工作(至少我是這么認為的),如果您使用@MapsId
與子實體共享父實體的主鍵
class A {
@Id
private Integer id;
@OneToOne(fetch = Lazy, mappedBy = "a", optional = false)
private B b;
}
class B {
@Id
private Integer id;
@OneToOne
@MapsId
private A a;
}
這應該有效,因為現在您正在與孩子共享父主鍵,並且 hibernate 現在知道標識符並且不需要從表中查詢它並且應該能夠輕松初始化代理。
但是,它不起作用並且仍然急切地加載,這很奇怪,經過一番挖掘后,我發現了 Vlad 自己報告的這個問題。 2
盡管我在相關問題中找到了一種解決方法,並且還詢問了上述問題是否有效,但這就是為什么不在這里發布的原因。
2我使用 hibernate 版本 5.4.8.Final 和 5.4.30.Final 檢查了這種行為
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.