Simple workaround for Hibernate lazy initialization problem

By | May 5, 2010

Update: this workaround is not a solution, just delaying the problem. I’m currently looking for other possibilities, databinder project looks promising.

I’ve been struggling with the infamous Lazy loading problem – LazyInitializationException and other related exceptions, such as “Could not initialize proxy – the owning Session was closed”. My current project uses Hibernate> ORM framework for persistence, simple Java in the business model layer and Wicket in the web layer. I chose to postpone learning the Spring framework because the task seemed simple enough to be solved without it, however I consider using it for the future projects. As the progress went, I started to get more and more Lazy-loading exceptions for objects that could not be initialized or saved. If you are reading this, you probably know what I’m talking about.

To my understanding, it is not the biggest problem that a Hibernate session is not open at the time when a lazy-loaded object is accessed. The Open Session In View solution to this problem exists and is easily implemented. It guarantees that a session is open during the web request processing lifetime. One can rely on the filter to start a transaction whenever a request begins and commit it after all processing (including the view rendering part) is over.

However, it is more important to keep in mind: even if there is an open session, any lazy object that was previously loaded in another session can not use it automatically. It must be THE same session that loaded the object, or the object needs to be reattached with a merge() or update() call. It doesn’t help if the getCurrentSession() method of your Hibernate session factory returns a session instance, or if the parent/children of the lazy-loaded object have been reattached using update(), the session mapping does not propagate recursively.

Solutions found on the web either suggest not to use lazy loading at all, or reattach the objects to the current session. The former takes lots of RAM to keep the whole object hierarchy loaded from the database and generates a crapton of SQL queries to do so. Reattaching may help in emergency cases but later it backfires by leaving desperate update method calls all over the code.

Yet another solution

In this example I will not use the Open Session in View method. Instead, the Hibernate session is opened when the user logs in. The Hibernate session is stored as a HTTP session attribute until the user either logs out or the session expires. More details on storing parameters in a HTTP session can be found at http://www.javabeat.net/javabeat/scwcd/tutorials/basics/5-session-handling.php. The most important goal here is to get informed when the HTTP session ends so that the Hibernate session can be closed too – the HibernateSessionAttributeValue handles that. The explicit log out is easier to handle by a link onClick() method. The crucial code is given below.

First, build a simple class for attaching a Hibernate session to a HTTP session. By implementing HttpSessionBindingListener interface, it asks for the session management to call its valueUnbound() method when the HTTP session expires.
Disclaimer: the code has not been tested properly, use on your own risk. Comments and suggestions are appreciated


class HibernateSessionAttributeValue implements HttpSessionBindingListener {
	protected org.hibernate.Session hibernateSession = null;

	public HibernateSessionAttributeValue(org.hibernate.Session hibernateSession) {
		super();
		this.hibernateSession = hibernateSession;
	}

	public org.hibernate.Session getHibernateSession() {
		return hibernateSession;
	}
	
	public void valueBound(HttpSessionBindingEvent arg0) {
	}
}
public void valueUnbound(HttpSessionBindingEvent arg0) {
	if (hibernateSession != null) {
		HibernateUtil.commit(hibernateSession);
		HibernateUtil.close(hibernateSession);
		setHibernateSession(null);
	}
}

WiaSession.java

public class WiaSession extends WebSession {
	private static final String HIBERNATE_ATTR_NAME = "hibernate";

	public static WiaSession get() {
		return (WiaSession) Session.get();
	}

	public org.hibernate.Session getHibernateSession() {
		HibernateSessionAttributeValue sessionAttribute = (HibernateSessionAttributeValue) getAttribute(HIBERNATE_ATTR_NAME);
		if (sessionAttribute != null) {
			return sessionAttribute.getHibernateSession();
		}
		return null;
	}

	// called from Login form
	public User login(String username, String password) throws Exception {
		HibernateSessionAttributeValue sessionAttribute = (HibernateSessionAttributeValue) getAttribute(HIBERNATE_ATTR_NAME);
		if (sessionAttribute != null) {
			throw new Exception("Session is already established!");
		}

		// an open session is needed by User.get method
		org.hibernate.Session hibernateSession = HibernateUtil.startSession();
		sessionAttribute = new HibernateSessionAttributeValue(hibernateSession);
		setAttribute(HIBERNATE_ATTR_NAME, sessionAttribute);

		user = User.get(username, password);
		if (user == null) {
			//failed - close the session and remove session attribute
			HibernateUtil.close(hibernateSession);
			setAttribute(HIBERNATE_ATTR_NAME, null);
			return null;
		}
		return user;
	}
//called by 'log out' link
public void logout() {
	HibernateSessionAttributeValue sessionAttribute = (HibernateSessionAttributeValue) getAttribute(HIBERNATE_ATTR_NAME);
	if (sessionAttribute != null) {
		org.hibernate.Session hibernateSession = sessionAttribute.getHibernateSession();
		if (hibernateSession != null) {
			HibernateUtil.commit(hibernateSession);
			HibernateUtil.close(hibernateSession);
			sessionAttribute.setHibernateSession(null);
		}
	}
	Session.get().invalidate();
}

WicketApplication.java
The main Wicket application class does not have any special handling except for creating WiaSession instances.

public class WicketApplication extends WebApplication {
	public WicketApplication() {
	}

	@Override
	protected void init() {
		super.init();
		// causes the home page to be displayed instead of the default
		// "return to home page" link
		getApplicationSettings().setPageExpiredErrorPage(getHomePage());
	}

	// get default home page
	@Override
	public Class<extends Page> getHomePage() {
		return CommonPage.class;
	}

	@Override
	public Session newSession(Request request, Response response) {
		WiaSession wiaSession = new WiaSession(request);
		return wiaSession;
	}
}

2 thoughts on “Simple workaround for Hibernate lazy initialization problem

    1. Antanas

      Unfortunately, I switched to another project soon after this was written, so it was not tested well

      Reply

Leave a Reply

Your email address will not be published.