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

查看:161
本文介绍了如何使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)

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

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

以及在OtherEntity中

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

如果您不能做到这一点(并且不能随心所欲地抓取),则字节码检测是您唯一的选择.我必须同意 CPerkins ,但是-如果由于急切的OneToOne关联而有 80 !!! 个加入,那么您会遇到更大的问题:-)

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天全站免登陆