Grafting Spring Transactions on Legacy Transaction Models

Posted by Tejus Parikh on July 30, 2009

In another part of the continuing series on getting Spring to work properly with our legacy components, I recently had to revisit our the way were were handling transactions. In the previous post on this topic, I demonstrated how you can use a HandlerInterceptor to control your transactions. In this post, I demonstrate how you could use spring transactions, even if you can’t use the Spring way of configuring your Hibernate SessionFactory and need to support legacy code. The end result is much like putting a Ferrari body on a Pontiac Fiero, but it will accomplish the job. The earlier solution has a significant drawback. The transaction will commit after the controller finished processing the request. Since Hibernate will not flush it’s session until a commit, all database errors will occur in a place where you can’t respond to them easily. Pushing the transaction boundary to the level below the controller (where it belongs anyway) will allow for correct error handling at the UI level. This can be accomplished with Spring Transactions. Spring needs two things for @Transactional annotated code to do what you expect: the session factory and the transaction manager. Our legacy code used a statically initialized class to create a session factory. My first step was to manually create the HibernateTransactionManager with the correct session factory.


	sessionHolder = new HibernateUtilSessionHolder();

	SessionFactory sessionFactory = configuration.buildSessionFactory();

	sessionHolder.setSessionFactory(sessionFactory);



	HibernateTransactionManager htm = new HibernateTransactionManager(sessionFactory);

	sessionHolder.setTxManager(htm);

The sessionHolder is just a class I use to hold the current SessionFactory and TransactionManager. The sessionHolder is also the interface between the legacy code TransactionManager. Here is an example method from HibernateUtilSessionHolder:

    public Session getSession() {

        SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());

        if (sessionHolder == null) {

            if (LOG.isDebugEnabled()) {

                LOG.debug("opening session");

            }

            Session s = sessionFactory.openSession();

            sessionHolder = new SessionHolder(s);

            TransactionSynchronizationManager.bindResource(getSessionFactory(), sessionHolder);

        }

        if (LOG.isDebugEnabled()) {

            LOG.debug("GETTING session");

        }

        return sessionHolder.getSession();

    }

Since I hide all the functionality of the TransactionManager behind the previous API, none of the legacy code is aware the underlying transaction model has changed and manual commits will work as they have done before. The next trick is to get annotations working within the context of new code. Exposing the SessionFactory and TransactionManager to Spring is pretty easy. First, create two static methods to access what was created earlier:

    public PlatformTransactionManager getTransactionManagerInstance() {

        return sessionHolder.getTxManager();

    }



    public SessionFactory getSessionFactoryInstance() {

        return sessionHolder.getSessionFactory();

    }

Now expose them to the context by using Spring’s factory bean creation mechanism:

HIbernateUtil is the static class that created the SessionFactory and TransactionManager. The other two lines show how to use it as a factory. Finally, all that’s left to do is to add

to the spring.xml file and mark up a bunch of code with @Transactional. It might be a Fiero underneath, but it still feels like a Ferrari.

Tejus Parikh

I'm a software engineer that writes occasionally about building software, software culture, and tech adjacent hobbies. If you want to get in touch, send me an email at [my_first_name]@tejusparikh.com.