懒惰的集合初始化在hibernate中失败

Ale*_*exz 8 java collections hibernate lazy-initialization

今天我遇到了hibernate的下一个问题:

我的方法:

@Transactional
public Period getDefault(Team team) {
    Period defaultPeriod = team.getDefaultPeriod();
    List<Period> periods = _periodDAO.getPeriods(team);
        if (!periods.contains(defaultPeriod)) {
            defaultPeriod = periods.get(periods.size() - 1);
        }
    }
    _periodDAO.initializeIssues(defaultPeriod);
    return defaultPeriod;
}
Run Code Online (Sandbox Code Playgroud)

方法initializeIssues:

public void initializeIssues(Period period) {
    if (period.getIssues() != null) {
        Hibernate.initialize(period.getIssues());
    }
}
Run Code Online (Sandbox Code Playgroud)

如果收集包含defaultPeriod,我会收到异常

Caused by: org.hibernate.HibernateException: collection is not associated with any session
at org.hibernate.collection.AbstractPersistentCollection.forceInitialization(AbstractPersistentCollection.java:474)
at org.hibernate.Hibernate.initialize(Hibernate.java:417)
Run Code Online (Sandbox Code Playgroud)

但是,如果我删除一些行并将方法更改为

@Transactional    
public Period getDefault(Team team) {
    Period defaultPeriod = team.getDefaultPeriod();
    _periodDAO.initializeIssues(defaultPeriod);
    return defaultPeriod;
}
Run Code Online (Sandbox Code Playgroud)

它工作正常.

我调试了第一个示例,并且hibernate会话在整个方法期间没有关闭.

据我所知,如果会话中的加载对象(句点中的一个元素)具有与活动会话关联的集合,并且在对象之前存在(defaultPeriod)也具有相同的关联 - 它(defaultPeriod)将失去其关联.

这是真的吗?还有谁遇到过同样的问题?

谢谢你的回答.

Sot*_*lis 13

据推测,你的Team论点来自另一个事务和另一个Hibernate Session.

当一个@Transactional方法返回时,TransactionManager关闭Session它会进行一些清理并取消设置(设置为null)Session所有PersistentCollection实例的字段.你defaultPeriod有其中一个在其issues领域.

Hibernate Hibernate.initialize()强制执行lazy的初始化PersistentCollection,但是有以下代码(调用AbstractPersistentCollection#forceInitialization())

    if ( session == null ) {
        throw new HibernateException( "collection is not associated with any session" );
    }
Run Code Online (Sandbox Code Playgroud)

如果您计划在issues原始@Transactional方法(生成的代码Team)之外使用集合,则需要加载基础对象.将其更改为EAGER加载或执行您正在执行的操作Hibernate.initialize().

另一个解决方案是让Session最后一个长度超过第一个长度@Transactional,但我没有详细说明.快速google或SO搜索应该提出一些选择.


这就是正在发生的事情

Period defaultPeriod = team.getDefaultPeriod();
Run Code Online (Sandbox Code Playgroud)

获取一个Period带id 的对象(例如)42.因为它发生在另一个Session已经被关闭的时候,它issues是一个PersistentCollectionnull Session参考的东西,并且会抛出Exception你得到的东西.

你这样做

List<Period> periods = _periodDAO.getPeriods(team);
Run Code Online (Sandbox Code Playgroud)

假设List包含一个Period带id 的对象42,所以ifin

   if (!periods.contains(defaultPeriod)) {
        defaultPeriod = periods.get(periods.size() - 1);
   }
Run Code Online (Sandbox Code Playgroud)

没有被执行.尽管equals()回报率true(contains()也返回true并成为false!),对象是不一样的.on中List有一个附加(非null)Session,因此可以初始化一个.但你的,defaultPeriod不能举办的那个.

  • 无论在@ Transactional中发生什么,都会发生在一个Hibernate"Session"中,在一个`Transaction`中.当该方法返回时,根据配置,将提交`Transaction`并关闭`Session`.只有在"Session"尚未关闭时才能初始化集合.我认为它不一定必须在同一个`Transaction`中. (2认同)