I was able to create a JDK proxy:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;
import org.hibernate.SessionFactory;
import org.hibernate.engine.SessionFactoryImplementor;
public class SessionFactoryProxyCreator {
public static SessionFactory instance;
public static SessionFactory createProxy(final SessionFactory realSessionFactory) {
ClassLoader cl = SessionFactory.class.getClassLoader();
Class<?>[] interfaces = new Class[] { SessionFactory.class, SessionFactoryImplementor.class };
instance = (SessionFactory)Proxy.newProxyInstance(cl, interfaces, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("openSession".equals(method.getName())) {
System.out.println("NEW SESSION AT " + new Date());
}
return method.invoke(realSessionFactory, args);
}
});
return instance;
}
}
and you would call this from a custom SessionFactoryBean:
import org.codehaus.groovy.grails.orm.hibernate.ConfigurableLocalSessionFactoryBean;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class MyConfigurableLocalSessionFactoryBean extends ConfigurableLocalSessionFactoryBean {
public MyConfigurableLocalSessionFactoryBean() {
setCurrentSessionContextClass(MyCurrentSessionContext.class);
}
@Override
protected SessionFactory buildSessionFactory() throws Exception {
setExposeTransactionAwareSessionFactory(false);
return SessionFactoryProxyCreator.createProxy(super.buildSessionFactory());
}
@Override
protected SessionFactory newSessionFactory(Configuration config) throws HibernateException {
setExposeTransactionAwareSessionFactory(false);
return SessionFactoryProxyCreator.createProxy(super.newSessionFactory(config));
}
}
which depends on a modified version of Spring's SpringSessionContext that uses the proxy instead of the real session factory:
import org.hibernate.HibernateException;
import org.hibernate.classic.Session;
import org.hibernate.context.CurrentSessionContext;
import org.hibernate.engine.SessionFactoryImplementor;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
public class MyCurrentSessionContext implements CurrentSessionContext {
public MyCurrentSessionContext(SessionFactoryImplementor sessionFactory) {
// ignore the real sessionFactory, need to use the proxy
}
public Session currentSession() throws HibernateException {
try {
return (org.hibernate.classic.Session)SessionFactoryUtils.doGetSession(
SessionFactoryProxyCreator.instance, false);
}
catch (IllegalStateException e) {
throw new HibernateException(e.getMessage());
}
}
}
This needs to be registered in resources.groovy to replace the standard Grails ConfigurableLocalSessionFactoryBean:
import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
import org.codehaus.groovy.grails.orm.hibernate.events.PatchedDefaultFlushEventListener
beans = {
sessionFactory(MyConfigurableLocalSessionFactoryBean) {
def ds = AH.application.config.dataSource
def hibConfig = AH.application.config.hibernate
dataSource = ref('dataSource')
List hibConfigLocations = []
if (AH.application.classLoader.getResource('hibernate.cfg.xml')) {
hibConfigLocations << 'classpath:hibernate.cfg.xml'
}
def explicitLocations = hibConfig?.config?.location
if (explicitLocations) {
if (explicitLocations instanceof Collection) {
hibConfigLocations.addAll(explicitLocations.collect { it.toString() })
}
else {
hibConfigLocations << hibConfig.config.location.toString()
}
}
configLocations = hibConfigLocations
if (ds?.configClass) {
configClass = ds.configClass
}
hibernateProperties = ref('hibernateProperties')
grailsApplication = ref('grailsApplication', true)
lobHandler = ref('lobHandlerDetector')
entityInterceptor = ref('entityInterceptor')
eventListeners = ['flush': new PatchedDefaultFlushEventListener(),
'pre-load': ref('eventTriggeringInterceptor'),
'post-load': ref('eventTriggeringInterceptor'),
'save': ref('eventTriggeringInterceptor'),
'save-update': ref('eventTriggeringInterceptor'),
'post-insert': ref('eventTriggeringInterceptor'),
'pre-update': ref('eventTriggeringInterceptor'),
'post-update': ref('eventTriggeringInterceptor'),
'pre-delete': ref('eventTriggeringInterceptor'),
'post-delete': ref('eventTriggeringInterceptor')]
}
}