【问题标题】:hibernate: LazyInitializationException: could not initialize proxy休眠:LazyInitializationException:无法初始化代理
【发布时间】:2010-09-25 15:11:43
【问题描述】:

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

【问题讨论】:

    标签: hibernate initialization lazy-evaluation


    【解决方案1】:

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

    session.update(object);
    

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

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

    【讨论】:

      【解决方案2】:

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

      1. 无论哪个对象产生这个问题,使用HibernateTemplate.initialize(object name)
      2. 在您的 hbm 文件中使用 lazy=false

      【讨论】:

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

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

      【讨论】:

      • 你应该在这里解释解决方案,而不是链接到第三方网站..
      【解决方案4】:

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

      【讨论】:

      【解决方案5】:

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

      注释

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

      【讨论】:

        【解决方案6】:

        我们也遇到了这个错误。为了解决这个问题,我们在 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>  
        

        【讨论】:

          【解决方案7】:

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

          生活是美好的...... :)

          【讨论】:

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

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

          • 如果您使用 XML 配置:将 default-lazy="false" 添加到您的 &lt;hibernate-mapping&gt; 元素中
          • 如果您使用注解配置:将@Proxy(lazy=false) 添加到您的实体类

          【讨论】:

            【解决方案9】:

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

            【讨论】:

              【解决方案10】:

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

              【讨论】:

                【解决方案11】:

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

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

                【讨论】:

                  【解决方案12】:

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

                  ` ID ...'

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

                  希望对你有所帮助。

                  【讨论】:

                    【解决方案13】:

                    对惰性字段使用 Hibernate.initialize

                    【讨论】:

                      【解决方案14】:

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

                      @PersistenceContext   
                      

                      @PersistenceContext(type = PersistenceContextType.EXTENDED)
                      

                      【讨论】:

                      • 这仅在您手动管理交易时才有效
                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2018-09-28
                      • 2019-06-29
                      • 1970-01-01
                      • 2019-12-11
                      • 1970-01-01
                      • 2013-01-10
                      相关资源
                      最近更新 更多