Java 休眠中的延迟集合初始化失败

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18724797/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 10:30:26  来源:igfitidea点击:

Lazy collection initialization fails in hibernate

javahibernatecollectionslazy-initialization

提问by Alexz

Today I faced with next problem in hibernate:

今天我在休眠中面临下一个问题:

My method:

我的方法:

@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;
}

Method initializeIssues:

方法初始化问题:

public void initializeIssues(Period period) {
    if (period.getIssues() != null) {
        Hibernate.initialize(period.getIssues());
    }
}

I receive exception if collection periodscontains defaultPeriod

如果收集包含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)

But if I remove some lines and change method to

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

@Transactional    
public Period getDefault(Team team) {
    Period defaultPeriod = team.getDefaultPeriod();
    _periodDAO.initializeIssues(defaultPeriod);
    return defaultPeriod;
}

It works fine.

它工作正常。

I debugged first example and hibernate session does not close during whole method.

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

As I understand, if loaded object (one element in periods) in session has collection which associated with active session and existing before object (defaultPeriod) also has same association - it (defaultPeriod) will lose its association.

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

Is it truth? Who else faced with same problem?

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

Thank you for answers.

谢谢你的回答。

回答by Sotirios Delimanolis

Presumably, your Teamargument is coming from another transaction and another Hibernate Session.

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

When a @Transactionalmethod returns, the TransactionManagercloses the Sessionwhich does some cleanup and unsets (sets to null) the Sessionfield of all PersistentCollectioninstances. Your defaultPeriodhas one of these in its issuesfield.

当一个@Transactional方法返回时,TransactionManager关闭 进行Session一些清理并取消设置(设置为nullSession所有PersistentCollection实例的字段。您defaultPeriod在其issues领域拥有其中之一。

Hibernate's Hibernate.initialize()forces the initialization of a lazy PersistentCollection, but has the following code (calls AbstractPersistentCollection#forceInitialization())

HibernateHibernate.initialize()强制初始化一个 lazy PersistentCollection,但具有以下代码(调用AbstractPersistentCollection#forceInitialization()

    if ( session == null ) {
        throw new HibernateException( "collection is not associated with any session" );
    }

If you are planning on using the issuescollection outside the original @Transactionalmethod (the code that produces Team), you need to load the underlying objects. Either change it to EAGER loading or do what you are doing with Hibernate.initialize().

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

Another solution is to make the Sessionlast longer than just the length of the first @Transactional, but I don't have details for that. A quick google or SO search should bring up some options.

另一种解决方案是使Sessionlast 比第一个的长度更长@Transactional,但我没有详细信息。快速 google 或 SO 搜索应该会提供一些选项。



This is what is happening

这就是正在发生的事情

Period defaultPeriod = team.getDefaultPeriod();

gets a Periodobject with id (ex.) 42. Because it happened in another Sessionthat has since been closed, the issuesis a PersistentCollectionwhich has a nullSessionreference, and will throw the Exceptionyou get.

获取一个Period带有 id (ex.)的对象42。因为它在另一个碰巧Session的是已被关闭,issues是一个PersistentCollection具有nullSession参考,并抛出Exception你。

The you do this

你这样做

List<Period> periods = _periodDAO.getPeriods(team);

Let's say the Listcontains a Periodobject with id 42, so the ifin

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

   if (!periods.contains(defaultPeriod)) {
        defaultPeriod = periods.get(periods.size() - 1);
   }

doesn't get executed. Although the equals()returns true(contains()also returns trueand becomes falsebecause of !), the objects are not the same. The on in the Listhas an attached (non-null) Session, so that one can be initialized. But yours, the one held by defaultPeriodcannot.

不会被执行。虽然equals()返回truecontains()也返回true并成为false因为!),但对象并不相同。中的 onList有一个附加的 (non-null) Session,以便可以初始化。但你的,持有的defaultPeriod不能。