如何使JPA一对一关系懒惰

2022-08-31 05:37:27

在我们开发的这个应用程序中,我们注意到视图特别慢。我分析了视图,并注意到有一个查询由休眠执行,即使数据库中只有两个对象需要获取,该查询也花费了10秒。所有和关系都很懒惰,所以这不是问题。在检查正在执行的实际 SQL 时,我注意到查询中有超过 80 个联接。OneToManyManyToMany

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

任何想法,我将如何使它工作?请注意,我不使用 来定义关系或配置细节,一切都是在java代码中完成的。persistence.xml


答案 1

首先,对KLE的答案进行一些澄清:

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

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

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

罗伯·具有有效点,但是您可能无法根据您的模型实现它(例如,如果您的一对一关联空)。

现在,就原始问题而言:

A)应该工作得很好。是否确定它不会在查询本身中被覆盖?可以在 HQL 中指定和/或通过 Criteria API 显式设置获取模式,该模式将优先于类注释。如果不是这种情况,并且您仍然遇到问题,请发布您的类,查询和生成的SQL,以进行更多直截了当的对话。@ManyToOne(fetch=FetchType.LAZY)join fetch

B)更棘手。如果它绝对不可为空,请遵循Rob H.的建议并指定如下:@OneToOne

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

否则,如果可以更改数据库(向 owner 表添加外键列),请执行此操作并将其映射为“已联接”:

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

和在其他实体中:

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

如果你不能做到这一点(并且不能忍受急切的获取),字节码检测是你唯一的选择。我必须同意CPerkins的观点,但是 - 如果你有80!!!由于热切的OneToOne协会而加入,那么你有更大的问题,然后这个:-)


答案 2

要使延迟加载在可为空的一对一映射上工作,您需要让休眠执行编译时检测并添加一对一关系。@LazyToOne(value = LazyToOneOption.NO_PROXY)

映射示例:

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

示例 Ant Build 文件扩展名(用于执行休眠编译时检测):

<property name="src" value="/your/src/directory"/><!-- path of the source files --> 
<property name="libs" value="/your/libs/directory"/><!-- path of your libraries --> 
<property name="destination" value="/your/build/directory"/><!-- path of your build directory --> 

<fileset id="applibs" dir="${libs}"> 
  <include name="hibernate3.jar" /> 
  <!-- include any other libraries you'll need here --> 
</fileset> 

<target name="compile"> 
  <javac srcdir="${src}" destdir="${destination}" debug="yes"> 
    <classpath> 
      <fileset refid="applibs"/> 
    </classpath> 
  </javac> 
</target> 

<target name="instrument" depends="compile"> 
  <taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask"> 
    <classpath> 
      <fileset refid="applibs"/> 
    </classpath> 
  </taskdef> 

  <instrument verbose="true"> 
    <fileset dir="${destination}"> 
      <!-- substitute the package where you keep your domain objs --> 
      <include name="/com/mycompany/domainobjects/*.class"/> 
    </fileset> 
  </instrument> 
</target>

推荐