简体   繁体   English

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

[英]Hibernate OneToOne(optional=true) with FetchMode.JOIN try to re-select null values

My problem is that hibernate eager loading of OneToOne association execute +1 select for each null relation. 我的问题是,对于每个空关系,hibernate急切加载OneToOne关联会执行+1 select。

Entity example: 实体示例:

@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;
}

(It's a legacy database schema, so database modifications is not allowed) (这是遗留数据库模式,因此不允许进行数据库修改)

Query example: 查询示例:

CriteriaBuilder builder... CriteriaQuery query...
Root<SideBlue> root = query.from(SideBlue.class);
root.fetch(SideBlue_.sideRed, JoinType.LEFT);
entityManager().createQuery(query).getResultList();

The result: If all blue side entities has one red side, everything goes correctly, so hibernate only execute one query to the database for whichever entities will be retrieved. 结果:如果所有蓝色边实体都有一个红色边,一切都正常,所以hibernate只对数据库执行一个查询,无论哪个实体都将被检索。

But, if blue side entities has no red side entity associated, hibernate try to find the other side one more time. 但是,如果蓝方实体没有关联的红色实体,那么hibernate会再次尝试找到另一方。 Hibernate sql comment says '/* load RedSide */ select ...' for each null redSide property. Hibernate sql注释为每个null redSide属性说'/ * load RedSide * / select ...'。

How can I skip this second select? 如何跳过第二次选择?

The practical problem appears when latency is not extremely low. 当延迟不是非常低时,出现实际问题。 If I try to select 1million rows, and 1/3 have null 'red sides', the total latency added is a real problem. 如果我尝试选择1百万行,并且1/3具有空的“红色边”,则添加的总延迟是一个真正的问题。

EDIT: 编辑:

This is the debug log for the query 这是查询的调试日志

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

The first row contains the blue and red sides, but the second one only the blue side. 第一行包含蓝色和红色边,但第二行仅包含蓝色边。 So hibernate must know that related red side dont exists. 所以hibernate必须知道相关的红色方面不存在。 But, after all result rows are processed... 但是,在处理完所有结果行之后......

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

Well, you trying to not load SideRed when you do a query for SideBlue. 好吧,当你对SideBlue进行查询时,你试图不加载SideRed。 I think it's a Lazy loading problem that is related with this "limitation" from Hibernate (from https://community.jboss.org/wiki/SomeExplanationsOnLazyLoadingone-to-one?_sscc=t ): 我认为这是一个与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. 在加载B之后,你可以调用getCee()来获得C.但是看看,getCee()是你的类的一个方法,而Hibernate无法控制它。 Hibernate does not know when someone is going to call getCee(). Hibernate不知道有人打算调用getCee()。 That means Hibernate must put an appropriate value into "cee" property at the moment it loads B from database. 这意味着Hibernate必须在从数据库加载B时将适当的值放入“cee”属性。

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. 如果为C启用了代理,Hibernate可以放置一个尚未加载的C代理对象,但是当有人使用它时将加载它。 This gives lazy loading for one-to-one. 这为一对一提供了延迟加载。

But now imagine your B object may or may not have associated C (constrained="false"). 但现在想象你的B对象可能有也可能没有关联的C(约束=“假”)。 What should getCee() return when specific B does not have C? 当特定B没有C时,getCee()应该返回什么? 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()). 但请记住,Hibernate必须在设置B时设置正确的“cee”值(因为它不知道有人何时会调用getCee())。 Proxy does not help here because proxy itself in already non-null object. 代理在这里没有帮助,因为代理本身已经是非null对象。

So the resume: if your B->C mapping is mandatory (constrained=true), Hibernate will use proxy for C resulting in lazy initialization. 所以简历:如果您的B-> C映射是强制性的(约束=真),Hibernate将使用C代理导致延迟初始化。 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. 但是如果你允许B没有C,那么Hibernate只是在它加载B时检查C的存在。但是检查存在的SELECT效率很低,因为相同的SELECT可能不仅仅检查存在,而是加载整个对象。 So lazy loading goes away. 懒惰的装载消失了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM