想象一下关系数据库中的2个表,例如人和计费。在这些实体之间定义了一个(非强制性)OneToOne关联,它们共享Person主键(即PERSON_ID在Person和Billing中定义,并且在后者中是外键)。
通过命名查询(例如:
)对Person进行选择时from Person p where p.id = :id
Hibernate / JPA生成两个选择查询,一个在Person表上,另一个在Billing表上。
上面的示例非常简单,并且不会导致任何性能问题,因为查询只返回一个结果。现在,假设Person
与其他实体(均共享n
主键)具有Person
OneToOne关系(所有非强制关系)。
如果我错了,请纠正我,但在Person上运行select
查询,返回r
行,将导致Hibernate生成(n+1)*r
个选项,即使这些关联是懒惰的
是否存在针对此潜在性能灾难的解决方法(除了根本不使用共享主键)?谢谢你的所有想法。
答案 0 :(得分:8)
想象一下关系数据库中的2个表,例如人和计费。在这些实体之间定义了(非强制性)OneToOne关联,
默认情况下,对于非强制性OneToOne,延迟提取概念是不可能的,Hibernate必须访问数据库才能知道关联是否为null
。来自这个旧维基页面的更多细节:
Some explanations on lazy loading (one-to-one)
[...]
现在考虑我们的B级了 与C
的一对一关联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代理 尚未加载的对象,但是 将在有人使用它时加载。 这给了延迟加载one-to-one
。但现在想象你的
B
对象可能或 可能没有关联C
(constrained="false"
)。什么应该 具体getCee()
时会B
返回 没有C
?空值。但要记住, Hibernate必须设置正确的值 设定B
时的“cee” (因为它不知道什么时候有人 将致电getCee()
)。代理没有 在这里帮助因为代理本身 已经是非空对象。所以简历:如果您的B-> C映射 是强制性的(
constrained=true
), Hibernate将使用C代理 导致延迟初始化。但 如果你允许B没有C,Hibernate 只是要检查C的存在 它加载的时刻B.但是一个SELECT来 检查存在效率低下 因为相同的SELECT可能不仅仅是 检查存在,但加载整个 宾语。所以延迟加载消失。
所以,不可能......默认情况下。
是否存在针对此潜在性能灾难的解决方法(除了根本不使用共享主键)?谢谢你的所有想法。
问题不在于共享主键,无论是否有共享主键,您都会得到它,问题是可空 OneToOne。
第一个选项:使用字节码检测(请参阅下面的文档参考)和 no-proxy 提取:
@OneToOne( fetch = FetchType.LAZY )
@org.hibernate.annotations.LazyToOne(org.hibernate.annotations.LazyToOneOption.NO_PROXY)
第二个选项:使用假的ManyToOne(fetch=FetchType.LAZY)
。这可能是最简单的解决方案(据我所知,推荐的解决方案)。但我没有用共享的PK测试这个。
第三个选项:使用join fetch
预先加载结算。
答案 1 :(得分:1)
这是Hibernate的常见性能问题(只搜索“Hibernate n + 1”)。避免n + 1个查询有三种选择:
答案 2 :(得分:0)
您可以尝试“盲猜优化”,这对“n + 1选择问题”有利。 像这样注释你的字段(或getter):
@org.hibernate.annotations.BatchSize(size = 10)
java.util.Set<Billing> bills = new HashSet<Billing>();
答案 3 :(得分:0)
远离hibernate的OneToOne映射
这是非常破碎和危险的。您是远离数据库损坏问题的一个小错误。
http://opensource.atlassian.com/projects/hibernate/browse/HHH-2128
答案 4 :(得分:0)
如果您将关系指定为延迟或明确指示您希望hibernate运行单独的查询,则只会出现“n + 1”问题。
Hibernate可以通过选择Person上的外部联接来获取与Billing的关系,完全避免了n + 1问题。我认为这是你的hbm文件中的fetch =“XXX”指示。
答案 5 :(得分:0)
将 optional =true 与这样的一对一关系使用以避免 n+1 问题
@OneToOne(fetch = FetchType.LAZY, optional=true)
@PrimaryKeyJoinColumn