与共享主键的OneToOne关系生成n + 1个选择;任何解决方法? [英] OneToOne relationship with shared primary key generates n+1 selects; any workaround?

查看:156
本文介绍了与共享主键的OneToOne关系生成n + 1个选择;任何解决方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在关系数据库中想象两个表,例如人员和帐单。在这些实体之间定义了一个(非强制性的)OneToOne关联,它们共享Person主键(即PERSON_ID在Person和Billing中定义,并且它是后者中的外键)。



在通过命名查询进行人员选择时,如:

  from Person p where Hibernate / JPA生成两个select查询,一个在Person表中,另一个在Person表中,另一个在Person表中。

上面的示例非常简单,并且不会导致任何性能问题,因为查询只返回一个结果。现在,假设 Person 与其他实体(所有共享 n OneToOne关系(全部非强制) c $ c> Person 主键)。

如果我错了,请纠正我,但运行选择对Person进行查询,返回 r 行,会导致(n + 1)* r 选择由Hibernate生成,即使关联是 lazy

是否有解决此潜在性能灾难的方法(除了不使用共享主键)?感谢您提出您的所有想法。

解析方案


设想关系数据库中的2个表,例如人员和帐单。在这些实体之间定义了一个(非强制的)OneToOne关联,

从非概念上讲,非强制OneToOne默认情况下,Hibernate必须命中数据库才能知道关联是否为 null 。来自这个旧维基页面的更多细节:


关于延迟加载的一些解释(一对一)



[...]



现在考虑我们的B类有
一对一的关联到C

  class B {
private C cee;

public C getCee(){
return cee;
}

public void setCee(C cee){
this.cee = cee;
}
}

class C {
//真的不重要
}

在加载B之后,您可以调用
getCee()来获得C.但是, b $ b getCee()是您的类
的一种方法,Hibernate无法控制它。
Hibernate不知道某人
何时会调用 getCee()
意味着Hibernate必须在
数据库中加载B时将
适当的值放入 cee
属性中。如果为
C 启用了代理,Hibernate可以将一个尚未加载的C代理
对象,但
将被加载当有人使用它时。
这为
一对一提供了延迟加载。

但现在想象你的 B 对象may或
可能没有关联 C
constrained =false)。当特定 B
没有<$ c $时,
getCee() ç> C ?空值。但请记住,
Hibernate在设置 B
时必须设置正确的
cee值(因为它不知道何时某人
将会调用 getCee())。代理不
在这里帮助,因为代理本身在
中已经是非空对象。所以简历:如果你的B-> C映射
是强制性的( constrained = true ),
Hibernate将使用C
的代理,导致延迟初始化。但是
,如果你允许B没有C,Hibernate
只是在加载B时检查C在
的存在。但是一个SELECT到
检查存在是低效的
因为相同的SELECT可能不仅仅是
检查存在,而是加载整个
对象。所以懒加载消失


所以,默认情况下不可能。


是否有解决此潜在性能灾难的方法(除了根本不使用共享主键)?感谢您的所有想法。


问题不在于共享主键,有或没有共享主键,您会得到它的问题是可空 OneToOne。



第一选项:使用字节码检测(请参阅文档下面)和 no-proxy 获取:

  @OneToOne(fetch = FetchType.LAZY)
@ org.hibernate.annotations.LazyToOne(org.hibernate.annotations.LazyToOneOption.NO_PROXY)

第二选项:使用假的 ManyToOne(fetch = FetchType.LAZY)。这可能是最简单的解决方案(据我所知,推荐的解决方案)。但我没有用共享的PK来测试它。



第三选项:使用
$ b

  • a href =https://stackoverflow.com/questions/1444227/making-a-onetoone-relation-lazy>制作OneToOne关系懒惰



  • 引用




    Imagine 2 tables in a relational database, e.g. Person and Billing. There is a (non-mandatory) OneToOne association defined between these entities, and they share the Person primary key (i.e. PERSON_ID is defined in both Person and Billing, and it is a foreign key in the latter).

    When doing a select on Person via a named query such as:

    from Person p where p.id = :id
    

    Hibernate/JPA generates two select queries, one on the Person table and another on the Billing table.

    The example above is very simple and would not cause any performance issues, given the query returns only one result. Now, imagine that Person has n OneToOne relationships (all non-mandatory) with other entities (all sharing the Person primary key).

    Correct me if I'm wrong, but running a select query on Person, returning r rows, would result in (n+1)*r selects being generated by Hibernate, even if the associations are lazy.

    Is there a workaround for this potential performance disaster (other than not using a shared primary key at all)? Thank you for all your ideas.

    解决方案

    Imagine 2 tables in a relational database, e.g. Person and Billing. There is a (non-mandatory) OneToOne association defined between these entities,

    Lazy fetching is conceptually not possible for non-mandatory OneToOne by default, Hibernate has to hit the database to know if the association is null or not. More details from this old wiki page:

    Some explanations on lazy loading (one-to-one)

    [...]

    Now consider our class B has one-to-one association to C

    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.

    So, not possible... by default.

    Is there a workaround for this potential performance disaster (other than not using a shared primary key at all)? Thank you for all your ideas.

    The problem is not the shared primary key, with or without shared primary key, you'll get it, the problem is the nullable OneToOne.

    First option: use bytecode instrumentation (see references to the documentation below) and no-proxy fetching:

    @OneToOne( fetch = FetchType.LAZY )
    @org.hibernate.annotations.LazyToOne(org.hibernate.annotations.LazyToOneOption.NO_PROXY)
    

    Second option: Use a fake ManyToOne(fetch=FetchType.LAZY). That's probably the most simple solution (and to my knowledge, the recommended one). But I didn't test this with a shared PK though.

    Third option: Eager load the Billing using a join fetch.

    Related question

    References

    这篇关于与共享主键的OneToOne关系生成n + 1个选择;任何解决方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆