我有一个带有@OneToOne关联的实体。像这样:
@Entity
public class Person {
@Id Long id;
@OneToOne(fetch = FetchType.LAZY) Address address;
}
当我加载这样的实体时,Hibernate会忽略LAZY。Vlad解释道:
除了@OneToOne关联的父方之外,懒惰加载也可以工作。这是因为Hibernate没有其他方法可以知道是给这个变量分配null还是代理
这里有类似的陈述。我不明白。有FK列PERSON.ADDRESS_ID,如果有任何值,Hibernate应该知道应该使用Proxy。我是不是错过了什么?
上传:我最初的密码是在科特林。我曾尝试在Java中创建同样的例子,令人惊讶的是,懒惰加载在那里效果很好。
尝试使用@OneToMany
关系来理解它。
当你有了它,你指定了一些集合,即列表,例如我们有一个实体
class A {
@OneToMany
List<B> bs;
public List<B> getBs() {
return bs;
}
}
因此,当hibernate加载A
时,它能够识别出您有List<B>
,并且您可以在类加载后立即调用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,你会得到nullA
。
更正:
正如我已经解释过的,对于可选项关系(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现在知道标识符,它不需要从表中查询它,并且应该能够轻松初始化代理。
然而,它不起作用,仍然急切地加载,这很奇怪,经过一番挖掘,我发现了弗拉德自己报告的这个问题2
尽管我在相关问题上找到了一个变通方法,并就上述问题询问了这是否有效,但这就是为什么不在这里发帖的原因。
1
一些较旧版本的hibernate也支持从父端进行延迟加载,但在最近的版本中,这种行为被删除了,因为它们需要检查子级的存在。2我使用hibernate版本5.4.8.Final和5.4.30.Final 检查了这种行为