52

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

@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 类的标准模式来避免此错误?

欢迎提供片段,非常感谢您的宝贵时间。

4

9 回答 9

60

Hibernate 4.1.6 finally solves this issue: https://hibernate.atlassian.net/browse/HHH-7457

You need to set the hibernate-property hibernate.enable_lazy_load_no_trans=true

Here's how to do it in 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>

Voila; Now you don't have to worry about LazyInitializationException while navigating your domain-model outside of a hibernate-session (persistence-context in "JPA-speak")

于 2012-08-11T09:10:05.043 回答
17

有很多方法可以预取属性,所以它们在会话关闭后就在那里:

  1. 调用适当的吸气剂。将字段提取到 bean 后,会话关闭后它就在那里。
  2. 您可以在 EJBQL 查询中初始化字段,查找JOIN FETCH关键字。
  3. 如果您使用的是支持它的 Hibernate 版本,请启用 AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS。

当您尝试这些解决方案时,可能会出现几个问题:

  1. getter 的调用可能会被 JIT 编译器优化掉(有时这需要一段时间)。
  2. 您尝试连接的实体JOIN FETCH可能通过涉及列表的多个“多”关系链接。在这种情况下,生成的查询返回不明确的结果,Hibernate 将拒绝在单个查询中获取您的数据。
  3. 已经有一个与 AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS 相关的有趣错误。而且还会有更多,因为正如冬眠者所说:注意:这可能发生在事务之外并且不安全。谨慎使用。你主要是靠自己。

最好的方法是先尝试JOIN FETCH。如果这不起作用,请尝试 getter 方法。如果 JIT 编译器在运行时搞砸了,请将结果分配给public static volatile Object.

或者停止使用休眠...

于 2009-05-10T11:45:14.033 回答
16

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

于 2012-09-06T21:20:58.960 回答
8

LazyInitializationException 意味着您在休眠会话关闭后或对象与会话分离后调用集合。

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

于 2009-03-23T02:53:57.740 回答
6

解决 LazyInitializationException 的最佳方法是在实体查询中使用 JOIN FETCH 指令。

FetchType.EAGER 加载对性能不利。此外,还有一些反模式,例如:

你永远不应该使用它,因为它们要么需要为 UI 渲染打开数据库连接(在视图中打开会话),要么对于在初始持久性上下文 ( hibernate.enable_lazy_load_no_trans) 之外获取的每个惰性关联都需要一个数据库连接。

有时,您甚至不需要实体,而 DTO 投影甚至更好。仅在需要修改实体时才应获取实体。对于只读事务,DTO 预测更好

于 2016-09-27T11:13:03.993 回答
5

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

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

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

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

于 2009-02-23T18:19:48.577 回答
4

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

于 2011-11-02T12:39:14.683 回答
1

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

于 2014-10-26T21:33:15.057 回答
0

我正在开发一个项目,旨在解决使用 ModelMapper 将实体映射到 DTO 时常见的 JPA 问题。这个问题已经在项目中解决了。项目链接:JPA 模型映射器

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

资料来源:JPA 模型映射器

因此,在项目中,我们通过为所有未加载的实体设置 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 模型映射器

于 2018-04-17T19:07:27.000 回答