如何在使用JPA和Hibernate时解决LazyInitializationException

时间:2009-02-23 17:04:56

标签: java hibernate jpa orm lazy-initialization

我正在为想要使用延迟初始化的客户开发一个项目。 当使用默认的延迟加载模式映射类时,它们总是会得到“延迟初始化异常”。

@JoinTable(name = "join_profilo_funzionalita", joinColumns = {@JoinColumn(name =    "profilo_id", referencedColumnName = "profilo_id")}, inverseJoinColumns = {@JoinColumn(name = "funzionalita_id", referencedColumnName = "funzionalita_id")})
//@ManyToMany(fetch=FetchType.EAGER) - no exceptions if uncommented
@ManyToMany 
private Collection<Funzionalita> funzionalitaIdCollection;

是否有使用JPA类的标准模式来避免此错误?

欢迎使用代码段,非常感谢您的时间。

9 个答案:

答案 0 :(得分:60)

Hibernate 4.1.6最终解决了这个问题:https://hibernate.atlassian.net/browse/HHH-7457

你需要设置hibernate属性hibernate.enable_lazy_load_no_trans = true

以下是如何在Spring中执行此操作:

<bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="myDataSource"/>
    <property name="packagesToScan" value="com.mycompany.somepackage"/>
    <property name="jpaVendorAdapter" ref="hibernateVendorAdapter"/>
    <property name="jpaDialect" ref="jpaDialect"/>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.enable_lazy_load_no_trans">true</prop>
        </props>
    </property>
</bean>

瞧;现在,在hibernate会话之外导航域模型时,您不必担心LazyInitializationException(“JPA-speak”中的持久性上下文)

答案 1 :(得分:16)

预取属性的方法有很多种,所以在会话结束后它们就在那里:

  1. 调用适当的getter。在将字段提取到bean之后,会话关闭后就会存在。
  2. 您可以在EJBQL查询中初始化字段,查找JOIN FETCH关键字。
  3. 启用AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS,如果您使用的是支持它的Hibernate版本。
  4. 尝试这些解决方案时可能会出现几个问题:

    1. JIT编译器可能会优化getter的调用(有时需要一段时间)。
    2. 您尝试JOIN FETCH的实体可能通过涉及List的多个“多个”关系进行链接。在这种情况下,生成的查询会返回不明确的结果,Hibernate将拒绝在单个查询中获取您的数据。
    3. 已经有一个interesting bug related to AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS。还有更多因为hibernate的人说:注意:这可能发生在交易之外并且不安全。谨慎使用。你大部分都是自己。
    4. 最好的方法是首先尝试JOIN FETCH。如果这不起作用,请尝试使用getter方法。如果JIT编译器在运行时将其搞砸了,请将结果分配给public static volatile Object

      或者停止使用Hibernate ......

答案 2 :(得分:14)

请注意,您不应该使用hibernate.enable_lazy_load_no_trans之前的Hibernate 4.1.7,因为它会泄漏连接。见https://hibernate.onjira.com/browse/HHH-7524

答案 3 :(得分:8)

LazyInitializationException意味着您在hibernate会话关闭后或者在从会话中分离对象之后调用该集合。

您需要将对象重新附加到休眠会话,更改调用集合的位置,或将会话关闭的边界移动到更高层。

答案 4 :(得分:5)

OpenSessionInView是一种处理此问题的模式。这里有一些信息:

http://www.hibernate.org/43.html

在实施此模式并理解其含义时,您需要谨慎。每次在视图中导航延迟关联时,它都会触发另一个SQL查询来加载数据。如果您的用例是这些SQL查询的数量和大小很小,那么这可能无关紧要。确保至少调整日志记录设置,以便查看Hibernate在后台“神奇地”执行哪种查询以便加载数据。

还要考虑您正在编写的应用程序类型。如果您不处理远程处理(没有Web服务,没有基于AJAX的Web客户端),那么OSIV可能会非常好用。但是,如果远程处理序列化程序开始遍历整个对象图,则可能会触发大量的SQL查询并使您的数据库和应用程序服务器瘫痪。

答案 5 :(得分:5)

The best way to solve the LazyInitializationException是在实体查询中使用JOIN FETCH指令。

FetchType.EAGER loading对性能不利。此外,还有反模式,如:

您永远不应该使用它,因为它们要求为UI呈现打开数据库连接(在视图中打开会话),或者在初始持久化上下文之外获取的每个惰性关联都需要数据库连接({ {1}})。

有时,您甚至不需要实体,DTO投影甚至更好。 只有在需要修改实体时才应获取实体。对于只读交易,DTO projections are better

答案 6 :(得分:4)

当您使用集合并且想要使用延迟加载对其进行初始化时,请在会话关闭之前使用该集合。如果你想使用会话就此结束,那么你会得到lazyinitializeException,因为懒惰是默认尝试。

答案 7 :(得分:1)

Oracle Java教程指出“Enterprise Bean支持事务,即管理共享对象并发访问的机制。”因此,为了处理Lazy Fetch问题,我创建了一个无状态Java会话Bean,然后在从方法返回之前获取我需要的所有子类。这可以避免延迟提取异常。 Oracle还将此称为“SessionFaçade”核心J2EE模式。这种模式似乎比提到的其他一些做法更好。

答案 8 :(得分:0)

我正在开发一个项目,旨在解决使用ModelMapper将实体映射到DTO时常见的JPA问题。该问题已在该项目中得到解决。项目链接:JPA Model Mapper

  

&#34;对于将实体声明为延迟加载的性能至关重要,所以我们   每次我们需要一些数据时都不需要获取所有相关实体。   但这种技术会导致一些问题。最常见的是   LazyInitializationException有时可能非常烦人。   大多数情况下,我们只想要一个未加载的空对象   实体而不是在访问时抛出异常的对象...&#34;

来源:JPA Model Mapper

因此,在项目中,我们通过为所有未加载的实体设置null来处理LazyInitializationException。以下示例显示了它的工作原理。

为所有未加载的实体重新映射实体设置为null:

TypedQuery<SystemEntity> query =
        em.createQuery("select s from SystemEntity s where s.id = 1",  SystemEntity.class);

SystemEntity system = query.getSingleResult();
return new JpaModelMapper(em).mapEntity(system, SystemEntity.class);

将实体重新映射到所有未加载实体的DTO设置为null:

TypedQuery<SystemEntity> query =
        em.createQuery("select s from SystemEntity s where s.id = 1",  SystemEntity.class);

SystemEntity system = query.getSingleResult();
return new JpaModelMapper(em).mapEntity(system, SystemDTO.class);

有关更多信息,请参阅JPA Model Mapper