Hibernate org.hibernate.LazyInitializationException:未能延迟初始化角色集合:

Posted

技术标签:

【中文标题】Hibernate org.hibernate.LazyInitializationException:未能延迟初始化角色集合:【英文标题】:Hibernate org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: 【发布时间】:2013-12-08 08:23:21 【问题描述】:

我有下面提到的实体类,当我执行我的应用程序时,我得到了以下异常。其他一些类似的问题并没有解决问题。

WARNING: StandardWrapperValve[jersey-serlvet]: PWC1406: Servlet.service()
for servlet jersey-serlvet threw exception
org.hibernate.LazyInitializationException: failed to lazily initialize 
a collection of role: test.entity.Dept.empDeptno, no session
or session was closed
at org.hibernate.collection.internal.AbstractPersistentCollection.
throwLazyInitializationException(AbstractPersistentCollection.java:393)
       at    org.hibernate.collection.internal.AbstractPersistentCollection.
throwLazyInitializationExceptionIfNotConnected
(AbstractPersistentCollection.java:385)
    at org.hibernate.collection.internal.AbstractPersistentCollection.
initialize(AbstractPersistentCollection.java:378) 

我该如何解决这个问题?

Emp 实体

@Entity
@Table(name = "EMP", schema = "SCOTT"
)
@XmlRootElement
@NamedQueries(
    @NamedQuery(name = "Emp.findAllEmployees", query = "select e from Emp e left 
    join fetch e.deptNo order by e.empno desc")
)
public class Emp implements java.io.Serializable 
@Id
@Column(name = "EMPNO", unique = true, nullable = false, precision = 4,
scale = 0)
private short empno;
@ManyToOne
@JoinColumn(name = "DEPTNO", referencedColumnName = "DEPTNO")
private Dept deptNo;

部门实体

@Entity
@Table(name = "DEPT", schema = "SCOTT"
)
@XmlRootElement
public class Dept implements java.io.Serializable 
@Id
@Column(name = "DEPTNO", unique = true, nullable = false, precision = 2,
scale = 0)
private short deptno;
@OneToMany(fetch=FetchType.LAZY,mappedBy = "deptNo")
private Set<Emp> empDeptno;

DAOImpl

@Override
public List<Emp> findAllEmployees() 
  return getEntityManager().createNamedQuery("Emp.findAllEmployees",
 Emp.class).getResultList();

泽西 RESTful 服务

 @Component
 @Path("/employee")
 public class EmployeeRestService 

 @Autowired
 EmployeeService employeeService;

 @GET
 @Produces(MediaType.APPLICATION_JSON)
 public List<Emp> getEmployees() 
 List<Emp> emp = new ArrayList<Emp>();
 emp.addAll(getEmployeeService().findAllEmployees());
 return emp;
 

Spring applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd"
>
    <!-- Data Source Declaration -->    
    <bean id="DataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="jdbc/scottDS"/>   
    </bean>

    <context:component-scan base-package="net.test" />
    <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
    <bean id="entityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="DataSource" />
        <property name="packagesToScan" value="net.test" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="false" />
                <property name="generateDdl" value="false" />
                <property name="databasePlatform" value="$jdbc.dialectClass" />
            </bean>
        </property>
    </bean>
    <bean id="defaultLobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" />  
    <!-- Transaction Config -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager"/>          
    <context:annotation-config/>
    <bean id="hibernateStatisticsMBean" class="org.hibernate.jmx.StatisticsService">
        <property name="statisticsEnabled" value="true" />
        <property name="sessionFactory" value="#entityManagerFactory.sessionFactory" />
    </bean>
</beans>

【问题讨论】:

您是否尝试过在会话仍然可用时访问该集合?你可能仍然会收到一个代理对象,所以对它调用一个简单的操作,比如size() @kostja 我正在从 RESTful 服务调用方法,我通过编辑我的问题发布了我的 RESTful 服务和 DAOImpl 代码 sn-p。 您可能希望将empDetno 字段的FetchType 更改为EAGER。一个不太干净的解决方案是通过访问findAllEmployees 方法来触发加载empDet 集。 @kostja 如果我想将LAZY 用作fetchType 怎么办?对于第二种解决方案,您能举个例子吗?谢谢 也许这是***.com/questions/5990005/…的副本 【参考方案1】:

如果您想继续使用FetchType.LAZY,但需要访问延迟加载的属性以进行某些查询,可移植的解决方案是在事务/会话中访问该字段并对其执行操作。我提到可移植性是因为 AFAIK Hibernate 至少提供了一种不同的方法来显式触发加载,这不是 JPA 规范的一部分。

调整您的代码,这可能如下所示:

public List<Emp> findAllEmployees() 
  List<Emp> employees = getEntityManager().createNamedQuery("Emp.findAllEmployees",
    Emp.class).getResultList();

  //trigger loading of attributes
  for(Emp emp: employees)
    emp.getDeptNo().getEmpDetNo().size();
  
  return employees;

编辑:另一个可移植的替代方法是在查询中使用 fetch 连接。您的 Emp.findAllEmployees 查询可能如下所示:

SELECT e FROM Emp e JOIN FETCH e.dept.empDetno

如果您有没有部门的 Emps 和没有没有 empDetNo 的部门,请将其设为左联接

【讨论】:

我有一个问题,empDeptno 在 Dept Entity 中,而不是在 Emp Entity 中,第二个备选方案我已经为我的查询设置了 fetch join。 @Polppan 对,对不起,读这个问题太仓促了。我认为您可以通过导航扩展获取连接。已相应地编辑了答案。像 isnot2bad 建议的 OTOH 优化事务分界可能是更好的方法。【参考方案2】:

我已经通过在 web.xml 中添加以下内容解决了这个问题

<filter>
<filter-name>OpenEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OpenEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

感谢here 和here

谢谢

【讨论】:

【参考方案3】:

问题是您的数据库/JPA 事务的范围只包含服务(我假设它是一个无状态会话 bean)并且不包含 REST 资源 bean。

    Web 服务器 将请求分派给 JAX-RS 服务 JAX-RS 服务调用 EJB Stateless Session Bean 交易开始 EJB 无状态会话 Bean 从数据库加载数据(可能涉及其他 bean) EJB 无状态会话 Bean 返回结果 交易结束 JAX-RS 服务返回结果 JAX-RS ProducerList&lt;Emp&gt; 创建 XML 并访问字段 empDeptno

所以当 Jersey 获得 Emp 的列表并从中生成 XML 时,事务已经关闭。现在导航字段empDeptNo 时,JPA 尝试延迟加载它,但由于我们已经在有效的事务/会话之外,因此失败。

您可能会尝试通过创建无状态会话 bean 来扩展事务范围以包含您的 Jersey REST 资源 bean。那么可能是这样的:

    Web 服务器 将请求分派给 JAX-RS 服务 交易开始 JAX-RS 服务调用 EJB Stateless Session Bean EJB 无状态会话 Bean 从数据库加载数据(可能涉及其他 bean) EJB 无状态会话 Bean 返回结果 JAX-RS 服务返回结果 JAX-RS ProducerList&lt;Emp&gt; 创建 XML 并访问字段 empDeptno交易结束

我不能 100% 确定,也可能是第 8 步出现在第 7 步之前,因此交易可能在生产者完成其工作之前关闭。如果是这样的话,这个解决方案简直是错误的......

但我认为你应该简单地尝试一下......

【讨论】:

你的意思是这样的? blog.bdoughan.com/2010/08/… 顺便说一下我用的是Spring 没错!但是他们不使用延迟加载,所以(对我来说)仍然不清楚它是否有效,因为序列化在事务范围内,或者因为所有要序列化的数据都已经可用。也许有人可以澄清这一点! 我尝试在 Rest Controller 类中添加@Transaction,但也没有用。 @Polppan 我不太了解spring,也许这会有所作为。但主要原则应该是相同的。这是交易范围的问题。

以上是关于Hibernate org.hibernate.LazyInitializationException:未能延迟初始化角色集合:的主要内容,如果未能解决你的问题,请参考以下文章

Spring和Hibernate的注解整合 hibernate3和hibernate4/5的区别

hibernate.merge()方法怎么用

hibernate 异常 怎么解决

Hibernate之Hibernate环境配置

(转)Hibernate框架基础——Hibernate API及Hibernate主配置文件

Hibernate基础学习—Hibernate相关API介绍