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 Producer 从
List<Emp>
创建 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 Producer 从
List<Emp>
创建 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的区别