Lazy collection initialization fails in hibernate

Alexz picture Alexz · Sep 10, 2013 · Viewed 19.2k times · Source

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 periods contains 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.

Is it truth? Who else faced with same problem?

Thank you for answers.

Answer

Sotirios Delimanolis picture Sotirios Delimanolis · Sep 10, 2013

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

When a @Transactional method returns, the TransactionManager closes the Session which does some cleanup and unsets (sets to null) the Session field of all PersistentCollection instances. Your defaultPeriod has one of these in its issues field.

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

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

If you are planning on using the issues collection outside the original @Transactional method (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().

Another solution is to make the Session last 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.


This is what is happening

Period defaultPeriod = team.getDefaultPeriod();

gets a Period object with id (ex.) 42. Because it happened in another Session that has since been closed, the issues is a PersistentCollection which has a null Session reference, and will throw the Exception you get.

The you do this

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

Let's say the List contains a Period object with id 42, so the if in

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

doesn't get executed. Although the equals() returns true (contains() also returns true and becomes false because of !), the objects are not the same. The on in the List has an attached (non-null) Session, so that one can be initialized. But yours, the one held by defaultPeriod cannot.