On my walk home, I realized a possible work-around is to subclass OpenSessionInViewFilter. I just did this:
Code:
package com.dcxms.web.filters;
import net.sf.hibernate.FlushMode;
import net.sf.hibernate.Interceptor;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import org.springframework.beans.BeansException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate.SessionFactoryUtils;
import org.springframework.orm.hibernate.support.OpenSessionInViewFilter;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
public class MyOpenSessionInViewFilter extends OpenSessionInViewFilter {
private String entityInterceptorBeanName;
public void setEntityInterceptorBeanName(String entityInterceptorBeanName) {
this.entityInterceptorBeanName = entityInterceptorBeanName;
}
public Interceptor getEntityInterceptor() throws IllegalStateException, BeansException {
if (this.entityInterceptorBeanName != null) {
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
if (wac == null)
throw new IllegalStateException("Cannot get entity interceptor via bean name without a WebApplicationContext");
return (Interceptor) wac.getBean(this.entityInterceptorBeanName, Interceptor.class);
}
return null;
}
protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, getEntityInterceptor(), null);
session.setFlushMode(FlushMode.NEVER);
return session;
}
}
Here's the modified web.xml entry:
Code:
<filter>
<filter-name>hibernate</filter-name>
<filter-class>
com.dcxms.web.filters.MyOpenSessionInViewFilter
</filter-class>
<init-param>
<param-name>entityInterceptorBeanName</param-name>
<param-value>auditLogInterceptor</param-value>
</init-param>
</filter>
I also removed the entityInterceptor from LocalSessionFactoryBean, and added a entityInterceptorBeanName to HibernateTransactionManager. I have some Quartz jobs that call service layer methods, so not all HB Sessions are created from servlet requests.
Code:
<bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
<property name="entityInterceptorBeanName">
<value>auditLogInterceptor</value>
</property>
</bean>
and of course, the auditLogInterceptor is marked non-singleton:
Code:
<bean id="auditLogInterceptor" class="com.dcxms.auditing.AuditLogInterceptor" singleton="false">
<property name="auditLog">
<ref local="auditLog" />
</property>
</bean>
I've confirmed that I am indeed getting new instances of my HB Interceptor in both servlet requests and in the Quartz jobs. So "mission accomplished", maybe.
I'm a bit concerned about other ways that a HB Session might be had in the application. Clearly, if someone gets a session from the LocalSessionFactoryBean, the entityInterceptor will not get set.
I'm curious to get feedback about this problem/solution. I haven't seen anyone really talk about the concurrency problems using HB Interceptors with Spring. I'm I missing the obvious solution, or are there lots of potential concurrency bugs in people's HB Interceptors?