java - 具有共享主键的 OneToOne 关系生成 n+1 个选择;任何解决方法?

标签 java hibernate jpa

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

当通过命名查询对 Person 进行选择时,例如:

from Person p where p.id = :id

Hibernate/JPA 生成两个选择查询,一个在 Person 表上,另一个在 Billing 表上。

上面的示例非常简单,不会导致任何性能问题,因为查询只返回一个结果。现在,想象一下 Personn与其他实体(所有共享 Person 主键)的 OneToOne 关系(所有非强制)。

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

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

最佳答案

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

默认情况下,对于非强制性的 OneToOne,延迟获取在概念上是不可能的,Hibernate 必须访问数据库才能知道关联是否为 null。这个旧 wiki 页面的更多详细信息:

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.

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

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.

问题不是共享主键,有或没有共享主键,你会明白的,问题是nullable OneToOne。

第一个选项:使用字节码检测(参见下面的文档引用)和无代理抓取:

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

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

第三个选项:使用join fetch 急切加载账单。

相关问题

引用资料

关于java - 具有共享主键的 OneToOne 关系生成 n+1 个选择;任何解决方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1002547/

相关文章:

java - Eclipse Indigo - JPA 验证问题

Java子字符串错误: String index out of range

java - 可执行Jar,如何设置应用程序名称?

java - Tapestry XML 输出

java - 尝试使用 ProcessBuilder 执行 Java 应用程序时出现类未找到异常

java - 我如何为给定的 SQL 查询编写 JPA 查询

Spring-Hibernate 应用程序 : Illegal access: this web application instance has been stopped already

java - 更新期间 hibernate 不刷新

java - 检门+Spring+JPA+ hibernate : No Persistence Unit Found

java - jpql 根据另一个表中的某些属性从表中选择对象?