休眠:LazyInitializationException:无法初始化代理


72

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

它在尝试将sf中的值与sf2中的对应值进行比较的第三个assertTrue上失败。这是例外:

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)

Answers:


68

问题是您试图访问分离对象中的集合。您需要重新附加对象,然后才能将集合访问当前会话。您可以通过

session.update(object);

使用lazy=false不是一个好的解决方案,因为您将放弃休眠的惰性初始化功能。如果为lazy=false,则在请求对象的同时将集合加载到内存中。这意味着,如果我们有一个包含1000个项目的集合,则无论我们是否要访问它们,它们都将被加载到内存中。这不好。

请阅读本文,其中解释了问题,可能的解决方案以及为什么以这种方式实现。另外,要了解会话和事务,您必须阅读另一篇文章


14

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

  1. 使用哪个对象创建此问题,请使用 HibernateTemplate.initialize(object name)
  2. 使用lazy=false您的HBM文件。

遇到相同的问题,并且lazy = false修复了该问题。谢谢
autonomatt

1
现在以我为例,我lazy=false在所有dao级别上都使用了它,但事实证明,由于它,应用程序性能很慢,试图进行设置,lazy=true但现在抛出了lazyException,如何解决此问题的任何建议。
雷切尔2012年

pakore,您能否指出为什么不是解决方案以及如何理解呢?
维克多

2
@Victor lazy = false与渴望相同。当我们选择使用紧急加载关联时,每次加载实体时,即使我们不询问也不使用它,所有“紧急关联”也会加载。
JonathasPacífico13年

10

看我的文章。我遇到了同样的问题-LazyInitializationException-这是我最终想出的答案:
http : //community.jboss.org/wiki/LazyInitializationExceptionovercome
设置lazy = false不是答案-它可以一次加载所有内容,这就是不一定好。示例:
1个记录表A引用:
5个记录表B引用:
25个记录表C引用:
125个记录表D
...
等等。这只是可能出错的一个示例。
蒂姆·萨宾


你应该在这里说明的解决方案,而不是链接到第三方网站..
saimiris_devel

7

如果您将休眠与JPA批注一起使用,则这将很有用。在您的服务类中,应该使用@PersistenceContext为实体管理器设置一个设置器。将其更改为@PersistenceContext(type = PersistenceContextType.EXTENDED)。然后,您可以在任何地方访问惰性属性。


1
除非您手动管理事务,否则这是不正确的。Spring EXTENDED持久性上下文类型用于长会话模式,而不是OP所询问的每个请求会话模式。
HDave 2011年


4

如果您使用的是延迟加载,则必须使用注释您的方法

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 用于无状态会话EJB


3

我们也遇到了此错误。解决此问题的方法是在Hibernate映射文件中添加了lazy = false

看来我们有一个类A在会话中,该类加载另一个类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>  

2

好吧,终于找到了我被解雇的地方。我误以为应该将每个DAO方法包装在一个事务中。太错了!我学到了教训。我已经从所有DAO方法中提取了所有事务代码,并已在应用程序/管理器层严格设置了事务。这完全解决了我所有的问题。数据在我需要时适当地延迟加载,一旦执行提交,就打包并关闭。

生活是美好的... :)


我不确定我是否完全理解,因为我不记得在其他项目中看到过这种情况。但是您是对的:@org.springframework.transaction.annotation.Transactional(readOnly=true)在服务层中添加方法可以解决此问题。(在该层中,我们正在获取一个实体并将其传递给DAO的另一个调用。)
Arjan

2

如果您知道的影响lazy=false并且仍然希望将其设为默认值(例如,出于原型目的),则可以使用以下任何一种方法:

  • 如果您使用XML配置:添加default-lazy="false"到您的<hibernate-mapping>元素
  • 如果您使用注释配置:添加@Proxy(lazy=false)到您的实体类

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(object);


2

默认情况下,所有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

这样,您就可以急切地获取所有惰性属性,并且在Session关闭后也可以访问它们。

您可以获取尽可能多的[one|many]-to-one关联和一个“ [[多|对]”列表关联(由于运行笛卡尔乘积)。

要初始化多个“一对多”,应在加载根实体后立即使用Hibernate.initialize(collection)



1

我认为Piko在他的回应中表示存在hbm文件。我有一个名为Tax.java的文件。映射信息保存在hbm(=休眠映射)文件中。在class标记中,有一个名为lazy的属性。将该属性设置为true。以下hbm示例显示了一种将lazy属性设置为false的方法

`id ...'

如果您使用的是注解,请查看休眠文档。 http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/

希望对您有所帮助。



1

如果您使用Spring和JPA批注,则可以避免在惰性初始化中避免会话问题的最简单方法:

@PersistenceContext   

@PersistenceContext(type = PersistenceContextType.EXTENDED)

1
这仅在您手动管理交易时有效
Gemasoft 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.