使用Hibernate懒惰加载@OneToOne关系



我有一个带有@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 检查了这种行为

最新更新