双向 OneToOne 被多次加载
Bidirectional OneToOne gets Loaded multiple Times
我有两个实体通过双向 OneToOne 关联连接,如下所示:
@Entity
class Parent {
@NotNull
String businessKey;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "child_id")
@JsonManagedReference
Child child;
}
@Entity
class Child {
@JsonBackReference
@OneToOne(mappedBy = "child", fetch = FetchType.LAZY)
Parent parent;
}
背景
我正在使用 Spring 数据和 CRUDRepository 来使用生成的查询加载父项,例如 Parent findByBusinessKey(String businessKey)
因为默认的 FetchMode 是 Eager,所以子实体通过其在父 Table 中的 child_id
外键获取。 Eager fetching 在这里不是问题,因为整个图稍后会序列化为 json。
最初关联是单向的,Child 没有引用其 Parent。不幸的是,必须添加反向引用并且性能极度下降。生成的 Sql 的日志记录显示以下查询(简化):
select p.id, p.business_key, ..., p.child_id from Parent p where p.business_key = ?;
select c.id, ... from Child c where c.id = ?;
-- here it goes wrong
select p.id, p.business_key, ... from Parent p
left join OtherEagerJoinedTable t on p.other_id = t.id
left join AnotherEagerJoinedTable a on p.another_id = a.id
where p.child_id = ?;
Hibernate 似乎从数据库中单独获取父级,而不是使用已经选择的实例。对于像这样的每个 OneToOne 关系(有几个)都会发生这种情况,每个连接查询最多需要 200 毫秒。使用单向映射,整个实体图的查询时间不到 100 毫秒。
问题
如何告诉 Hibernate 父实体已经存在并且不需要提取?我在 Child 实体中尝试了 optional=false
,但它没有帮助。
版本
我目前正在使用 hibernate 5.2.12 和 Spring Boot 1.2.8。不幸的是,版本更新目前不是一个选项。此外,即使数据库方面的更改仍然是可能的,也需要在其他地方进行相对较大的重构工作。
我只需要告诉 hibernate 不要急切地从 Db 中获取父引用。也许 EntityGraph 可以帮助解决这个问题,但我还没有使用过它们,现在也不太了解它。
另见
- https://vladmihalcea.com/hibernate-facts-the-importance-of-fetch-strategy(结论部分提到可选属性,但本例中的拥有方与我的情况相反)
- Hibernate performs twice the same query if a bidirectional relationship is defined
- How can I make a JPA OneToOne relation lazy
感谢任何帮助
@OneToOne 映射就是这种情况。我去过那里,做过那件事。要通过它,您必须在 Hibernate 脏检查机制上实现字节码增强,并更改父端不使用代理(注释:@LazyToOne(LazyToOneOption.NO_PROXY))。
请参考Vlad Mihalcea's article describing exactly your case I think
在那篇文章中,您还将找到 link 另一篇有用的 Vlad 文章 - 有关如何实现字节码增强的信息
我有两个实体通过双向 OneToOne 关联连接,如下所示:
@Entity
class Parent {
@NotNull
String businessKey;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "child_id")
@JsonManagedReference
Child child;
}
@Entity
class Child {
@JsonBackReference
@OneToOne(mappedBy = "child", fetch = FetchType.LAZY)
Parent parent;
}
背景
我正在使用 Spring 数据和 CRUDRepository 来使用生成的查询加载父项,例如 Parent findByBusinessKey(String businessKey)
因为默认的 FetchMode 是 Eager,所以子实体通过其在父 Table 中的 child_id
外键获取。 Eager fetching 在这里不是问题,因为整个图稍后会序列化为 json。
最初关联是单向的,Child 没有引用其 Parent。不幸的是,必须添加反向引用并且性能极度下降。生成的 Sql 的日志记录显示以下查询(简化):
select p.id, p.business_key, ..., p.child_id from Parent p where p.business_key = ?;
select c.id, ... from Child c where c.id = ?;
-- here it goes wrong
select p.id, p.business_key, ... from Parent p
left join OtherEagerJoinedTable t on p.other_id = t.id
left join AnotherEagerJoinedTable a on p.another_id = a.id
where p.child_id = ?;
Hibernate 似乎从数据库中单独获取父级,而不是使用已经选择的实例。对于像这样的每个 OneToOne 关系(有几个)都会发生这种情况,每个连接查询最多需要 200 毫秒。使用单向映射,整个实体图的查询时间不到 100 毫秒。
问题
如何告诉 Hibernate 父实体已经存在并且不需要提取?我在 Child 实体中尝试了 optional=false
,但它没有帮助。
版本
我目前正在使用 hibernate 5.2.12 和 Spring Boot 1.2.8。不幸的是,版本更新目前不是一个选项。此外,即使数据库方面的更改仍然是可能的,也需要在其他地方进行相对较大的重构工作。 我只需要告诉 hibernate 不要急切地从 Db 中获取父引用。也许 EntityGraph 可以帮助解决这个问题,但我还没有使用过它们,现在也不太了解它。
另见
- https://vladmihalcea.com/hibernate-facts-the-importance-of-fetch-strategy(结论部分提到可选属性,但本例中的拥有方与我的情况相反)
- Hibernate performs twice the same query if a bidirectional relationship is defined
- How can I make a JPA OneToOne relation lazy
感谢任何帮助
@OneToOne 映射就是这种情况。我去过那里,做过那件事。要通过它,您必须在 Hibernate 脏检查机制上实现字节码增强,并更改父端不使用代理(注释:@LazyToOne(LazyToOneOption.NO_PROXY))。
请参考Vlad Mihalcea's article describing exactly your case I think
在那篇文章中,您还将找到 link 另一篇有用的 Vlad 文章 - 有关如何实现字节码增强的信息