这是让我感到困惑的一个。我正在尝试实现一个基本的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)
答案 0 :(得分:68)
问题是您正在尝试访问detached对象中的集合。在将集合访问到当前会话之前,需要重新附加对象。你可以通过
来做到这一点session.update(object);
使用lazy=false
不是一个好的解决方案,因为你丢弃了hibernate的Lazy Initialization功能。当lazy=false
时,集合在请求对象的同时加载到内存中。这意味着如果我们有一个包含1000个项目的集合,它们都将被加载到内存中,尽管我们将要访问它们。这不好。
请阅读此article,其中解释了问题,可能的解决方案以及为何以这种方式实施。另外,要了解会话和交易,您必须阅读this other article。
答案 1 :(得分:14)
这通常意味着拥有的Hibernate会话已经关闭。您可以执行以下操作之一来修复它:
HibernateTemplate.initialize(object name)
lazy=false
。答案 2 :(得分:11)
查看我的文章。我有同样的问题--LazyInitializationException - 这是我最终想出的答案:
http://community.jboss.org/wiki/LazyInitializationExceptionovercome
设置lazy = false不是答案 - 它可以一次加载所有内容,而且不一定好。例如:
1记录表A参考文献:
5记录表B参考:
25记录表C参考文献:
125记录表D
...
这只是可能出错的一个例子
--Tim Sabin
答案 3 :(得分:7)
如果您正在使用带有JPA注释的hibernate,那么这将非常有用。在您的服务类中,应该有一个带有@PersistenceContext的实体管理器的setter。将其更改为@PersistenceContext(type = PersistenceContextType.EXTENDED)。然后你可以在任何地方访问懒惰的财产。
答案 4 :(得分:4)
如果您使用延迟加载,则必须使用
注释您的方法 无状态会话EJB的 @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
答案 5 :(得分:3)
我们也遇到了这个错误。我们为解决这个问题所做的是在Hibernate映射文件中添加了 lazy = false 。
看来我们有一个A类,它在一个Session里面加载另一个B类。我们试图访问B类的数据,但这个B类与会话分离。
为了让我们访问这个B类,我们必须在A类的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>
答案 6 :(得分:2)
好的,终于找到了我的疏忽。我错误地认为我应该在事务中包装每个DAO方法。非常错!我已经吸取了教训。我从所有DAO方法中拖出了所有事务代码,并在应用程序/管理器层严格设置了事务。这完全解决了我所有的问题。正如我需要的那样,数据被正确地延迟加载,一旦我提交就被包装并关闭。
生活很好......:)
答案 7 :(得分:2)
如果您了解lazy=false
的影响并仍希望将其设为默认值(例如,用于原型设计),则可以使用以下任何一种方法:
default-lazy="false"
添加到<hibernate-mapping>
元素@Proxy(lazy=false)
添加到您的实体类答案 8 :(得分:2)
似乎只有你的DAO正在使用会话。因此,对于每次调用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() )
正在抛出异常,因为它从数据库请求新加载,并且与实体加载相关联的会话已经关闭。
有两种方法可以解决此问题:
创建一个会话以包含我们所有的代码。因此,这意味着要更改您的DAO内容以避免开第二个会话
创建一个会话,然后在断言之前将您的实体更新(即重新连接)到此会话。
了Session.update(对象);
答案 9 :(得分:1)
如果您手动管理Hibernate会话,您可能需要在此处查看sessionFactory.getCurrentSession()及相关文档:
http://www.hibernate.org/hib_docs/v3/reference/en/html/architecture-current-session.html
答案 10 :(得分:1)
答案 11 :(得分:1)
将Hibernate.initialize用于惰性字段
答案 12 :(得分:1)
如果您使用Spring和JPA注释,在惰性初始化中避免会话问题的最简单方法是重放:
@PersistenceContext
到
@PersistenceContext(type = PersistenceContextType.EXTENDED)
答案 13 :(得分:0)
默认情况下,首次访问时,所有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;
}
});
另一种选择是在加载实体时获取所有LAZY关联,以便:
SecurityFiling sf2 = sfdao.read( sf.getId() );
也应该获取LAZY submissionType
:
select sf
from SecurityFiling sf
left join fetch.sf.submissionType
这样,您急切地获取所有延迟属性,并且您也可以在会话关闭后访问它们。
您可以获取尽可能多的[one|many]-to-one
个关联和一个&#34; [one | many] -to-many&#34;列表关联(因为运行笛卡尔积)。
要初始化多个&#34; [one | many] -to-many&#34;,您应该在加载根实体后立即使用Hibernate.initialize(collection)。