有没有办法在创建新的Hibernate会话时拦截它们?我需要访问每个Session实例以启用带参数的Hibernate过滤器。
我工作的唯一解决方案涉及包装SessionFactory,但这涉及很多半讨厌的黑客,并且它要求我实现大约60种方法,其中只有少数是有趣的。
Hibernate的SessionFactory实现是出于某种令人烦恼的原因而宣布最终的,因此扩展它不是一种选择。我也试过方面和Java代理没有任何运气。
答案 0 :(得分:5)
我能够创建一个JDK代理:
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;
}
}
您可以从自定义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));
}
}
取决于使用代理而不是真实会话工厂的Spring的SpringSessionContext的修改版本:
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());
}
}
}
这需要在resources.groovy中注册以替换标准的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')]
}
}
答案 1 :(得分:2)
我已经解决了这个问题(至少在Hibernate为这样的事情提供了适当的API之前)。解决方案的简短版本:
更长的版本: http://www.developer-b.com/blog/entry/1635/2010/oct/07/intercepting-hibernate-sessions
来源/ Github: http://github.com/multi-tenant/grails-hibernate-hijacker(仍然非常实验性)
感谢您的投入!
答案 2 :(得分:2)
Burt和Kimble的答案都有效,但你可以更容易地做到这一点。您需要创建一个实现Hibernate CurrentSessionContext类的类,但不需要为会话工厂创建代理,因为您可以覆盖会话上下文类中的会话创建行为,然后只需指定此类的名称在会话工厂bean的属性中。例如像这样编写会话上下文
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;
}
}
然后在传递给会话工厂类的属性中,指定此类名:
hibernate.current_session_context_class=org.company.MySessionContext
例如,在典型的Spring场景中,您可以使用Spring工厂bean来实例化您的hibernate属性,如下所示:
<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>
然后通常您将使用Spring会话工厂bean创建会话工厂,例如(注意不同版本的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包含三个不同的会话上下文类,因此只需覆盖与您相关的一个:
org.hibernate.context.JTASessionContext
org.hibernate.context.ThreadLocalSessionContext
org.hibernate.context.ManagedSessionContext
这三个方法都有方法buildOrObtainSession,方法的javadoc实际上说&#34;提供用于子类化目的&#34;。如果您使用跨多个资源的事务(例如多个数据库或数据库和JMS队列),则需要JTA会话上下文,如果您只是在每个事务中访问单个资源,则ThreadLocalSessionContext就足够了。
答案 3 :(得分:1)
查看Hibernate-filter plugin - 这可能是您想要使用的内容,或者您至少可以看到该插件是如何实现的。
另外我相信Multi-tenant plugin可能有一些使用Hibernate Session过滤器的代码。
答案 4 :(得分:1)
在代码中只有一个地方你可以从hibernate请求一个新会话(例如在你的DAO的抽象基类中),并在那里启用你的过滤器,这可能是最干净的。