gpt4 book ai didi

java - Hibernate OneToOne(optional=true) with FetchMode.JOIN 尝试重新选择空值

转载 作者:搜寻专家 更新时间:2023-11-01 00:51:34 27 4
gpt4 key购买 nike

我的问题是 OneToOne 关联的 hibernate 预加载会为每个空关系执行 +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 ...'。

如何跳过第二次选择?

当延迟不是极低时,就会出现实际问题。如果我尝试选择 100 万行,并且 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

最佳答案

好吧,当您查询 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
}

Right after loading B, you may call getCee() to obtain C. But look, getCee() is a method of YOUR class and Hibernate has no control over it. Hibernate does not know when someone is going to call getCee(). That means Hibernate must put an appropriate value into "cee" property at the moment it loads B from database.

If proxy is enabled for C, Hibernate can put a C-proxy object which is not loaded yet, but will be loaded when someone uses it. This gives lazy loading for one-to-one.

But now imagine your B object may or may not have associated C (constrained="false"). What should getCee() return when specific B does not have C? Null. But remember, Hibernate must set correct value of "cee" at the moment it set B (because it does no know when someone will call getCee()). Proxy does not help here because proxy itself in already non-null object.

So the resume: if your B->C mapping is mandatory (constrained=true), Hibernate will use proxy for C resulting in lazy initialization. But if you allow B without C, Hibernate just HAS TO check presence of C at the moment it loads B. But a SELECT to check presence is just inefficient because the same SELECT may not just check presence, but load entire object. So lazy loading goes away.

关于java - Hibernate OneToOne(optional=true) with FetchMode.JOIN 尝试重新选择空值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13386635/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com