75

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

14 回答 14

69

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

session.update(object);

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

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

于 2010-09-03T12:15:43.520 回答
15

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

  1. 无论哪个对象产生这个问题,使用HibernateTemplate.initialize(object name)
  2. lazy=false在您的 hbm 文件中使用。
于 2008-12-06T14:51:40.157 回答
10

See my article. I had the same problem - LazyInitializationException - and here's the answer I finally came up with:
http://community.jboss.org/wiki/LazyInitializationExceptionovercome
Setting lazy=false is not the answer - it can load everything all at once, and that's not necessarily good. Example:
1 record table A references:
5 records table B references:
25 records table C references:
125 records table D
...
etc. This is but one example of what can go wrong.
--Tim Sabin

于 2011-02-16T20:05:51.333 回答
7

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

于 2010-09-26T13:50:21.107 回答
4

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

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

于 2010-08-16T00:17:34.867 回答
3

We encountered this error as well. What we did to solve the issue is we added a lazy=false in the Hibernate mapping file.

It appears we had a class A that's inside a Session that loads another class B. We are trying to access the data on class B but this class B is detached from the session.

In order for us to access this Class B, we had to specify in the class A's Hibernate mapping file the lazy=false attribute. For example,

     <many-to-one name="classA" 
                 class="classB"
                 lazy="false">
        <column name="classb_id"
                sql-type="bigint(10)" 
                not-null="true"/>
    </many-to-one>  
于 2010-09-03T08:39:00.917 回答
2

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

生活是美好的... :)

于 2009-01-07T23:17:42.490 回答
2

默认情况下,第一次访问时会延迟获取所有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

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

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

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

于 2015-03-12T07:36:27.410 回答
2

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

  • 如果您使用 XML 配置:添加default-lazy="false"到您的<hibernate-mapping>元素
  • 如果您使用注释配置:添加@Proxy(lazy=false)到您的实体类
于 2010-11-04T02:08:02.340 回答
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(对象);

于 2012-05-03T11:39:40.900 回答
1

对惰性字段使用 Hibernate.initialize

于 2011-08-03T14:15:16.637 回答
1

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

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

于 2008-12-09T02:17:15.903 回答
1

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

`身份证...'

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

我希望这有帮助。

于 2010-07-26T13:07:50.523 回答
1

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

@PersistenceContext   

@PersistenceContext(type = PersistenceContextType.EXTENDED)
于 2015-06-19T11:56:01.703 回答