如何使 JPA OneToOne 关系变得懒惰 [英] How can I make a JPA OneToOne relation lazy

查看:31
本文介绍了如何使 JPA OneToOne 关系变得懒惰的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我们正在开发的这个应用程序中,我们注意到一个视图特别慢.我对视图进行了分析,并注意到 hibernate 执行了一个查询,即使数据库中只有两个对象要获取,它也需要 10 秒.所有 OneToManyManyToMany 关系都是惰性的,所以这不是问题.在检查正在执行的实际 SQL 时,我注意到查询中有 80 多个连接.

进一步检查问题,我注意到问题是由实体类之间的OneToOneManyToOne 关系的深层层次结构引起的.所以,我想,我会让他们偷懒,这应该可以解决问题.但是注释 @OneToOne(fetch=FetchType.LAZY)@ManyToOne(fetch=FetchType.LAZY) 似乎不起作用.要么我得到一个异常,要么它们实际上没有被代理对象替换,因此很懒惰.

我有什么想法可以让它发挥作用吗?请注意,我没有使用 persistence.xml 来定义关系或配置细节,一切都是在 java 代码中完成的.

解决方案

首先,对 KLE 的回答进行一些说明:

  1. 无约束(可为空)一对一关联是唯一一种在没有字节码检测的情况下无法代理的关联.这样做的原因是所有者实体必须知道关联属性是否应该包含代理对象或 NULL 并且它无法通过查看其基表的列来确定,因为一对一通常通过共享 PK 映射,因此它无论如何都必须急切地获取代理,这使得代理毫无意义.这是更详细的解释.

  2. 多对一关联(显然是一对多)不会遇到这个问题.所有者实体可以轻松检查自己的 FK(在一对多的情况下,最初创建空集合代理并按需填充),因此关联可以是惰性的.

  3. 用一对多代替一对一几乎不是一个好主意.您可以用独特的多对一替换它,但还有其他(可能更好)的选择.

Rob H. 有一个有效的观点,但是根据您的模型,您可能无法实现它(例如,如果您的一对一关联可以为空).

现在,就原始问题而言:

A) @ManyToOne(fetch=FetchType.LAZY) 应该可以正常工作.您确定它没有在查询本身中被覆盖吗?可以在 HQL 中指定 join fetch 和/或通过 Criteria API 显式设置获取模式,这将优先于类注释.如果情况并非如此,并且您仍然遇到问题,请发布您的类、查询和生成的 SQL,以便进行更直接的对话.

B) @OneToOne 比较棘手.如果它绝对不能为空,请遵循 Rob H. 的建议并将其指定为:

@OneToOne(optional = false, fetch = FetchType.LAZY)

否则,如果您可以更改数据库(向所有者表添加外键列),请这样做并将其映射为已加入":

@OneToOne(fetch = FetchType.LAZY)@JoinColumn(name="other_entity_fk")公共其他实体 getOther()

在其他实体中:

@OneToOne(mappedBy = "other")公共所有者实体 getOwner()

如果你不能这样做(并且不能忍受急切获取)字节码检测是你唯一的选择.我必须同意 CPerkins,但是 - 如果你有 80 !!! 由于渴望 OneToOne 关联而加入,那么你会遇到更大的问题:-)

In this application we are developing, we noticed that a view was particularly slow. I profiled the view and noticed that there was one query executed by hibernate which took 10 seconds even if there only were two object in the database to fetch. All OneToMany and ManyToMany relations were lazy so that wasn't the problem. When inspecting the actual SQL being executed, I noticed that there were over 80 joins in the query.

Further inspecting the issue, I noticed that the problem was caused by the deep hierarchy of OneToOne and ManyToOne relations between entity classes. So, I thought, I'll just make them fetched lazy, that should solve the problem. But annotating either @OneToOne(fetch=FetchType.LAZY) or @ManyToOne(fetch=FetchType.LAZY) doesn't seem to work. Either I get an exception or then they are not actually replaced with a proxy object and thus being lazy.

Any ideas how I'll get this to work? Note that I do not use the persistence.xml to define relations or configuration details, everything is done in java code.

解决方案

First off, some clarifications to KLE's answer:

  1. Unconstrained (nullable) one-to-one association is the only one that can not be proxied without bytecode instrumentation. The reason for this is that owner entity MUST know whether association property should contain a proxy object or NULL and it can't determine that by looking at its base table's columns due to one-to-one normally being mapped via shared PK, so it has to be eagerly fetched anyway making proxy pointless. Here's a more detailed explanation.

  2. many-to-one associations (and one-to-many, obviously) do not suffer from this issue. Owner entity can easily check its own FK (and in case of one-to-many, empty collection proxy is created initially and populated on demand), so the association can be lazy.

  3. Replacing one-to-one with one-to-many is pretty much never a good idea. You can replace it with unique many-to-one but there are other (possibly better) options.

Rob H. has a valid point, however you may not be able to implement it depending on your model (e.g. if your one-to-one association is nullable).

Now, as far as original question goes:

A) @ManyToOne(fetch=FetchType.LAZY) should work just fine. Are you sure it's not being overwritten in the query itself? It's possible to specify join fetch in HQL and / or explicitly set fetch mode via Criteria API which would take precedence over class annotation. If that's not the case and you're still having problems, please post your classes, query and resulting SQL for more to-the-point conversation.

B) @OneToOne is trickier. If it's definitely not nullable, go with Rob H.'s suggestion and specify it as such:

@OneToOne(optional = false, fetch = FetchType.LAZY)

Otherwise, if you can change your database (add a foreign key column to owner table), do so and map it as "joined":

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name="other_entity_fk")
public OtherEntity getOther()

and in OtherEntity:

@OneToOne(mappedBy = "other")
public OwnerEntity getOwner()

If you can't do that (and can't live with eager fetching) bytecode instrumentation is your only option. I have to agree with CPerkins, however - if you have 80!!! joins due to eager OneToOne associations, you've got bigger problems then this :-)

这篇关于如何使 JPA OneToOne 关系变得懒惰的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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