使用FetchMode.JOIN的Hibernate OneToOne(optional = true)尝试重新选择空值

时间:2012-11-14 20:18:19

标签: java hibernate jpa orm

我的问题是,对于每个空关系,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

1 个答案:

答案 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可能不只是检查存在,而是加载整个对象。太懒了   装载消失了。