How to intercept all Hibernate sessions when they are created (Spring / Grails environment)

Is there a way to intercept all new Hibernate sessions when they are created? I need to access every instance of the session to enable the Hibernate filter with a parameter.

The only solution I worked with was to complete the SessionFactory, but it was due to a lot of half-sized hacks, and it also required me to implement about 60 methods, where only a few of them are interesting.

SessionFactory hibernation has been declared final for some annoying reason, so an extension is not an option. I also tried aspects and Java proxies with no luck.

+4
source share
5 answers

I solved this problem (at least until Hibernate provides the correct API for such things). Short version of the solution:

  • Proxy session factory
  • Intercept the invocations method for getCurrentSession and use the CurrentSessionContext implementation that we initialized (and not Hibernate).

Longer version: http://www.developer-b.com/blog/entry/1635/2010/oct/07/intercepting-hibernate-sessions

Sources / Gitub: http://github.com/multi-tenant/grails-hibernate-hijacker (still very experimental)

Thanks for entering!

+2
source

I managed 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 it 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 SpringSessionContext that uses a proxy instead of a real factory session:

 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 .groovy resources 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')] } } 
+5
source

The answers of Burt and Kimble will work, but you can do it more easily. You need to create a class that implements the Hibernate CurrentSessionContext class, but there is no need to create proxies for the factory session, since you can override the session creation behavior in the session context class, and then simply specify the name of this class in the properties of your factory bean session. e.g. write your session context as follows

 import org.hibernate.FlushMode; import org.hibernate.classic.Session; import org.hibernate.context.JTASessionContext; import org.hibernate.engine.SessionFactoryImplementor; public class MySessionContext extends JTASessionContext { public MySessionContext(SessionFactoryImplementor factory) { super(factory); } @Override protected Session buildOrObtainSession() { Session session = super.buildOrObtainSession(); // do stuff to the session here return session; } } 

Then, in the properties that you pass to your factory session class, specify this class name:

 hibernate.current_session_context_class=org.company.MySessionContext 

For example, in a typical Spring script, you can use the Spring factory bean to create the properties of your sleep mode, for example:

 <bean id="hibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="properties"> <props> <prop key="hibernate.current_session_context_class">org.company.MySessionContext</prop> // your other Hibernate properties here </props> </property> </bean> 

Then, as a rule, you will create a factory session using the Spring factory bean session, for example (the name of the package of notes will differ for different versions of Hibernate):

 <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocations" ref="hibernateConfigLocations"/> <property name="hibernateProperties" ref="hibernateProperties"/> <property name="entityInterceptor" ref="hibernateEntityInterceptor"/> <property name="jtaTransactionManager" ref="transactionManager"/> <property name="implicitNamingStrategy" ref="underscoresNamingStrategy"/> </bean> 

Hibernate includes three different session context classes, so just override the value that applies to you:

 org.hibernate.context.JTASessionContext org.hibernate.context.ThreadLocalSessionContext org.hibernate.context.ManagedSessionContext 

All three have a buildOrObtainSession method, and the javadoc for the method actually says "provided for subclass purposes." A JTA session context will be needed if you use transactions that span multiple resources, such as multiple databases or databases and JMS queues, if you just access one resource in each transaction, then a ThreadLocalSessionContext will be enough.

+2
source

Take a look at the Hibernate-filter plugin - it may be what you want to use, or you can at least see how this plugin does it.

I also believe that a Multi-tenant plugin may have some code that uses Hibernate session filters.

+1
source

It would probably be clean to have only one place in the code where you request a new session from sleep mode (for example, in the abstract base class of your DAOs) and enable your filter there.

0
source

All Articles