Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lazy collection initialization fails in hibernate

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.

like image 730
Alexz Avatar asked Sep 10 '13 16:09

Alexz


People also ask

How do I fix lazy initialization exception?

The right way to fix a LazyInitializationException is to fetch all required associations within your service layer. The best option for that is to load the entity with all required associations in one query.

What is lazy initialization in hibernate?

LazyInitializer is a Hibernate component that helps to generate proxy objects for Entities and can also be used to fetch the underlying entities of these proxy objects.

What is lazy initialization exception?

Class LazyInitializationExceptionIndicates an attempt to access not-yet-fetched data outside of a session context. For example, when an uninitialized proxy or collection is accessed after the session was closed.


1 Answers

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.

like image 170
Sotirios Delimanolis Avatar answered Sep 24 '22 03:09

Sotirios Delimanolis