hibernate:LazyInitializationException:无法初始化代理
这是让我困惑的一个。 我试图实现一个基本的Hibernate DAO结构,但是有一个问题。
这是基本的代码:
int startingCount = sfdao.count(); sfdao.create( sf ); SecurityFiling sf2 = sfdao.read( sf.getId() ); sfdao.delete( sf ); int endingCount = sfdao.count(); assertTrue( startingCount == endingCount ); assertTrue( sf.getId().longValue() == sf2.getId().longValue() ); assertTrue( sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() ) ); assertTrue( sf.getSfTransactionNumber().equals( sf2.getSfTransactionNumber() ) );
它在第三个assertTrue失败,它试图将sf中的值与sf2中的相应值进行比较。 这是一个例外:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:86) at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:140) at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190) at com.freightgate.domain.SecurityFiling_$$_javassist_7.getSfSubmissionType(SecurityFiling_$$_javassist_7.java) at com.freightgate.dao.SecurityFilingTest.test(SecurityFilingTest.java:73) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:40)
问题是,您正尝试访问分离的对象中的集合。 访问集合到当前会话之前,您需要重新附加对象。 你可以做到这一点
session.update(object);
使用lazy=false
不是一个好的解决scheme,因为你抛弃了hibernate的Lazy Initialization特性。 当lazy=false
,集合在请求对象的同时加载到内存中。 这意味着,如果我们有一个包含1000个项目的集合,尽pipe我们要访问它们,但它们全都会被加载到内存中。 这不好。
请阅读这篇文章解释问题,可能的解决scheme,为什么这样实施。 此外,要了解会话和事务,您必须阅读这篇文章 。
这通常意味着拥有Hibernate会话已经closures。 您可以执行以下任一操作来修复它:
- 无论哪个对象创build这个问题,都使用
HibernateTemplate.initialize(object name)
- 在你的HBM文件中使用
lazy=false
。
看我的文章。 我有同样的问题 – LazyInitializationException – 这是我终于想出的答案:
http://community.jboss.org/wiki/LazyInitializationExceptionovercome
设置lazy = false并不是答案 – 它可以一次加载所有的东西,这不一定好。 例:
1个logging表参考文献:
5logging表B参考:
25条logging表C引用:
125logging表D
…
这只是可能出错的一个例子。
– 萨宾
如果你使用JPA批注的hibernate,那么这将是有用的。 在你的服务类中,应该有@PersistenceContext的实体pipe理器的setter。 将其更改为@PersistenceContext(type = PersistenceContextType.EXTENDED)。 那么你可以在任何地方访问懒的属性。
如果你正在使用Lazy加载你的方法必须注明
无状态会话EJB的@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
我们也遇到了这个错误。 我们做了什么来解决这个问题是我们在Hibernate映射文件中添加了一个lazy = false 。
看来我们有一个类A,它在一个会话中加载另一个类B.我们试图访问类B上的数据,但是这个类B从会话中分离出来。
为了让我们访问这个B类,我们必须在类的Hibernate映射文件中指定lazy = false属性。 例如,
<many-to-one name="classA" class="classB" lazy="false"> <column name="classb_id" sql-type="bigint(10)" not-null="true"/> </many-to-one>
好吧,终于搞清楚我在哪里失职了。 我错误地认为我应该把每个DAO方法包装在一个事务中。 非常错误! 我已经吸取了教训。 我从所有DAO方法中拖出所有的事务代码,并严格在应用程序/pipe理器层build立事务。 这完全解决了我所有的问题。 数据是适当的惰性加载,因为我需要它,包装和closures一旦我做了提交。
生活是美好的… 🙂
如果您知道lazy=false
的影响,并且仍然希望将其设置为默认值(例如,用于原型devise),则可以使用以下任一项:
- 如果您正在使用XMLconfiguration:将
default-lazy="false"
到您的<hibernate-mapping>
元素 - 如果您使用注释configuration:将
@Proxy(lazy=false)
到您的实体类(es)
看来只有你的DAO正在使用会话。 因此,一个新的会话打开,然后closures每个DAO方法的调用。 因此,程序的执行可以恢复为:
// open a session, get the number of entity and close the session int startingCount = sfdao.count(); // open a session, create a new entity and close the session sfdao.create( sf ); // open a session, read an entity and close the session SecurityFiling sf2 = sfdao.read( sf.getId() ); // open a session, delete an entity and close the session sfdao.delete( sf ); etc...
默认情况下,实体中的收集和关联是懒惰的:它们是从数据库按需加载的。 从而:
sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() )
抛出一个exception,因为它从数据库中请求一个新的加载,并且与实体加载相关的会话已经closures。
有两种方法可以解决这个问题:
-
创build一个会议,以封闭我们所有的代码。 因此,这意味着要改变你的DAO内容,以避免打开第二个会话
-
创build一个会话,然后更新(即重新连接)您的实体本会话之前的断言。
了Session.update(对象);
如果您手动pipe理Hibernate会话,则可能需要查看sessionFactory.getCurrentSession()和相关文档:
http://www.hibernate.org/hib_docs/v3/reference/en/html/architecture-current-session.html
我认为Piko在他的回应中表示有hbm文件。 我有一个名为Tax.java的文件。 映射信息保存在hbm(= hibernate映射)文件中。 在类标签中有一个名为lazy的属性。 将该属性设置为true。 以下hbm示例显示了将lazy属性设置为false的方法 。
`id …'
如果你正在使用注释,而不是看在hibernate文档。 http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/
我希望有所帮助。
使用Hibernate.initialize作为懒惰字段
如果使用Spring和JPA注释,那么避免懒惰初始化中的会话问题的最简单的方法是重放:
@PersistenceContext
至
@PersistenceContext(type = PersistenceContextType.EXTENDED)
默认情况下,所有one-to-many
和many-to-many
关联在第一次被访问时会被延迟地获取。
在你的用例中,你可以通过把所有的DAO操作包装成一个逻辑事务来解决这个问题:
transactionTemplate.execute(new TransactionCallback<Void>() { @Override public Void doInTransaction(TransactionStatus transactionStatus) { int startingCount = sfdao.count(); sfdao.create( sf ); SecurityFiling sf2 = sfdao.read( sf.getId() ); sfdao.delete( sf ); int endingCount = sfdao.count(); assertTrue( startingCount == endingCount ); assertTrue( sf.getId().longValue() == sf2.getId().longValue() ); assertTrue( sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() ) ); assertTrue( sf.getSfTransactionNumber().equals( sf2.getSfTransactionNumber() ) ); return null; } });
另一个select是在加载实体时获取所有LAZY关联,以便:
SecurityFiling sf2 = sfdao.read( sf.getId() );
也应该获取LAZY submissionType
:
select sf from SecurityFiling sf left join fetch.sf.submissionType
通过这种方式,您可以热切地获取所有惰性属性,并且也可以在Sessionclosures后访问它们。
您可以获取尽可能多的[one|many]-to-one
关联和一个“一对多”的关联(由于运行笛卡尔积)。
要初始化多个“[一对|多]”,您应该在加载根实体之后使用Hibernate.initialize(collection) 。