From Could not obtain transaction-synchronized Session for current thread error handling to declarative and programmatic transaction processing

## Extended from ***Could not obtain transaction-synchronized Session for current thread*** error handling to ***Declarative and programmatic transaction processing***

To undertake the above ##[***Spring4.1+Hibernate4.3***Integration***EhCache***Secondary cache design report***org.hibernate.cache.spi.RegionFactory***or** *org.hibernate.engine.spi.CacheImplementor*** processing and detailed problem description] ( http://blog.csdn.net/MUXINGYE/article/details/54562264 "article title") follow-up processing

Integrated version spring-framework-4.1.0.RELEASE-dist hibernate-release-4.3.6.Final

The various answers on the Internet are so rubbing, the donkey's lips are not the horse's mouth. It feels so weird.

They are not fools, saying that those configuration problems are useless.

I see a lot of what annotations are used to solve, which is not the crux of the problem at all. It feels like sheer nonsense.

so. The most reliable way is to look at the execution of the source code.

The key to the problem involves the sequence of program execution and the invocation of several key methods.

If the current program first executes the method of the declarative transaction

Then *** in the process of declarative transaction execution, there is a very important assignment action to the data source and session***.

The source code analysis is as follows

1. Say it in front

As for why the following source code goes like this, it is related to the configuration of your Spring's declarative transaction processing. Let's not talk nonsense here, and paste my configuration information directly.

This is the Spring configuration file

	<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's configuration file only needs to pay attention to two points

	<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. Please see the detailed process below

from

org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(Method, Class<?>, InvocationCallback)
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

execute to -

org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(PlatformTransactionManager, TransactionAttribute, String)
内部的
status = tm.getTransaction(txAttr);

Then execute to -

org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(TransactionDefinition)
内部的 - Object transaction = doGetTransaction();

The following is the ***key code***. A little more detail-

	执行到
	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());
			}
			完成了绑定操作
			

The crux of the problem is here

后期在使用到 - 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();
		就可以正常获取,后续的操作也是没有问题的。

However, if you do not have normal declarative transaction processing related operations

会在
org.springframework.orm.hibernate4.SpringSessionContext.currentSession()
方法内部抛出
throw new HibernateException("Could not obtain transaction-synchronized Session for current thread");

3. There are four solutions to the problem -

One is to cancel the related database operations bound by non-declarative transaction methods. Kill the chicken to get the eggs. PASS.

The second is to use the native Hibernate API for the method invocation of the related transaction before the method invocation of the declarative transaction, and do not consider the declarative transaction problem.

However, this way of writing will report *** transaction nesting problem *** in the later stage, which is not recommended.

The third is to use openSession when using the native Hibernate API to manually handle the transaction rollback problem . - recommend

The code pattern looks like the following

		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();
		}

Fourth, use declarative transaction processing for all related methods to prevent getCurrentSession() from being unavailable - troublesome, unnecessary. Not consider.

The final conclusion is that a lot of repetitive code can be handled with declarative transactions. Occasional operations, using programmatic processing, and code that executes before declarative transaction methods. Only openSession. And not getCurrentSession.

If you need to reprint , please indicate the source of the article https://my.oschina.net/u/3032872/blog/1648371 Thank you~

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325057866&siteId=291194637