Hibernate org.hibernate.LazyInitializationException:懒得初始化角色集合:

时间:2013-11-23 12:06:01

标签: java spring hibernate jpa jersey

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

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

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

3 个答案:

答案 0 :(得分:9)

我已在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>

礼貌herehere

由于

答案 1 :(得分:5)

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

  1. Web服务器将请求分派给 JAX-RS服务
  2. JAX-RS服务调用EJB无状态会话Bean
  3. 交易开始
  4. EJB无状态会话Bean 从数据库加载数据(可能涉及其他bean)
  5. EJB无状态会话Bean 返回结果
  6. 交易结束
  7. JAX-RS服务返回结果
  8. JAX-RS Producer List<Emp>创建XML并访问字段empDeptno
  9. 因此,当Jersey获取Emp的列表以生成XML时,该事务已被关闭。当现在导航empDeptNo字段时,JPA会尝试延迟加载它,因为我们已经在有效的事务/会话之外而失败了。

    您可以尝试通过使用无状态会话bean来扩展事务范围以包含Jersey REST资源bean。然后它可能如下:

    1. Web服务器将请求分派给 JAX-RS服务
    2. 交易开始
    3. JAX-RS服务调用EJB无状态会话Bean
    4. EJB无状态会话Bean 从数据库加载数据(可能涉及其他bean)
    5. EJB无状态会话Bean 返回结果
    6. JAX-RS服务返回结果
    7. JAX-RS Producer List<Emp>创建XML并访问字段empDeptno
    8. 交易结束
    9. 我不是百分百肯定,也可能是步骤8之前的第8步,所以在生产者完成工作之前,交易可能会被关闭。如果是这种情况,这个解决方案就是错误的......

      但我认为你应该只是尝试一下......

答案 2 :(得分:2)

如果您想继续使用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 join。您的Emp.findAllEmployees查询可能如下所示:

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

如果你没有没有empDetNo

的部门和部门的Emps,请将其设为左连接