Friday, December 10, 2010

LazyInitializationException and Hibernate

If you ever really used Hibernate in a web project sooner or later you'll see one of this nasty LazyInitializationException telling you that you try to access an uninitialized collection or that the session is closed.
(The documentation says: Indicates access to unfetched data outside of a session context. For example, when an uninitialized proxy or collection is accessed after the session was closed.)


Normally this means that the program tries to follow a 1:n (or m:n) relationship which was decleared to be fetched LAZY as it is needed. 
A very simple example for such a declaration is stated below.


@Entity
@Table(name = "person")
public class Person {
[..]
@OneToMany(targetEntity = Address.class, mappedBy = "person", fetch = FetchType.LAZY)
private List addresses;
[..]
}


A very simple solution to this problem would be to just check if it is possible to follow the relationship without exception. If not, then reopen the session to Hibernate and reconnect the object to the database. Once reconnected all the needed data can be fetched and the object can be detached and sent to the client.

However, this would have to be performed prior to every single "get*" call on every entity in your domain model (Except the once that return non-entity types like String, int or long) which would mean of course a lot of code in each getter to do always the same or at least similar stuff.

At this point a programmer has to look for a different solution and it is the point where aspect oriented programming comes in handy. An aspect could be put around every getter in your domain model to dynamically check for the presence of the Hibernate session and just in case reopen it and fetch the needed contents.

Basically it should look like the following pseudo-code (adopted from a Java solution):

@Pointcut("execution(public * path.to.model..*.get*(..))")
public void possibleLazyInitializationException() {
}

@Around(value = "possibleLazyInitializationException()")
public Object preloadOnDemand(ProceedingJoinPoint pjp) {
  result= pjp.getTheRequestedAttributeReference;
  if (result == Collection,Map,BaseDomainObject && result.isNotInitialized && result.getSession == null){
    reopenHibernateSession;
    reload(pjp.getTargetObject);
    initializeTheResult;
    useItsSetterToPutItOnPlace;
  }
}

After this Aspect the normal getter method will find the object in place, so it can be called using the getter as usual.
(I planned to post now my own solution to it, however it is very specific to my setup, so I decided to put it in a dedicated blog entry)

No comments: