Saturday, July 27, 2013

Contextual session-local Hibernate interceptor with Spring

    Hibernate interceptor can be used for various purposes, e.g. audit trail, simple logging of persistent operations. However, we should have on our minds the fact that Hibernate SessionFactory level interceptor has to be implemented in a thread-safe manner, because it will be shared among all of the sessions. Is there any way to implement the interceptor with single-thread programming model?
We can use session-local (or preferably, contextual session-local) interceptor. Hibernate session is associated with particular transaction and is not supposed to be shared by threads. Hibernate 4.x allows to create session level interceptor by means of:
sessionFactory.withOptions().interceptor(ourSessionLocalInterceptor).openSession();
But what if we want to use contextual sessions (that are bounded to a thread or Spring managed transaction) and we want to use 'getCurrentSessionMethod()'? Because of the fact that I am currently using Spring 3.2.2 and Hibernate 4.2.2 tandem, I will present my solution for that problem within mentioned libraries. It is quite simple to achieve desired goal, however I spend some time to figure the things out.
Firstly, I define HibernateTransactionManager - I want to use Spring managed contextual Hibernate sessions. I do not use XML for Spring so I go with the following configuration:
@Configuration
public class Configuration {
    ...
    @Bean
    public HibernateTransactionManager transactionManager() throws IOException, NamingException {
        HibernateTransactionManager txManager = new HibernateTransactionManager(sessionFactory());
        txManager.setEntityInterceptorBeanName("auditInterceptor"); // that's the trick
        return txManager; 
    }
}

    @Component
    @Scope("prototype") // and that's the trick
    public class AuditInterceptor extends EmptyInterceptor {...}
The key is to use method which takes bean's name instead of specific instance. Moreover, our interceptor bean should have 'prototype' scope. Thanks to that every new session will be created with interceptor taken from Spring context - context.getBean(...). Using 'txManager.setEntityInterceptor(...)' would result in SessionFactory level interceptor - not our goal.
Hope that helped!

2 comments :

  1. Thank you so much for writing this !! You've literally save me hours of figuring stuff out !!

    ReplyDelete