[英]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.