ChatGPT解决这个技术问题 Extra ChatGPT

休眠: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)

p
pakore

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

session.update(object);

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

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


d
digitalsanctum

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

无论哪个对象创建了这个问题,使用 HibernateTemplate.initialize(object name) 在你的 hbm 文件中使用lazy=false。


有同样的问题,lazy=false 修复了它。谢谢
现在在我的情况下,我将 lazy=false 用于所有 dao 级别,但事实证明应用程序性能因此而变慢,尝试设置 lazy=true 但现在抛出了lazyException,任何关于如何解决此问题的建议。
pakore,你能指出为什么不是解决方案以及如何理解它吗?
@Victor lazy=false 与渴望相同。当我们选择使用急切加载关联时,每次加载实体时,所有“急切关联”都会被加载,即使我们不要求或不使用它。
T
Tim Sabin

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


您应该在这里解释解决方案,而不是链接到第三方网站。
R
Rohit Gaikwad

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


除非您手动管理交易,否则这是不正确的。 Spring EXTENDED 持久性上下文类型适用于长对话模式,而不是 OP 询问的会话每个请求模式。
C
Curtis

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

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


c
chris

我们也遇到了这个错误。我们为解决这个问题所做的就是在 Hibernate 映射文件中添加了一个lazy=false。

看来我们有一个在加载另一个类 B 的 Session 中的类 A。我们正在尝试访问类 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>  

P
Piko

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

生活是美好的... :)


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

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

如果您使用 XML 配置:将 default-lazy="false" 添加到您的 元素

如果您使用注释配置:将 @Proxy(lazy=false) 添加到您的实体类


K
Kartoch

似乎只有您的 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(对象);


V
Vlad Mihalcea

默认情况下,所有 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 关联和一个“[one|many]-to-many”列表关联(因为运行笛卡尔积)。

要初始化多个“[one|many]-to-many”,您应该在加载根实体后立即使用 Hibernate.initialize(collection)


c
cliff.meyers

如果您手动管理 Hibernate 会话,您可能需要在此处查看 sessionFactory.getCurrentSession() 和相关文档:

http://www.hibernate.org/hib_docs/v3/reference/en/html/architecture-current-session.html


A
Alexander Fenske

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

`身份证...'

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

我希望这有帮助。


m
myururdurmaz

对惰性字段使用 Hibernate.initialize


D
DmRomantsov

如果您使用 Spring 和 JPA 注释,避免在延迟初始化中出现会话问题的最简单方法是重放:

@PersistenceContext   

@PersistenceContext(type = PersistenceContextType.EXTENDED)

这仅在您手动管理交易时才有效