hibernate:LazyInitializationException:无法初始化代理

时间:2008-12-06 01:32:28

标签: hibernate initialization lazy-evaluation

这是让我感到困惑的一个。我正在尝试实现一个基本的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)

14 个答案:

答案 0 :(得分:68)

问题是您正在尝试访问detached对象中的集合。在将集合访问到当前会话之前,需要重新附加对象。你可以通过

来做到这一点
session.update(object);

使用lazy=false不是一个好的解决方案,因为你丢弃了hibernate的Lazy Initialization功能。当lazy=false时,集合在请求对象的同时加载到内存中。这意味着如果我们有一个包含1000个项目的集合,它们都将被加载到内存中,尽管我们将要访问它们。这不好。

请阅读此article,其中解释了问题,可能的解决方案以及为何以这种方式实施。另外,要了解会话和交易,您必须阅读this other article

答案 1 :(得分:14)

这通常意味着拥有的Hibernate会话已经关闭。您可以执行以下操作之一来修复它:

  1. 无论创建此问题的哪个对象,请使用HibernateTemplate.initialize(object name)
  2. 在hbm文件中使用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的影响并仍希望将其设为默认值(例如,用于原型设计),则可以使用以下任何一种方法:

  • 如果您使用的是XML配置:将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-manymany-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)