我的问题是,对于每个空关系,Hibernate急切加载OneToOne关联会执行+1选择。
实体示例:
@Entity
class SideBlue {
@Column(nullable = false)
private Integer timestamp;
@OneToOne(optional=true)
@JoinColumn(name="timestamp", referenceColumn="timestamp", insertable = false, updatable = false)
SideRed redSide;
}
@Entity
class SideRed {
@Column(nullable = false)
private Integer timestamp;
}
(这是遗留数据库模式,因此不允许进行数据库修改)
查询示例:
CriteriaBuilder builder... CriteriaQuery query...
Root<SideBlue> root = query.from(SideBlue.class);
root.fetch(SideBlue_.sideRed, JoinType.LEFT);
entityManager().createQuery(query).getResultList();
结果: 如果所有蓝色边实体都有一个红色边,一切都正常,所以hibernate只对数据库执行一个查询,以便检索哪个实体。
但是,如果蓝方实体没有关联的红方实体,那么hibernate会再次尝试找到另一方。 Hibernate sql注释为每个null redSide属性说'/ * load RedSide * / select ...'。
如何跳过第二次选择?
当延迟不是非常低时,会出现实际问题。如果我尝试选择1百万行,并且1/3具有空的“红色边”,则添加的总延迟是一个真正的问题。
修改
这是查询的调试日志
10:04:32.812 [main] DEBUG org.hibernate.loader.Loader - Result set row: 0
10:04:32.815 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[SideBlue#1269721], EntityKey[SideRed#3620564]
10:04:32.833 [main] DEBUG org.hibernate.loader.Loader - Result set row: 1
10:04:32.833 [main] DEBUG org.hibernate.loader.Loader - Result row: EntityKey[SideBlue#1269776], null
第一行包含蓝色和红色边,但第二行仅包含蓝色边。所以hibernate必须知道相关的红色方面不存在。但是,在处理完所有结果行之后......
10:04:33.083 [main] DEBUG o.h.engine.internal.TwoPhaseLoad - Resolving associations for [BlueSide#1269721]
10:04:33.084 [main] DEBUG org.hibernate.loader.Loader - Loading entity: [RedSide#component[timestamp]{timestamp=1338937390}]
10:04:33.084 [main] DEBUG org.hibernate.SQL - /* load RedSide */ select ...
! Nothing really loaded because the previous SQL return empty result set, again !
10:04:33.211 [main] DEBUG org.hibernate.loader.Loader - Done entity load
答案 0 :(得分:1)
好吧,当你对SideBlue进行查询时,你试图不加载SideRed。我认为这是一个与Hibernate(来自https://community.jboss.org/wiki/SomeExplanationsOnLazyLoadingone-to-one?_sscc=t)的“限制”相关的延迟加载问题:
class B { private C cee; public C getCee() { return cee; } public void setCee(C cee) { this.cee = cee; } } class C { // Not important really }
在加载B之后,你可以调用getCee()来获得C.但是看, getCee()是你的类的一个方法,而Hibernate无法控制 它。 Hibernate不知道有人打算调用getCee()。 这意味着Hibernate必须将适当的值放入“cee”属性中 目前它从数据库加载B。
如果为C启用了代理, Hibernate可以放置一个尚未加载的C代理对象,但会 当有人使用它时加载。这给了延迟加载 一个对一个。
但现在想象你的B物可能有也可能没有 关联C(约束=“假”)。什么应该getCee()返回时 具体B没有C?空值。但请记住,Hibernate必须设置 在设置B的那一刻,“cee”的正确值(因为它不知道 有人会调用getCee())。代理在这里没有帮助,因为 代理本身在已经非空的对象中。
所以简历:如果你的B-> C 映射是强制的(约束=真),Hibernate将使用代理 C导致延迟初始化。但如果你允许B没有C, Hibernate只是在加载B时检查C的存在。 但是,检查存在的SELECT是低效的,因为相同 SELECT可能不只是检查存在,而是加载整个对象。太懒了 装载消失了。