Hibernate org.hibernate。LazyInitializationException异常:未能延迟初始化一个角色集合:

时间:2022-12-21 17:29:14

I have the below mentioned Entity classes, when I execute my application I am getting the following exception. Some of the other similar questions didn't solve the problem.

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

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) 

How can I solve this issue?

我该如何解决这个问题?

Emp Entity

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;

Dept Entity

部门实体

@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

DAOImpl

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

Jersey RESTful service

泽rest式服务

 @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 个解决方案

#1


9  

I have resolved the issue by adding the following in web.xml

通过在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>

courtesy here and here

这里和这里的礼貌

Thanks

谢谢

#2


5  

The problem is that the scope of your database/JPA transaction only contains the service (which I assume is a stateless session bean) and does NOT contain the REST resource bean.

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

  1. Web server dispatches request to JAX-RS service
  2. Web服务器发送请求到JAX-RS服务。
  3. JAX-RS service calls EJB Stateless Session Bean
  4. JAX-RS服务调用EJB无状态会话Bean。
  5. Transaction starts
  6. 事务开始
  7. EJB Stateless Session Bean loads data from database (other beans might be involved)
  8. EJB无状态会话Bean从数据库加载数据(可能涉及其他Bean)
  9. EJB Stateless Session Bean returns result
  10. EJB无状态会话Bean返回结果。
  11. Transaction ends
  12. 事务结束
  13. JAX-RS service returns result
  14. jax - rs服务返回结果
  15. JAX-RS Producer creates XML out of List<Emp> and accesses field empDeptno.
  16. JAX-RS生成器从List 中创建XML,并访问字段empDeptno。

So when Jersey gets the list of Emp to produce XML out of it, the transaction has already been closed. When now the field empDeptNo is navigated, JPA tries to lazily load it, which fails as we're already outside a valid transaction/session.

因此,当Jersey获得Emp的列表来生成XML时,事务就已经被关闭了。当现在字段empDeptNo被导航时,JPA尝试延迟加载它,当我们已经在一个有效的事务/会话之外时,它会失败。

You might try to extend the transaction scope to also contain your Jersey REST resource bean by making a stateless session bean out of it. Then it might be as follows:

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

  1. Web server dispatches request to JAX-RS service
  2. Web服务器发送请求到JAX-RS服务。
  3. Transaction starts
  4. 事务开始
  5. JAX-RS service calls EJB Stateless Session Bean
  6. JAX-RS服务调用EJB无状态会话Bean。
  7. EJB Stateless Session Bean loads data from database (other beans might be involved)
  8. EJB无状态会话Bean从数据库加载数据(可能涉及其他Bean)
  9. EJB Stateless Session Bean returns result
  10. EJB无状态会话Bean返回结果。
  11. JAX-RS service returns result
  12. jax - rs服务返回结果
  13. JAX-RS Producer creates XML out of List<Emp> and accesses field empDeptno.
  14. JAX-RS生成器从List 中创建XML,并访问字段empDeptno。
  15. Transaction ends
  16. 事务结束

I'm not 100% sure, it might also be that step 8 comes BEFORE step 7, so the transaction might be closed before the producer does its job. If that's the case, this solution is simply wrong...

我不是100%肯定,也可能是步骤8出现在第7步之前,所以在生产者完成它的工作之前,交易可能会被关闭。如果是这样,那么这个解决方案就是错误的……

But I think you should simply try it...

但我认为你应该尝试一下……

#3


2  

If you would like to continue using FetchType.LAZY but need access to the lazily loaded attributes for some queries, a portable solution would be to access the field and perform an operation on it while still within a transaction/session. I mention portability because AFAIK Hibernate offers at least one different approach to explicitly trigger loading that is not part of the JPA spec.

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

Adapting your code, this could look like this:

调整您的代码,这可能是这样的:

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

EDIT: Another portable alternative would be to use fetch joins in the query. Your Emp.findAllEmployees query could look like this:

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

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

Make it a left join if you have Emps without departments and departments without empDetNo

如果你没有任何部门和部门的Emps,就把它变成一个左连接。

#1


9  

I have resolved the issue by adding the following in web.xml

通过在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>

courtesy here and here

这里和这里的礼貌

Thanks

谢谢

#2


5  

The problem is that the scope of your database/JPA transaction only contains the service (which I assume is a stateless session bean) and does NOT contain the REST resource bean.

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

  1. Web server dispatches request to JAX-RS service
  2. Web服务器发送请求到JAX-RS服务。
  3. JAX-RS service calls EJB Stateless Session Bean
  4. JAX-RS服务调用EJB无状态会话Bean。
  5. Transaction starts
  6. 事务开始
  7. EJB Stateless Session Bean loads data from database (other beans might be involved)
  8. EJB无状态会话Bean从数据库加载数据(可能涉及其他Bean)
  9. EJB Stateless Session Bean returns result
  10. EJB无状态会话Bean返回结果。
  11. Transaction ends
  12. 事务结束
  13. JAX-RS service returns result
  14. jax - rs服务返回结果
  15. JAX-RS Producer creates XML out of List<Emp> and accesses field empDeptno.
  16. JAX-RS生成器从List 中创建XML,并访问字段empDeptno。

So when Jersey gets the list of Emp to produce XML out of it, the transaction has already been closed. When now the field empDeptNo is navigated, JPA tries to lazily load it, which fails as we're already outside a valid transaction/session.

因此,当Jersey获得Emp的列表来生成XML时,事务就已经被关闭了。当现在字段empDeptNo被导航时,JPA尝试延迟加载它,当我们已经在一个有效的事务/会话之外时,它会失败。

You might try to extend the transaction scope to also contain your Jersey REST resource bean by making a stateless session bean out of it. Then it might be as follows:

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

  1. Web server dispatches request to JAX-RS service
  2. Web服务器发送请求到JAX-RS服务。
  3. Transaction starts
  4. 事务开始
  5. JAX-RS service calls EJB Stateless Session Bean
  6. JAX-RS服务调用EJB无状态会话Bean。
  7. EJB Stateless Session Bean loads data from database (other beans might be involved)
  8. EJB无状态会话Bean从数据库加载数据(可能涉及其他Bean)
  9. EJB Stateless Session Bean returns result
  10. EJB无状态会话Bean返回结果。
  11. JAX-RS service returns result
  12. jax - rs服务返回结果
  13. JAX-RS Producer creates XML out of List<Emp> and accesses field empDeptno.
  14. JAX-RS生成器从List 中创建XML,并访问字段empDeptno。
  15. Transaction ends
  16. 事务结束

I'm not 100% sure, it might also be that step 8 comes BEFORE step 7, so the transaction might be closed before the producer does its job. If that's the case, this solution is simply wrong...

我不是100%肯定,也可能是步骤8出现在第7步之前,所以在生产者完成它的工作之前,交易可能会被关闭。如果是这样,那么这个解决方案就是错误的……

But I think you should simply try it...

但我认为你应该尝试一下……

#3


2  

If you would like to continue using FetchType.LAZY but need access to the lazily loaded attributes for some queries, a portable solution would be to access the field and perform an operation on it while still within a transaction/session. I mention portability because AFAIK Hibernate offers at least one different approach to explicitly trigger loading that is not part of the JPA spec.

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

Adapting your code, this could look like this:

调整您的代码,这可能是这样的:

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

EDIT: Another portable alternative would be to use fetch joins in the query. Your Emp.findAllEmployees query could look like this:

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

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

Make it a left join if you have Emps without departments and departments without empDetNo

如果你没有任何部门和部门的Emps,就把它变成一个左连接。