##由***Could not obtain transaction-synchronized Session for current thread***错误处理引申到***声明式与编程式事务处理***
承接上文 ##[***Spring4.1+Hibernate4.3***整合***EhCache***二级缓存设计报***org.hibernate.cache.spi.RegionFactory***或***org.hibernate.engine.spi.CacheImplementor***的处理及细节问题说明](http://blog.csdn.net/MUXINGYE/article/details/54562264" 文章标题") 后续处理
整合版本 spring-framework-4.1.0.RELEASE-dist hibernate-release-4.3.6.Final
网上的各种答案,好搓啊,驴唇不对马嘴。 感觉真是太奇葩。
都不是傻子,说那些配置问题有个屁用。
我看很多是用什么注解解决的,这根本就不是问题的关键。 感觉纯粹是无稽之谈。
所以。最靠谱的还是看源码的执行喽。
问题的关键涉及到***程序执行的顺序问题以及几个关键方法的调用***
如果当前程序首先执行到了声明式事务的方法
那么***在声明式事务执行的过程中,有很重要的一个对数据源及session的赋值动作***。
源码分析如下
1、说在前面
至于下述源码 为什么这么走,跟你Spring的声明式事务处理的配置相关联, 此处少点废话, 直接贴上我的配置信息。
这是Spring的配置文件
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>classpath:dbcp.properties</value>
</property>
</bean>
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="${jdbc.dbcp.driverClassName}"/>
……
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocations">
<value>classpath:hibernate.cfg.xml</value>
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="dataSource" ref="dataSource"/>
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:advice id="txServiceAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" rollback-for="java.lang.Throwable" />
<tx:method name="*" rollback-for="java.lang.Throwable" />
</tx:attributes>
</tx:advice>
<tx:advice id="txDaoAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" rollback-for="java.lang.Throwable" />
<tx:method name="*" rollback-for="java.lang.Throwable" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut
expression="execution(* ..service.impl..*.*(..) )"
id="declareTransactionServicePC"/>
<aop:pointcut
expression="execution(* fjw..dao.impl..*.*(..) )"
id="declareTransactionDaoPC"/>
<aop:advisor advice-ref="txServiceAdvice" pointcut-ref="declareTransactionServicePC" order="10" />
<aop:advisor advice-ref="txDaoAdvice" pointcut-ref="declareTransactionDaoPC" order="11" />
</aop:config>
Hibernate的配置文件只需注意两点即可
<property name="current_session_context_class">
org.springframework.orm.hibernate4.SpringSessionContext
</property>
<property name="hibernate.cache.region.factory_class">
org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
</property>
2、下面请看是细流程
从
org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(Method, Class<?>, InvocationCallback)
的
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
执行到 -
org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(PlatformTransactionManager, TransactionAttribute, String)
内部的
status = tm.getTransaction(txAttr);
再执行到 -
org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(TransactionDefinition)
内部的 - Object transaction = doGetTransaction();
下述是***关键代码***。 贴的详细一点 ——
执行到
org.springframework.orm.hibernate4.HibernateTransactionManager.doGetTransaction()
方法内的 -
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
的内部执行流程
org.springframework.transaction.support.TransactionSynchronizationManager.TransactionSynchronizationManager()
public abstract class TransactionSynchronizationManager {
public static Object getResource(Object key) {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Object value = doGetResource(actualKey);
if (value != null && logger.isTraceEnabled()) {
logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" +
Thread.currentThread().getName() + "]");
}
return value;
}
Object value = doGetResource(actualKey); 这句是关键所在
第一次执行到此处时,为空,继续向下的话,
org.springframework.orm.hibernate4.HibernateTransactionManager.doGetTransaction()
方法内的 -
if (getDataSource() != null) {
ConnectionHolder conHolder = (ConnectionHolder)
TransactionSynchronizationManager.getResource(getDataSource());
txObject.setConnectionHolder(conHolder);
}
会尝试根据数据源完成获取,但是依然为空。
继续执行到方法内部的 - doBegin(transaction, definition);
内部流程是
org.springframework.orm.hibernate4.HibernateTransactionManager.doBegin(Object, TransactionDefinition)
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
事务在上一步已经获取到了
是org.springframework.orm.hibernate4.HibernateTransactionManager$HibernateTransactionObject@593444ea
继续向下 -
Session session = null;
try {
if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
Interceptor entityInterceptor = getEntityInterceptor();
Session newSession = (entityInterceptor != null ?
getSessionFactory().withOptions().interceptor(entityInterceptor).openSession() :
getSessionFactory().openSession());
此步会完成对Session的赋值操作
if (logger.isDebugEnabled()) {
logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction");
}
txObject.setSession(newSession);
会将创建的 Session 和 HibernateTransactionManager完成绑定
}
session = txObject.getSessionHolder().getSession();
这个时候,才能通过这个方法获取到事务绑定的session
SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=org.hibernate.engine.spi.ExecutableList@5642ab5d updates=org.hibernate.engine.spi.ExecutableList@330092e9 deletions=org.hibernate.engine.spi.ExecutableList@6aad1927 orphanRemovals=org.hibernate.engine.spi.ExecutableList@a4e8628 collectionCreations=org.hibernate.engine.spi.ExecutableList@2949b161 collectionRemovals=org.hibernate.engine.spi.ExecutableList@251c6c74 collectionUpdates=org.hibernate.engine.spi.ExecutableList@6d1446b4 collectionQueuedOps=org.hibernate.engine.spi.ExecutableList@4e2e0b5e unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])
后续操作是对当前事务的相关参数的设置,就忽略了
内部有一个操作 -
// Bind the session holder to the thread.
if (txObject.isNewSessionHolder()) {
TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());
}
完成了绑定操作
问题的关键来了
后期在使用到 - org.hibernate.internal.SessionFactoryImpl.getCurrentSession() 的时候
从org.hibernate.internal.SessionFactoryImpl.getCurrentSession()
内部的currentSessionContext.currentSession();
到org.springframework.orm.hibernate4.SpringSessionContext.currentSession()
内部的Object value = TransactionSynchronizationManager.getResource(this.sessionFactory);
内部会执行到上述分析的
org.springframework.transaction.support.TransactionSynchronizationManager.getResource(Object)
这个时候,是有值的。因为上述完成了赋值操作
org.springframework.orm.hibernate4.SessionHolder@68a908d
那么后续的Session session = sessionHolder.getSession();
就可以正常获取,后续的操作也是没有问题的。
但是,如果你没有正常的声明式事务处理的事务的相关操作
会在
org.springframework.orm.hibernate4.SpringSessionContext.currentSession()
方法内部抛出
throw new HibernateException("Could not obtain transaction-synchronized Session for current thread");
3、问题的解决方案可以有四 ——
一是取消非声明式事务方法绑定的相关数据库操作。杀鸡取卵。PASS。
二是在声明式事务的方法调用之前的相关事务处理的方法调用使用 原生的HibernateAPI,不要考虑声明式事务问题。
但是这种写法在后期会报出***事务嵌套问题***,不推荐。
三是,使用 原生的HibernateAPI的时候使用openSession,手动处理事务回滚问题。 - 推荐
形如下述代码模式
Transaction transaction = null;
Session session = null;
try {
session = HibernateUtil.openSession();
transaction = session.beginTransaction();
……
transaction.commit();
} catch (Exception e) {
if (transaction!=null) transaction.rollback();
} finally {
if (session!=null) session.close();
}
四是,将所有相关的方法采用声明式事务处理,防止getCurrentSession()获取不到 - 麻烦,没必要。不考虑。
最终结论就是, 大量重复代码采用声明式事务处理即可。 偶尔的操作,使用编程式处理,并且在声明式事务方法之前执行的代码。只能openSession。而不能getCurrentSession。
如需要转载 请注明文章出处 https://my.oschina.net/u/3032872/blog/1648371 谢谢~