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

进一步检查这个问题,我注意到这个问题是由实体类之间一对一和多对一关系的深层层次结构引起的。所以,我想,我只要把它们设为lazy,就能解决问题了。但是注释@OneToOne(fetch=FetchType.LAZY)或@ManyToOne(fetch=FetchType.LAZY)似乎都不起作用。要么我得到一个异常,要么它们实际上没有被代理对象替换,从而变得懒惰。

你知道我要怎么做吗?注意,我没有使用persistence.xml来定义关系或配置细节,一切都是在java代码中完成的。


当前回答

Hibernate中xtoone的基本思想是它们在大多数情况下都不是懒惰的。

一个原因是,当Hibernate必须决定放置一个代理(带id)或null时, 无论如何,它都必须查看另一个表才能加入。访问数据库中另一个表的代价是巨大的,所以它最好在那个时刻为那个表获取数据(非懒惰行为),而不是在以后的请求中获取数据,这将需要对同一个表进行第二次访问。

编辑:具体请参考ChssPly76的回答。这一个不那么准确和详细,它没有提供任何东西。谢谢ChssPly76。

其他回答

如果关系必须不是双向的,那么@ElementCollection可能比使用惰性的One2Many集合更容易。

Hibernate中xtoone的基本思想是它们在大多数情况下都不是懒惰的。

一个原因是,当Hibernate必须决定放置一个代理(带id)或null时, 无论如何,它都必须查看另一个表才能加入。访问数据库中另一个表的代价是巨大的,所以它最好在那个时刻为那个表获取数据(非懒惰行为),而不是在以后的请求中获取数据,这将需要对同一个表进行第二次访问。

编辑:具体请参考ChssPly76的回答。这一个不那么准确和详细,它没有提供任何东西。谢谢ChssPly76。

这个问题已经很老了,但是在Hibernate 5.1.10中,有一些新的更好的解决方案。

惰性加载除了@OneToOne关联的父端外都有效。这是因为Hibernate没有其他方法来知道是给这个变量分配一个空值还是一个代理。更多细节可以在本文中找到

您可以激活延迟加载字节码增强 或者,您可以删除父端,并使用上面文章中解释的@MapsId的客户端。通过这种方式,你会发现你并不真正需要父端,因为子端与父端共享相同的id,所以你可以通过知道父端id轻松获取子端 .

为了让惰性加载工作在可为空的一对一映射上,您需要让hibernate进行编译时插装,并向一对一关系添加@LazyToOne(value = LazyToOneOption.NO_PROXY)。

示例映射:

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

示例Ant构建文件扩展名(用于Hibernate编译时插装):

<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>

如果子实体是只读的,那么可以简单地设置optional=false。 然后确保每个映射实体的使用都是通过查询预加载的。

public class App {
  ...
  @OneToOne(mappedBy = "app", fetch = FetchType.LAZY, optional = false)
  private Attributes additional;

and

String sql = " ... FROM App a LEFT JOIN FETCH a.additional aa ...";

... 也许坚持也会有用……