Implementing Hibernate’s session-per-request pattern using Spring MVC

I’m currently working on a web based forecasting application that is
very data driven. The app is implemented as a J2EE application
utilizing the Spring framework (v2.0). This project was my first foray
into using Hibernate (v3.2), and as such I learned quite a bit
regarding Springs integration with Hibernate, Transaction management
and LazyInitializationExceptions. This blog is an attempt
to share some of what I learned and discuss the approach I took to
implement the session-per-request pattern using Springs MVC framework
and Hibernate integration modules.

Problem:

Our application utilized approximately 40 DAOs, and a single
request could require any number of DAO method calls to fulfill it. All
of our DAOs were implemented using Hibernate and I wanted to avoid
having to create a new Hibernate Session for each DAO method call, this
is sometimes referred to as the session-per-operation anti-pattern.

Another issue I was running into involved an org.hibernate.LazyInitializationException
being thrown from with in my JSP pages when they tried to reference
lazily loaded association on one of the Hibernate managed objects. The
Hibernate managed objects used in this app have a lot of associations
to other Hibernate managed objects, and these associations only needed
to be referenced some of time. To avoid having to pull in each
association every time an object is created all the associations were
configured to be lazily loaded. The problem is that Hibernate does not
support lazy initialization for detached objects and in my set up I was
creating a new Session for each DAO method and then closing that
session when the method completed. So the objects returned by that DAO
method by definition were detached. So the attempt to reference the
lazily associated object within the JSP was throwing the LazyInitializationException. (For more information on the details of lazy loading see:

http://www.hibernate.org/315.html And http://www.hibernate.org/hib_docs/v3/reference/en/html/performance.html)

In short I want to avoid the session-per-operation anti-pattern and
I want a way to keep the Hibernate Session open while my JSPs were
rendering so I could take advantage of the Lazy loading features of
Hibernate without throwing a LazyInitializationException.

While reading through various web sites and documentation I came
across a pattern that seemed to do what I wanted: the
session-per-request pattern. The session-per-request pattern is pretty
much what it sounds like, a new Hibernate Session is created for each
request and that session is used for all data base access needed to
fulfill that

request.

I started to look for ways to implement this pattern. Since each
request could call numerous DAO methods I needed some way to ensure
that each DAO method uses the same session. Also I needed to ensure
that my session stayed open while my JSPs were rendering so I could
take advantage of the Lazy loading features of Hibernate. (NOTE: This
issue caused me to abandon my foray into Springs declarative
transaction management because I couldn’t figure out how to tell the
transaction to terminate after the JSP gets rendered. The only thing I
could figure how to do was to have my transaction boundaries demarcated
at the DAO, Manager or Controller layer wouldn’t work because that
would not extend into my view layer.)

So the basic flow I thought I was looking for was:

  1. Intercept the request before processing begins
  2. Create a new Session that is to be used by all DAOs
  3. Begin a transaction on that session
  4. Make sure that the SessionFactory.getCurrentRequest() method returns a reference to our session.
  5. Intercept
    the response when the processing is complete (after the viewhas been
    rendered) and commit the transaction and close the Session.

Attempt Number 1:

While looking to address the issue of ensuring all DAOs used the
same Hibernate Session I came across Hibernates concept of Contextual
Sessions. After Hibernate 3.1 the behavior of the SessionFactory.getCurrentSession() method can be customized by configuring the SessionFactory to use a specific implementation of interface org.hibernate.context.CurrentSessionContext to manage the definition of the ‘current’ session.

So I decided to run with this idea and I created an implementation of the org.springframework.web.servlet.HandlerInterceptor that could be used to intercept all requests handled by a particular HandlerMapping. The idea was that I would inject the SessionFactory into this handler and use the org.hibernate.context.ManagedSessionContext class to manually manage the context of getCurrentSession. In the preHandle
method of the handler I would open a new Hibernate session, begin a
transaction on that session and bind it to the current thread using the
ManagedSessionContext.bind method. This would then cause all calls to the SessionFactory's getCurrentSession() method to retrieve that session which bound to the current thread. Then in the afterCompletion method I would commit the transaction, close the session and unbind it from the current thread.



/**

Called before the processing of the request is passed off to the Controller.

*/


public boolean preHandle(HttpServletRequest request,

HttpServletResponse response, Object handler) throws Exception {

    Session sess = sessionFactory.openSession();

    sess.beginTransaction();

    ManagedSessionContext.bind((org.hibernate.classic.Session)sess);

    return true;

}

/**Called after the view has been processed.*/

public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler,
Exception ex) throws Exception {

    Session sess = sessionFactory.getCurrentSession();

    sess.getTransaction().commit();

    sess.close();

    ManagedSessionContext.unbind(this.sessionFactory);

}

[For more information regarding Spring Handler Mappings and Handler Interceptors visit Spring’s reference documentation on Handler Mappings and Handler Interceptors.]

A swing and a miss.

So I wired in my HandlerInterceptor class in to one of my HandlerMappings, started up the application and hoped for the best. After testing out my first page I noticed that the getCurrentSession() call I was making in the afterCompletion method was throwing this exception:

org.hibernate.HibernateException: No Hibernate Session bound
to thread, and configuration does not allow creation of
non-transactional one here.

I come to find out there were two problems with this approach:

First Issue: Spring doesn’t support Hibernate ContextualSessions.

When using Hibernate with Spring you configure the session factory has a Spring managed bean with a descriptor such has:



<bean id="mySessionFactory" class="org.springframework.orm.
hibernate3.LocalSessionFactoryBean">

  <property name="dataSource" ref="myDataSource"/>

  <property name="mappingResources">

    <list>

      <value>mappings/ObjectToMap.hbm.xml</value>

       ...

    </list>

  </property>

  <property name="hibernateProperties">

   <props>

    <prop key="hibernate.dialect">org.hibernate.dialect.
Oracle9Dialect</prop>

     ...

   </props>

  </property>   

</bean>

We then inject the mySessionFactory bean into all the classes that use a SessionFactory, such has DAOs.

If you notice the class that we define for the session factory bean is not a Hibernate class. It is a Spring class, org.springframework.orm.hibernate3.LocalSessionFactoryBean. As its javadoc describes this class will actually provide a Proxy of the SessionFactory to the classes it is injected into. Thus when you call getCurrentSession() on this SessionFactory that call doesn’t make it to the Hibernate session factory it is intercepted by the Spring proxy.

Inside Spring’s org.springframework.orm.hibernate3.AbstractSessionFactoryBean a java.lang.reflect.InvocationHandler is created that handles calls to all methods on the proxy object, this code shows the handling of the getCurrentSession() method (lines 264-272):



if (method.getName().equals("getCurrentSession")){

    // Handle getCurrentSession method: return transactional Session, if any.

    try {

        return SessionFactoryUtils.doGetSession((SessionFactory) proxy, false);

    }catch (IllegalStateException ex) {

         throw new HibernateException(ex.getMessage());

    }

}

Instead of using the Hibernate Managed Session Context to track
what the ‘current’ session is the spring proxy intercepts the call to
the getCurrentSession() method and will call the SessionFactoryUtils.doGetSession method which utilizes the org.springframework.transaction.support.TransactionSynchronizationManager to manage what the ‘current’ session context is. Thus with the set up I had I couldn’t use Hibernate managed Session Contexts.

So the reason I was getting the afore mentioned HibernateException is because the getCurrentSession() call I was making in the afterCompletion method was being made on the Spring proxy around the Hibernate SessionFactory which was actually trying to retrieve the ‘current’ Session using the TransactionSynchronizationManager. But since no Session was bound to the current Transaction/Thread using the TransactionSynchronizationManager an IllegalStateException gets thrown which is caught and wrapped in a HibernateException by the code in line 270 of the AbstractSessionFactory class.

Second Issue: I didn’t need to use Hibernate Contextual Sessions

In our application all of our DAOs are implemented by extending the org.springframework.orm.hibernate3.support.HibernateDaoSupport
class. When using this support class the typical way of running queries
is to use its getHibernateTemplate() method to obtain and instance of a
org.springframework.orm.hibernate3.HibernateTemplate object and call one of its various methods to perform the Query:



public List getAllBrands() {

    return this.getHibernateTemplate().find("from Brand brand");

}

Or



public void save(final Brand brand){

    getHibernateTemplate().execute( new HibernateCallback()

    {

        public Object doInHibernate(Session session)

         {

            session.save(brand);

             return null;

         }

    });

}

I had naively assumed that Sessions used by these methods were generated using a call to the SessionFactory's getCurrentSession() method. This isn’t the case, the HibernateTemplate class also uses the SessionFactoryUtils class to retrieve the needed Hibernate Session. The SessionFactoryUtils
class provides several methods for getting a session, for our purposes
they will all do pretty much the same thing, they will use the TransactionSynchronizationManager to manage the concept of a ‘current’ session. The TransactionSynchronizationManager
will, simply put, look to see if a Session is bound to the current
thread/transaction and return that Session. So with this knowledge it
appears that we can use the TransactionSynchronizationManager class to allow us to define the ‘current’ session.

Successful Attempt

I continued on the path of using a HandlerInterceptor but I implemented the preHandle and afterCompletion methods to use the TransactionSynchronizationManager to manage my session’s contexts.


import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import org.hibernate.Session;

import org.hibernate.SessionFactory;

import org.springframework.orm.hibernate3.SessionHolder;

import org.springframework.transaction.support.TransactionSynchronizationManager;

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

/**

This HandlerInterceptor implements the single session per request
pattern when added has an interceptor to a handler mapping. It expects
to be used in a JTA environment and used with a JTASessionContext
CurrentSessionContext. To use it you must declare it has an interceptor
for a handler mapping.

<bean id="sessionPerRequestInterceptor" class="packageName.HibernateSessionPerRequestHandlerInterceptor">

   <property name="sessionFactory" ref="mySessionFactory"/>

</bean> <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

   <property name="interceptors">

      <list><ref bean="sessionPerRequestInterceptor"/></list>

   </property>

   <property name="mappings">

      <props>

         <prop key="blah/blah">maintainCalendarFormController</prop>

      </props>

   </property> </bean>

* @author David Harris david.harris@1answersolutions.com

* @see HandlerInterceptor

*/


public class HibernateSessionPerRequestHandlerInterceptor implements

HandlerInterceptor {

private static final Logger log = Logger.getLogger(HibernateSessionPerRequestHandlerInterceptor.class.getPackage().getName());

private SessionFactory sessionFactory;

/**

Sets the session factory. The expected object is acctually the Spring proxy around the Hibernate Session Factory.

* @param sessionFactory

*/


public void setSessionFactory(SessionFactory sessionFactory) {

    this.sessionFactory=sessionFactory;

}

/** Un-implemented method.

* @see
org.springframework.web.servlet.HandlerInterceptor#postHandle(javax.servlet.http.HttpServletRequest,
javax.servlet.http.HttpServletResponse,
java.lang.Object,org.springframework.web.servlet.ModelAndView)

*/


public void postHandle(HttpServletRequest request, HttpServletResponse

response, Object handler, ModelAndView modelAndView) throws Exception

{

    //Do nothing

}

/**

This guy is supposed to intercept a request that needs to be wrapped in a transaction. It will create a new Hibernate

session and begin a transaction on that session. It will then bind that session to the current thread using springs

TransactionSynchronizationManager class. This will cause the calls
to the getCurrentSession() method of the SessionFactory proxy provided
by spring as well has the calls to springs
SessionFactoryUtils.getSession() method to return the session attached
to the current transaction/thread.
* @see HandlerInterceptor.preHandle(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse, java.lang.Object)

* @see TransactionSynchronizationManager

*/


public boolean preHandle(HttpServletRequest request,

HttpServletResponse response, Object handler) throws Exception {

    log.debug("Opening session and beginning transaction.");

    Session sess = sessionFactory.openSession();

    sess.beginTransaction();

    

/*

This is how spring ties the hibernate session to the current
transaction/thread it acctually binds a SessionHolder object containing
the session. This bindResource method expects the spring proxy
sessionFactory and not the Hibernate session factory in order to work
correctly.

*/

    //Bind the Session to the current thread/transaction


    TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(sess));

    //Activate transaction synchronization for the current thread.

    TransactionSynchronizationManager.initSynchronization();

    return true;

}

/**

This guy will check to see if an exception was thrown while
processing and if so will roll back the current transaction. It will
obtain the current session attached to the Thread and close it.

* @see HandlerInterceptor.afterCompletion(javax.servlet.http.HttpServletRequest,
javax.servlet.http.HttpServletResponse, java.lang.Object,
java.lang.Exception)


* @see TransactionSynchronizationManager

*/


public void afterCompletion(HttpServletRequest request,

HttpServletResponse response, Object handler, Exception ex) throws

Exception {

/*

Since the sessionFactory here is acctually the Spring proxy around
the Hibernate Sesison Factory this getCurrentSession() method will use
the TransactionSynchronizationManager to retreive the Session bound to
the current thread/transaction. Its basically the same has calling
TransactionSynchronizationManager.getResource(sessionFactory) but its
easier to read this way.

*/


    Session sess =sessionFactory.getCurrentSession();

    try{

        if(ex==null){

            log.debug("Committing the database transaction");

            sess.getTransaction().commit();

        }else{

            //An exception was thrown during the processing of the request.

            log.error(ex);

            log.debug("Rolling back the database transaction");

            sess.getTransaction().rollback();

        }

        if(sess.isOpen())

         sess.close();

    }catch(Exception e){

        log.error(e);

        throw e;//Let the exception propagate up.

    }finally{

        try{

            if(sess.isOpen()){

                sess.close();

            }

        }catch(Exception e){/*do nothing*/}

        TransactionSynchronizationManager.unbindResource(sessionFactory);

        TransactionSynchronizationManager.clearSynchronization();

    }

}

}

You can then configure it has a bean in your spring config file:



<bean id="sessionPerRequestInterceptor" class="packageName.HibernateSessionPerRequestHandlerInterceptor">

    <property name="sessionFactory" ref="mySessionFactory"/>

</bean>

After this bean is configured you can set it has an interceptor for
any mapping that handles URLs which need to be wrapped in a
transaction:



<bean id="urlMappingWithDBAccess" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

   <property name="interceptors">

      <list><ref bean="sessionPerRequestInterceptor"/></list>

   </property>

   <property name="mappings">

      <props>

         <prop key="url/path">myControllerBean</prop>

      </props>

   </property>

</bean>

And for URLs that do not need any Session/Transaction management
you simply define a UrlHandlerMapping bean that doesn’t have any
interceptors.



<bean id="urlMappingWithDBAccess" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

   <property name="mappings">

      <props>

         <prop key="url/path">myControllerBean</prop>

      </props>

   </property>

</bean>

This give you the flexibility to determine precisely what URLs you
want to take part in the Transaction/Session management just by
changing your configuration files.

This is the first time I have written a blog. If anybody has any comment/suggestion that can help me improve please let me know.

Advertisements
This entry was posted in Uncategorized. Bookmark the permalink.

One Response to Implementing Hibernate’s session-per-request pattern using Spring MVC

  1. Multi-dimensional servicesFinancing is an important step
    particularly for modular homes. 22, 2006, The Knight Group is currently Atlanta’s Most Affordable home builders in tennessee” with an average of around 1. 35 per share, which is worthy of careful review if you are interested in speaking to a professional quality focused home builders in tennessee, talk to him. However, mere affiliation you not the only thing you need to abide by.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s