Spring 與 MyBatis 事務(wù)管理源碼解析
點擊上方藍色字體,選擇“標星公眾號”
優(yōu)質(zhì)文章,第一時間送達
? 作者?|?eternal_heathens
來源 |?cnblogs.com/eternal-heathens/p/13391261.html
用到mybatis便由spring和myabtis集成,SqlSessionFactoryBean(直接負責(zé)對mybatis所需環(huán)境的創(chuàng)建) ,配置相應(yīng)的datasource到springConfig文件中,并將datasource注入到SqlSessionFactoryBean的實例化到容器中,依據(jù)他創(chuàng)建sqlsession到spring容器中,便可調(diào)用sqlssion執(zhí)行對數(shù)據(jù)庫的操作
(以下的三個都由Spring 的容器ClassPathXmlApplicationContext進行管理)
"comboPooledDataSource"?class="com.mchange.v2.c3p0.ComboPooledDataSource">
????????"driverClass"?value="com.mysql.jdbc.Driver"/>
????????"jdbcUrl"?value="jdbc:mysql://localhost:3306/ssm"/>
????????"user"?value="root"/>
????????"password"?value="123456"/>
????"sqlSessionFactory"?class="org.mybatis.spring.SqlSessionFactoryBean">
????"dataSource"?ref="comboPooledDataSource"/> "mapperScannerConfigurer"?class="org.mybatis.spring.mapper.MapperScannerConfigurer">
????"basePackage"?value="cn.itcast.dao"/> 在對@Repository的類的接口類調(diào)用時,MapperScannerConfigurer將Dao層加到Spring容器中,以便Service層調(diào)用
public?class?MapperScannerConfigurer implements?BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
??//接口包
??
????private?String?basePackage;
????private?boolean?addToConfig = true;
????private?SqlSessionFactory sqlSessionFactory;
????private?SqlSessionTemplate sqlSessionTemplate;
????private?String?sqlSessionFactoryBeanName;
????private?String?sqlSessionTemplateBeanName;
????private?Class extends?Annotation> annotationClass;
????private?Class> markerInterface;
????private?ApplicationContext applicationContext;
????private?String?beanName;
????private?boolean?processPropertyPlaceHolders;
????private?BeanNameGenerator nameGenerator;
?????···
?????get/set方法用以注入
?????···
}獲取事務(wù)屬性對象(TransactionAttributes若無設(shè)置attributes則默認為DefaultTransactionDefinition),放置在容器中,transactionManager調(diào)用getTransaction時作為參數(shù)傳入
事務(wù)屬性對象持有事務(wù)的相關(guān)配置,比如事務(wù)的隔離級別,傳播行為,是否只讀等。我們開啟spring事務(wù)管理時,通常都會在配置文件里加入這樣一段配置。
<tx:advice?id="txAdvice"?transaction-manager="transactionManager">
????<tx:attributes>
????????<tx:method?name="get*"?read-only="true"/>
????????<tx:method?name="*"/>
????tx:attributes>
tx:advice>TransactionDefinition主要定義了有哪些事務(wù)屬性可以指定:
這是大多數(shù)數(shù)據(jù)庫系統(tǒng)的默認隔離級別(但不是MySQL默認的)。它滿足了隔離的簡單定義:一個事務(wù)只能看見已經(jīng)提交事務(wù)所做的改變。這種隔離級別 也支持所謂的**不可重復(fù)讀(Nonrepeatable Read),如果一個用戶在一個事務(wù)中多次讀取一條數(shù)據(jù),而另外一個用戶則同時更新啦這條數(shù)據(jù),造成第一個用戶多次讀取數(shù)據(jù)不一致。****
這是MySQL的默認事務(wù)隔離級別,它確保同一事務(wù)的多個實例在并發(fā)讀取數(shù)據(jù)時,會看到同樣的數(shù)據(jù)行。不過理論上,這會導(dǎo)致另一個棘手的問題:幻讀 (Phantom Read)。簡單的說,幻讀指第一個事務(wù)讀取一個結(jié)果集后,第二個事務(wù),對這個結(jié)果集經(jīng)行增刪操作(第一個事務(wù)暫時沒用到,沒添加行鎖),然而第一個事務(wù)中再次對這個結(jié)果集進行查詢時,數(shù)據(jù)發(fā)現(xiàn)丟失或新增,出現(xiàn)了“幻影”。通過加表級鎖解決,如間隙鎖InnoDB和Falcon存儲引擎通過多版本并發(fā)控制(MVCC,Multiversion Concurrency Control)機制解決了該問題。
Serializable(可串行化)ISOLATION_SERIALIZABLE
這是最高的隔離級別,它通過強制事務(wù)排序,使之不可能相互沖突,從而解決幻讀問題。簡言之,它是在每個讀的數(shù)據(jù)行上加上共享鎖。在這個級別,可能導(dǎo)致大量的超時現(xiàn)象和鎖競爭。
使用“一站式”的TransactionProxyFactoryBean
使用BeanNameAutoProxyCreater
使用Spring2.x的聲明事務(wù)配置方式(我們使用的)
我們一般通過TransactionTemplate來對PlatformTransactionManager的事務(wù)進行封裝,使得出現(xiàn)異常需要callback時,將其依據(jù)callback接口的實現(xiàn)可以在TransactionTemplate類的excute方法中便處理成功,我們就只需要注意call back接口的設(shè)計與實現(xiàn),就能將PlatformTransactionManager的callback都由該模板實現(xiàn),減少重復(fù)代碼的編寫。當然小型測試直接用也可以。
Spring的事務(wù)處理中,通用的事務(wù)處理流程是由抽象事務(wù)管理器AbstractPlatformTransactionManager來提供的,而具體的底層事務(wù)處理實現(xiàn),由PlatformTransactionManager的具體實現(xiàn)類來實現(xiàn),如 DataSourceTransactionManager 、JtaTransactionManager和 HibernateTransactionManager等。(我們通常使用的是DataSourceTransactionManager來與mybatis集成)
spring事務(wù)處理的一個關(guān)鍵是保證在整個事務(wù)的生命周期里所有執(zhí)行sql的jdbc connection和處理事務(wù)的jdbc connection始終是同一個(不然事務(wù)通過不同connection commit的時候可能會相互覆蓋,順序也難以確定)。然后執(zhí)行sql的業(yè)務(wù)代碼一般都分散在程序的不同地方,如何讓它們共享一個jdbc connection呢?
這里spring做了一個前提假設(shè):即一個事務(wù)的操作一定是在一個thread中執(zhí)行,在事務(wù)未結(jié)束前該事務(wù)一直存在于該thread中,且一個thread中如果有多個事務(wù)在不同的jdbc connection的話,他們必須順序執(zhí)行(在上一個結(jié)束的情況下),不能同時存在,此時若是將connection也綁定到線程中,那(這個假設(shè)在絕大多數(shù)情況下都是成立的,mybatis自身的事務(wù)處理中,sqlsession也可以多次執(zhí)行commit,connection的獲取是在sqlsession執(zhí)行sql時進行的,因此sqlsession也可有多個不同jdbc connection生成的事務(wù),必須順序執(zhí)行)。
基于這個假設(shè),spring在transaction創(chuàng)建時,會用ThreadLocal把創(chuàng)建這個事務(wù)的jdbc connection綁定到當前thread,接下來在事務(wù)的整個生命周期中都會從ThreadLocal中獲取同一個jdbc connection(只要沒有主動斷開connection)。
Spring本身的事務(wù)流程(配合JdbcTemplate)
Repeatable Read(可重讀)ISOLATION_REPEATABLE_READ
事務(wù)的隔離級別(Isolation)
事務(wù)的傳播行為(Propagation Behavior)
默認為PROPAGATION_REQUIRED ,若存在事務(wù)則加入當前事務(wù),若沒有則自己新建一個事務(wù)。
Propagation Behavior注意PROPAGATION_REQUIRES_NEW和PROPAGATION_NESTED的區(qū)別,前者將當前事務(wù)掛起,創(chuàng)建新的事務(wù)執(zhí)行;后者,則是在當前事務(wù)種的一個嵌套事務(wù)中執(zhí)行
事務(wù)的超時時間(Timeout)
是否為只讀事務(wù)(ReadOnly)
SQL標準定義了4類隔離級別,包括了一些具體規(guī)則,用來限定事務(wù)內(nèi)外的哪些改變是可見的,哪些是不可見的。低級別的隔離級一般支持更高的并發(fā)處理,并擁有更低的系統(tǒng)開銷。
ISOLATION_DEFAULT 數(shù)據(jù)庫的默認級別:mysql為Repeatable Read(可重讀),其他的一般為Read Committed(讀取提交內(nèi)容)
Read Uncommitted(讀取未提交內(nèi)容)?ISOLATION_READ_UNCOMITTED
在該隔離級別,所有事務(wù)都可以看到其他未提交事務(wù)的執(zhí)行結(jié)果。本隔離級別很少用于實際應(yīng)用,因為它的性能也不比其他級別好多少。讀取未提交的數(shù)據(jù),也被稱之為臟讀(Dirty Read)。
Read Committed(讀取提交內(nèi)容)ISOLATION_READ_COMITTED
TransactionAttribute在TransactionDefinition的基礎(chǔ)上添加了rollbackon方法,可以通過聲明的方式指定業(yè)務(wù)方法在拋出的哪些的異常的情況下可以回滾事務(wù)。(該接口主要面向使用Spring AOP進行聲明式事務(wù)管理的場合)

spring提供編程式的事務(wù)管理(自己創(chuàng)建事務(wù)管理器,由自己調(diào)用管理器的方法創(chuàng)建事務(wù)或處理事務(wù),可以使用動態(tài)代理減少代理類的編寫,但是還是要編寫很多事務(wù)的代碼在業(yè)務(wù)代碼的前后,不是很方便管理,對于事務(wù)管理這種特性基本一致的操作用聲明反而更利于維護)和聲明式事務(wù)處理(聲明后由容器創(chuàng)建,由聲明來調(diào)用相應(yīng)的方法,聲明式事務(wù)管理用AOP避免了我們?yōu)槊恳粋€需要事務(wù)管理的類創(chuàng)建一個代理類)。
若是聲明式事務(wù)管理,我們會發(fā)現(xiàn),我們沒有自己操作commit的空間,他會在你事務(wù)聲明的方法結(jié)束時就commit,你可以把很多業(yè)務(wù)代碼放在一個事務(wù)service方法中實現(xiàn)同一事務(wù),但若是不想則可以編碼式事務(wù)控制。
聲明式事務(wù)管理中我們用到org.springframework.transaction.interceptor.TransactionInterceptor幫我們攔截業(yè)務(wù)方法,使得我們在springMVc中調(diào)用serviece中的方法時(我們從容器拿到的Service是經(jīng)過AOP(也就是動態(tài)代理)處理的了),需要先經(jīng)過它。
需要一個容器去放置用不同ORM框架時,在TransactionInterceptor幫我們攔截后,將service增強成我們想要的模板,加入到像JdbcTempalte/SqlsessionTemplate這樣不同的模板中,以便在調(diào)用service的方法時,自動去調(diào)用相應(yīng)的數(shù)據(jù)庫框架執(zhí)行sql語句時需要的流程,如SqlsessionTemplate(它的原理跟sqlsession.getMapper相似)會自動對sqlsession進行創(chuàng)建,再進行該框架對sql語句執(zhí)行流程,這個容器可以是ProxyFactory(ProxyFactoryBean)或者其他的interceptor的實現(xiàn)類
而sqlsessionTemplate所需要的springMapConfig已經(jīng)用SqlSessionFactoryBean創(chuàng)建的SqlSessionFactory中了,sqlsessionTemplate會將SqlSessionFactory注入其中,便可以擁有需要的Dao層信息和需要的sql語句等了
在spring 1.x到2.x中我們可以使用4種配置的方式在IoC容器的配置文件種指定事務(wù)需要的元數(shù)據(jù)
.使用ProxyFactory(ProxyFactoryBean)+TransactionIntercepter
//攔截我們需要的service方法
?<bean?id="transactionInterceptor"
?????class="org.springframework.transaction.interceptor.TransactionInterceptor">
?????<property?name="transactionManager">
?????????<ref?bean="transactionManager"?/>
?????property>
?????<property?name="transactionAttributeSource">
?????????<value>
?????????????org.springframework.prospring.ticket.service.PaymentService.transfer=PROPAGATION_REQUIRED,ISOLATION_SERIALIZABLE,timeout_30,-Exception
?????????value>
?????property>
?bean>
?<bean?id="paymentServiceTarget"
?????class="org.springframework.prospring.ticket.service.PaymentServiceImpl">
?????<property?name="paymentDao">
?????????<ref?local="paymentDao"?/>
?????property>
?bean>
?
// 將需要的框架Template/相應(yīng)的dao實現(xiàn),與Service層接口以及transactionInterceptor集成一個Bean方便Client調(diào)用
?
?<bean?id="paymentService"?class="org.springframework.aop.framework.ProxyFactoryBean">
?????<property?name="target">
?????????<ref?local="paymentServiceTarget"?/>
?????property>
?????<property?name="proxyInterfaces">
?????????<value>org.springframework.prospring.ticket.service.PaymentService
?????????value>
?????property>
?????<property?name="interceptorNames">
???????<list>
?????????<value>transactionInterceptorvalue>
???????list>
?????property>
?bean><context:component-scan?base-package="cn.itcast">
????<context:exclude-filter?type="annotation"?expression="org.springframework.stereotype.Controller"/>
context:component-scan>
<bean?id="comboPooledDataSource"?class="com.mchange.v2.c3p0.ComboPooledDataSource">
????<property?name="driverClass"?value="com.mysql.jdbc.Driver"/>
????<property?name="jdbcUrl"?value="jdbc:mysql://localhost:3306/ssm"/>
????<property?name="user"?value="root"/>
????<property?name="password"?value="123456"/>
bean>
<bean?id="sqlSessionFactory"?class="org.mybatis.spring.SqlSessionFactoryBean">
????<property?name="dataSource"?ref="comboPooledDataSource"/>
bean>
<bean?id="mapperScannerConfigurer"?class="org.mybatis.spring.mapper.MapperScannerConfigurer">
????<property?name="basePackage"?value="cn.itcast.dao"/>
bean>
<bean?id="dataSourceTransactionManager"?class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
????<property?name="dataSource"?ref="comboPooledDataSource">property>
bean>
// <tx:advice>專門為聲明事務(wù)Advice而設(shè)置的配置元素,底層還是TransactionInterceptor
????若不指定<tx:attributes>,則采用DefaultTransactionDefinition
<tx:advice?id="txAdvice"?transaction-manager="dataSourceTransactionManager">
????<tx:attributes>
????????<tx:method?name="find*"?read-only="true"/>
????????<tx:method?name="*"?isolation="DEFAULT"/>
????tx:attributes>
tx:advice>
// 作用如同ProxyFactoryBean一直,不過使用了aop實現(xiàn),只需指定ServiceTarget的class,之后的便依靠動態(tài)代理實現(xiàn)
<aop:config>
????<aop:advisor?advice-ref="txAdvice"?pointcut="execution(* cn.itcast.service.impl.*ServiceImpl.*(..))"/>
aop:config>
//這個實現(xiàn)使得我們在MVC層調(diào)用的service對象已經(jīng)是經(jīng)過代理實現(xiàn)了的,我們可以直接用其中的方法,底層都會依據(jù)流程自己完成,我們會發(fā)現(xiàn),我們沒有自己操作commit的空間,他會在你事務(wù)聲明的方法結(jié)束時就commit,你可以把很多業(yè)務(wù)代碼放在一個事務(wù)service方法中實現(xiàn)同一事務(wù),但若是不想則可以編碼式事務(wù)控制。
從上面可以看出,Spring事務(wù)管理一共可分為三個步驟,分別是初始化事務(wù)、提交事務(wù)、回滾事務(wù),然后每個步驟又可細分為若干小步驟。spring事務(wù)工作流相當于為用戶屏蔽了具體orm框架的底層處理邏輯,基于spring開發(fā)的程序,即便更換了orm框架也是跟換了調(diào)用的sqlTemplate,我們的事務(wù)管理器更換成適合于他的便可,基本不用改變其他的實現(xiàn)。這是Spring的優(yōu)點
Spring控制datasourcetransaction ,mybaitis用springManagedTransaction對數(shù)據(jù)庫進行sql操作,但commit等都是最后由datasourceTransaction 進行.
<bean?id="comboPooledDataSource"?class="com.mchange.v2.c3p0.ComboPooledDataSource">
????????<property?name="driverClass"?value="com.mysql.jdbc.Driver"/>
????????<property?name="jdbcUrl"?value="jdbc:mysql://localhost:3306/ssm"/>
????????<property?name="user"?value="root"/>
????????<property?name="password"?value="123456"/>
????bean>
<bean?id="sqlSessionFactory"?class="org.mybatis.spring.SqlSessionFactoryBean">
????<property?name="dataSource"?ref="comboPooledDataSource"/>
bean>
<bean?id="mapperScannerConfigurer"?class="org.mybatis.spring.mapper.MapperScannerConfigurer">
????<property?name="basePackage"?value="cn.itcast.dao"/>
bean>
????<bean?id="dataSourceTransactionManager"?class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
????????<property?name="dataSource"?ref="comboPooledDataSource">property>
????bean>再看這個容器中的參數(shù),無論如何我們都需要SqlSessionFactoryBean(無論是否有用Spring的事務(wù)管理都需要)和DataSourceTransactionManager
由SqlSessionFactoryBean創(chuàng)建工廠,此工廠將生成SpringManagedTransactionFactory(可能有人會好奇為什么要大費周章重新實現(xiàn)一個TransactionFactory,到下面進入sqlsession的部分就可以知道答案了),再封裝成 Environment 對象。最后封裝成configuration對象來調(diào)用sqlSessionFactoryBuilder.build(configuration);生成sqlsqlSessionFactory,由XMLConfigBuilder讀取parse成一個sqlsqlSessionFactory
交由SqlsessionTemplate使用(其實現(xiàn)了?
SqlSession?接口,并不直接調(diào)用具體的?SqlSession?的方法,而是委托給一個動態(tài)代理,通過代理?SqlSessionInterceptor?對方法調(diào)用進行攔截,用調(diào)用接口的方法時,會去尋找是否有了停留在resources中的未關(guān)閉的sqlsession)若是編程式事務(wù)控制的話我們應(yīng)該先創(chuàng)建SqlSessionFactoryBean創(chuàng)建sqlsqlSessionFactory,再將其注入自己創(chuàng)建的SqlsessionTemplate對象中,再利用SqlsessionTemplate進行增刪查改
public?class?SqlSessionFactoryBean?implements?FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
protected?SqlSessionFactory buildSqlSessionFactory()?throws?IOException {
????XMLConfigBuilder xmlConfigBuilder = null;
????Configuration configuration;
????···
????各種configuration的參數(shù)判斷
????····
?????//生成SpringManagedTransactionFactory
????if?(this.transactionFactory == null) {
????????this.transactionFactory = new?SpringManagedTransactionFactory();
???????
????}
????
??//封裝成 Environment 對象
??//封裝成configuration對象
????configuration.setEnvironment(new?Environment(this.environment, this.transactionFactory, this.dataSource));
????if?(!ObjectUtils.isEmpty(this.mapperLocations)) {
????????Resource[] var29 = this.mapperLocations;
????????var27 = var29.length;
????????for(var5 = 0; var5 < var27; ++var5) {
????????????Resource mapperLocation = var29[var5];
????????????if?(mapperLocation != null) {
????????????????try?{
????????????????????XMLMapperBuilder xmlMapperBuilder = new?XMLMapperBuilder(mapperLocation.getInputStream(), configuration, mapperLocation.toString(), configuration.getSqlFragments());
????????????????????xmlMapperBuilder.parse();
????????????????} catch?(Exception var20) {
????????????????????throw?new?NestedIOException("Failed to parse mapping resource: '"?+ mapperLocation + "'", var20);
????????????????} finally?{
????????????????????ErrorContext.instance().reset();
????????????????}
????????????????if?(LOGGER.isDebugEnabled()) {
????????????????????LOGGER.debug("Parsed mapper file: '"?+ mapperLocation + "'");
????????????????}
????????????}
????????}
????} else?if?(LOGGER.isDebugEnabled()) {
????????LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
????}
??//調(diào)用sqlSessionFactoryBuilder.build(configuration);生成sqlsqlSessionFactory,
????return?this.sqlSessionFactoryBuilder.build(configuration);
}
}若要使用spring的事務(wù),使用sqlsessionTemplate前,我們要分析DataSourceTransactionManager整個流程,我們需要一步一步來,從最頂層開始
Spring的PlatformTransactionManager是事務(wù)管理的頂層接口,其中定義的三個方法對應(yīng)的就是初始化事務(wù)、提交事務(wù)、回滾事務(wù),,然后AbstractPlatformTransactionManager抽象類(作為模板)給出了三個步驟的具體實現(xiàn)。
但對諸如doGetTransaction()之類的和doBegin等還是弄成了抽象方法由DataSourceTransactionManager等實現(xiàn),以便自己決定對 TransactionSynchronization接口的實現(xiàn)類進行調(diào)用,自己實現(xiàn)線程安全、獲得ConnectionHolder和創(chuàng)建新事務(wù)時按何種方式dobgin,連接方式和注入各種參數(shù)。
public?abstract?class?AbstractPlatformTransactionManager?implements?PlatformTransactionManager, Serializable?{
public?final?TransactionzhuangtStatus getTransaction(@Nullable TransactionDefinition definition)?throws?TransactionException {
//在DataSourceTransactionManager中重寫了的類,查詢當前線程中是否有傳入的datasource對應(yīng)的ConnectionHolder,有則依據(jù)他創(chuàng)建transaction
?Object transaction = this.doGetTransaction();
?boolean?debugEnabled = this.logger.isDebugEnabled();
?if?(definition == null) {
?????definition = new?DefaultTransactionDefinition();
?}
?if?(this.isExistingTransaction(transaction)) {
?????return?this.handleExistingTransaction((TransactionDefinition)definition, transaction, debugEnabled);
?}
···
各種條件判斷
···
?????try?{
?????????boolean?newSynchronization = this.getTransactionSynchronization() != 2;
?????????
?????????//被創(chuàng)建的事務(wù)狀態(tài)對象類型是DefaultTransactionStatus,它持有上述創(chuàng)建的事務(wù)對象。事務(wù)狀態(tài)對象主要用于獲取當前事務(wù)對象的狀態(tài),比如事務(wù)是否被標記了回滾,是否是一個新事務(wù)等等。
?????????DefaultTransactionStatus status = this.newTransactionStatus((TransactionDefinition)definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
?????????
?????????//事務(wù)開始處理,進行促使化,連接獲取
?????????this.doBegin(transaction, (TransactionDefinition)definition);
?????????
?????????//對象都交由TransactionSynchronizationManager管理,TransactionSynchronizationManager把這些對象都保存在ThreadLocal中。在該transaction未commit/關(guān)閉前,該線程就會與該connection一直綁定在一起,通過只能同時綁定一個connection的原理,實現(xiàn)事務(wù)管理。
?????????
?????????this.prepareSynchronization(status, (TransactionDefinition)definition);
?????????
?????????返回事務(wù)狀態(tài)對象:(主要進行:查詢事務(wù)狀態(tài)、通過setRollbackOnly()方法標記當前事務(wù)使其回滾,根據(jù)事務(wù)參數(shù)創(chuàng)建內(nèi)嵌事務(wù)),statu的意思是方便先對事務(wù)進行判斷再獲取transaction進行操作。
?????????return?status;
?????} catch?(Error | RuntimeException var7) {
?????????this.resume((Object)null, suspendedResources);
?????????throw?var7;
?????}
?}
}
}DataSourceTransactionManager(繼承了AbstractPlatformTransactionManager)的主要作用是創(chuàng)建transaction和對transaction的初始化
public?class?DataSourceTransactionManager?extends?AbstractPlatformTransactionManager
????implements?ResourceTransactionManager, InitializingBean?{
@Override//主要增加了對TransactionSynchronizationManager中當前線程是否有connectionHolder
??protected?Object doGetTransaction()?{
??
????// 創(chuàng)建事務(wù)對象
????
????DataSourceTransactionObject txObject = new?DataSourceTransactionObject();
????txObject.setSavepointAllowed(isNestedTransactionAllowed());
????ConnectionHolder conHolder =
????????(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
????//ConnectionHolder賦值給事務(wù)對象txObject
????txObject.setConnectionHolder(conHolder, false);
????return?txObject;
????}
}
//處理事務(wù)開始的方法
????protected?void?doBegin(Object transaction, TransactionDefinition definition)?{
????????DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
????????Connection con = null;
????????try?{
????????????//如果數(shù)據(jù)源事務(wù)對象的ConnectionHolder為null或者是事務(wù)同步的
????????????if?(txObject.getConnectionHolder() == null?||
????????txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
????????????????//獲取當前數(shù)據(jù)源的數(shù)據(jù)庫連接
????????????????Connection newCon = this.dataSource.getConnection();
????????????????if?(logger.isDebugEnabled()) {
????????????????????logger.debug("Acquired Connection ["?+ newCon + "] for JDBC transaction");
????????????????}
????????????????//為數(shù)據(jù)源事務(wù)對象設(shè)置ConnectionHolder
????????????????txObject.setConnectionHolder(new?ConnectionHolder(newCon), true);
????????????}
????//設(shè)置數(shù)據(jù)源事務(wù)對象的事務(wù)同步 txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
????????????//獲取數(shù)據(jù)源事務(wù)對象的數(shù)據(jù)庫連接
????????????con = txObject.getConnectionHolder().getConnection();
????????????//根據(jù)數(shù)據(jù)連接和事務(wù)屬性,獲取數(shù)據(jù)庫連接的事務(wù)隔離級別
????????????Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
????//為數(shù)據(jù)源事務(wù)對象設(shè)置事務(wù)隔離級別
????txObject.setPreviousIsolationLevel(previousIsolationLevel);
????????????//如果數(shù)據(jù)庫連接設(shè)置了自動事務(wù)提交屬性,則關(guān)閉自動提交
????????????if?(con.getAutoCommit()) {
????????????????//保存數(shù)據(jù)庫連接設(shè)置的自動連接到數(shù)據(jù)源事務(wù)對象中
????????????????txObject.setMustRestoreAutoCommit(true);
????????????????if?(logger.isDebugEnabled()) {
????????????????????logger.debug("Switching JDBC Connection ["?+ con + "] to manual commit");
????????????????}
????????????????//設(shè)置數(shù)據(jù)庫連接自動事務(wù)提交屬性為false,即禁止自動事務(wù)提交
????????????????con.setAutoCommit(false);
????????????}
????????????//激活當前數(shù)據(jù)源事務(wù)對象的事務(wù)配置
????????????txObject.getConnectionHolder().setTransactionActive(true);
????????????//獲取事務(wù)配置的超時時長
int?timeout = determineTimeout(definition);
//如果事務(wù)配置的超時時長不等于事務(wù)的默認超時時長
????????????if?(timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
????????//數(shù)據(jù)源事務(wù)對象設(shè)置超時時長
????????txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
????????????}
????????????//把當前數(shù)據(jù)庫Connection和線程ThreadLocal綁定(key為DataSource,value為getConnectionHolder)spring事務(wù)管理的關(guān)鍵一步
????????????if?(txObject.isNewConnectionHolder()) {
????????TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
????????????}
????????}
????????catch?(Exception ex) {
????????????DataSourceUtils.releaseConnection(con, this.dataSource);
????????????throw?new?CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
????????}
????}此時若是注釋運行的便需要把PlatformTransactionManager,他生成的TransactionStatus、TransactionAttribute等封裝到TransactionInfo中以便注解能自己調(diào)用
完成了對事務(wù)的創(chuàng)建,并將其用TransactionSynchronizationManager綁定到了thread local上,接著便是對sqlsessionTemplate的調(diào)用了即對mybatis框架的sqlsession的整合了
Spring+mybatis的事務(wù)控制流程

SqlsessionTemplate(如同sqlsession.getmapper獲得的代理對象)的使用并不是直接用sqlsession進行增刪查改(實際上是sqlsession的一個代理類,其實現(xiàn)了?
SqlSession?接口,并不直接調(diào)用具體的?SqlSession?的方法,而是委托給一個動態(tài)代理,通過代理?SqlSessionInterceptor?對方法調(diào)用進行攔截,用調(diào)用接口的方法時,會去尋找是否有了停留在resources中的未關(guān)閉的sqlsession)public?class?SqlSessionTemplate?implements?SqlSession, DisposableBean?{
public?SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator)?{
????Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
????Assert.notNull(executorType, "Property 'executorType' is required");
????this.sqlSessionFactory = sqlSessionFactory;
????this.executorType = executorType;
????this.exceptionTranslator = exceptionTranslator;
????this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new?Class[]{SqlSession.class}, new?SqlSessionTemplate.SqlSessionInterceptor());
}
// 動態(tài)代理中的 InvocationHandler類,真正對方法的前后進行通知的類,代理類對象只是提供一個目標對象的封裝以調(diào)用而已
?private?class?SqlSessionInterceptor?implements?InvocationHandler?{
????????private?SqlSessionInterceptor()?{
????????}
????????public?Object invoke(Object proxy, Method method, Object[] args)?throws?Throwable {
????????
????????????//查看是否有為關(guān)閉的閑置的同個配置的sqlsession,有則調(diào)用,這也是你用sqlsessionTemplate代理執(zhí)行增刪查改一直是同一個sqlsession完成事務(wù)管理的原因,沒有則生成。我們可以看到是由SqlSessionUtils實現(xiàn)的
????????????
????SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
????????????Object unwrapped;
????????????try?{
????????????????Object result = method.invoke(sqlSession, args);
????????????????if?(!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
????????????????//查詢是否自動提交
????????????????//若是自動則為一條sql一次commit,是為新手設(shè)置的,一般手動提交。
????????????????????sqlSession.commit(true);
????????????????}
????????????????unwrapped = result;
????????????} catch?(Throwable var11) {
????????????????unwrapped = ExceptionUtil.unwrapThrowable(var11);
????????????????if?(SqlSessionTemplate.this.exceptionTranslator != null?&& unwrapped instanceof?PersistenceException) {
????????????????????SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
????????????????????sqlSession = null;
????????????????????Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
????????????????????if?(translated != null) {
????????????????????????unwrapped = translated;
????????????????????}
????????????????}
????????????????throw?(Throwable)unwrapped;
????????????} finally?{
????????????
????????????//關(guān)閉sqlsession,同一個事務(wù)可以有多個sqlsession
????????????
????????????????if?(sqlSession != null) {
????????????????????SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
????????????????}
????????????}
????????????return?unwrapped;
????????}
????}
}我們來看下SqlSessionUtils的實現(xiàn),這里的sqlsession的創(chuàng)建需要注意transactionFactory是SpringManageTransactionFactory。
public?final?class?SqlSessionUtils?{
??????public?static?SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator)?{
????????Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
????????Assert.notNull(executorType, "No ExecutorType specified");
????????
????????//有相應(yīng)的sqlsessionHolder則取出里面的SqlSession
????????
????????SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
????????SqlSession session = sessionHolder(executorType, holder);
????????if?(session != null) {
????????????return?session;
????????} else?{
????????????if?(LOGGER.isDebugEnabled()) {
????????????????LOGGER.debug("Creating a new SqlSession");
????????????}
??
??????//沒有則用工廠創(chuàng)建sqlsession,注意:此時的sqlsessionfactory是sqlsessionfactoryBean創(chuàng)建的,即他的configuration中的Enviroment中的transactionFactory是SpringManageTransactionFactory。
??????
????????????session = sessionFactory.openSession(executorType);
????????????
???????????// 注冊sqlsession到TransactionSyncronizedMager,
???????????
???????????
????????????registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
????????????return?session;
????????}
????}
}關(guān)于SqlSessionUtils中的registerSessionHolder方法,注冊sqlsession到TransactionSyncronizedMager中,
**包含 **
bindResource(sessionFactory, holder);
registerSynchronization(new SqlSessionUtils.SqlSessionSynchronization(holder, sessionFactory));兩步private?static?void?registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session)?{
????if?(TransactionSynchronizationManager.isSynchronizationActive()) {
????
??????//從sessionFactory中獲取Environment
??????
????????Environment environment = sessionFactory.getConfiguration().getEnvironment();
????????
????????//判斷environment中封裝的TransactionFactorySpringManagedTransactionFactory,是否用到了Spring的事務(wù)管理服務(wù),sqlsession中對TransactionSyncronizedMager的應(yīng)用只有與Spring集成時才用到,mybatis自身的事務(wù)管理并不會用到。
????????
????????if?(environment.getTransactionFactory() instanceof?SpringManagedTransactionFactory) {
????????????if?(LOGGER.isDebugEnabled()) {
????????????????LOGGER.debug("Registering transaction synchronization for SqlSession ["?+ session + "]");
????????????}
????????????SqlSessionHolder holder = new?SqlSessionHolder(session, executorType, exceptionTranslator);
????????????
????????????//綁定、注冊到TransactionSynchronizationManager
????????????TransactionSynchronizationManager.bindResource(sessionFactory, holder);
????????????TransactionSynchronizationManager.registerSynchronization(new?SqlSessionUtils.SqlSessionSynchronization(holder, sessionFactory));
????????????
????????????holder.setSynchronizedWithTransaction(true);
????????????holder.requested();
????????} else?{
????????????if?(TransactionSynchronizationManager.getResource(environment.getDataSource()) != null) {
????????????????throw?new?TransientDataAccessResourceException("SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
????????????}
????????????if?(LOGGER.isDebugEnabled()) {
????????????????LOGGER.debug("SqlSession ["?+ session + "] was not registered for synchronization because DataSource is not transactional");
????????????}
????????}
????} else?if?(LOGGER.isDebugEnabled()) {
????????LOGGER.debug("SqlSession ["?+ session + "] was not registered for synchronization because synchronization is not active");
????}
}其中SqlSessionSynchronization是一個事務(wù)生命周期的callback接口,mybatis-spring通過SqlSessionSynchronization在事務(wù)提交和回滾前分別調(diào)用DefaultSqlSession.commit()和DefaultSqlSession.rollback()
private?static?final?class?SqlSessionSynchronization?extends?TransactionSynchronizationAdapter?{
public?void?beforeCommit(boolean?readOnly)?{
????????????if?(TransactionSynchronizationManager.isActualTransactionActive()) {
????????????????try?{
????????????????????if?(SqlSessionUtils.LOGGER.isDebugEnabled()) {
????????????????????????SqlSessionUtils.LOGGER.debug("Transaction synchronization committing SqlSession ["?+ this.holder.getSqlSession() + "]");
????????????????????}
????????????????????this.holder.getSqlSession().commit();
????????????????} catch?(PersistenceException var4) {
????????????????????if?(this.holder.getPersistenceExceptionTranslator() != null) {
????????????????????????DataAccessException translated = this.holder.getPersistenceExceptionTranslator().translateExceptionIfPossible(var4);
????????????????????????if?(translated != null) {
????????????????????????????throw?translated;
????????????????????????}
????????????????????}
????????????????????throw?var4;
????????????????}
????????????}
????????}
????????public?void?beforeCompletion()?{
????????????if?(!this.holder.isOpen()) {
????????????????if?(SqlSessionUtils.LOGGER.isDebugEnabled()) {
????????????????????SqlSessionUtils.LOGGER.debug("Transaction synchronization deregistering SqlSession ["?+ this.holder.getSqlSession() + "]");
????????????????}
????????????????TransactionSynchronizationManager.unbindResource(this.sessionFactory);
????????????????this.holderActive = false;
????????????????if?(SqlSessionUtils.LOGGER.isDebugEnabled()) {
????????????????????SqlSessionUtils.LOGGER.debug("Transaction synchronization closing SqlSession ["?+ this.holder.getSqlSession() + "]");
????????????????}
????????????????this.holder.getSqlSession().close();
????????????}
????????}
????????public?void?afterCompletion(int?status)?{
????????????if?(this.holderActive) {
????????????????if?(SqlSessionUtils.LOGGER.isDebugEnabled()) {
????????????????????SqlSessionUtils.LOGGER.debug("Transaction synchronization deregistering SqlSession ["?+ this.holder.getSqlSession() + "]");
????????????????}
????????????????TransactionSynchronizationManager.unbindResourceIfPossible(this.sessionFactory);
????????????????this.holderActive = false;
????????????????if?(SqlSessionUtils.LOGGER.isDebugEnabled()) {
????????????????????SqlSessionUtils.LOGGER.debug("Transaction synchronization closing SqlSession ["?+ this.holder.getSqlSession() + "]");
????????????????}
????????????????this.holder.getSqlSession().close();
????????????}
????????????this.holder.reset();
????????}一番周折終于拿到了sqlsession到了代理類SqlsessionTemplate中,接著便是目標代碼本身(sql語句)的執(zhí)行了
在此之前我們談及為何要專門用SpringManagedTransaction,現(xiàn)在就要揭曉了,之前我們創(chuàng)建了sqlsession到了SpringTemplate,但是還未獲取connection,所以我們執(zhí)行語句前就要獲取connection,我們用mybatis的Executor執(zhí)行語句,會在prepareStatement中獲得connection
protected?Connection getConnection(Log statementLog)?throws?SQLException {
????Connection connection = this.transaction.getConnection();
????return?statementLog.isDebugEnabled() ? ConnectionLogger.newInstance(connection, statementLog, this.queryStack) : connection;
}看到上面的transsaction便可以知道我們需要一個transaction來實現(xiàn)連接的獲取,但是我們的連接是之前綁定到了ThreadLocal中了,我們Spring事務(wù)管理的關(guān)鍵要讓此時Thread中的connection用上,這時候我們就需要用到DataSourceUtils來獲取了
datasourceUtils:也是從TransactionSynchronizationManager獲取connection
SqlSessionUtils從TransactionSynchronizationManager獲取ConnectionHolder交給SpringManagedTransaction,并作為參數(shù)封裝進Executor,最后封裝進DefaultSqlSession,將Spring的事務(wù)管理與它的數(shù)據(jù)訪問框架是緊密結(jié)合的
SpringManagedTransaction中保留由datasource等信息,而且它特別實現(xiàn)了對SqlSessionUtils的調(diào)用(其他的jdbcTransaction和managedTransaction都沒有對SqlSessionUtils調(diào)用的方法,這兩者只適用于mybaitis自身事務(wù)情況,不適用于集成)
并且可以用來給datasourceUtils提供參數(shù),讓其可以直接從TransactionSynchronizationManager中調(diào)用相應(yīng)的connection,因為是同一線程且事務(wù)沒結(jié)束所以能保證是同一個了
SpringManagedTransaction的意義便在此,我們的問題也就解決了,最后便是jdbc.connection的excute操作了
public?abstract?class?DataSourceUtils?{
public?static?Connection doGetConnection(DataSource dataSource)?throws?SQLException {
Assert.notNull(dataSource, "No DataSource specified");
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if?(conHolder != null?&& (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
???conHolder.requested();
???if?(!conHolder.hasConnection()) {
??????logger.debug("Fetching resumed JDBC Connection from DataSource");
??????conHolder.setConnection(fetchConnection(dataSource));
???}
???return?conHolder.getConnection();
}
// Else we either got no holder or an empty thread-bound holder here.
logger.debug("Fetching JDBC Connection from DataSource");
Connection con = fetchConnection(dataSource);
if?(TransactionSynchronizationManager.isSynchronizationActive()) {
???logger.debug("Registering transaction synchronization for JDBC Connection");
???// Use same Connection for further JDBC actions within the transaction.
???// Thread-bound object will get removed by synchronization at transaction completion.
???ConnectionHolder holderToUse = conHolder;
???if?(holderToUse == null) {
??????holderToUse = new?ConnectionHolder(con);
???}
???else?{
??????holderToUse.setConnection(con);
???}
???holderToUse.requested();
???TransactionSynchronizationManager.registerSynchronization(
?????????new?ConnectionSynchronization(holderToUse, dataSource));
???holderToUse.setSynchronizedWithTransaction(true);
???if?(holderToUse != conHolder) {
??????TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
???}
}
return?con;
}可以看到mybatis-spring處理事務(wù)的主要流程和spring jdbc處理事務(wù)并沒有什么區(qū)別,都是通過DataSourceTransactionManager的getTransaction(), rollback(), commit()完成事務(wù)的生命周期管理,而且jdbc connection的創(chuàng)建也是通過DataSourceTransactionManager.getTransaction()完成,mybatis并沒有參與其中,mybatis只是在執(zhí)行sql時通過DataSourceUtils.getConnection()獲得當前thread的jdbc connection,然后在其上執(zhí)行sql。
最后寫一下commit的流程(rollback實現(xiàn)差不多,回到保存的回調(diào)點而已)
調(diào)用DataSourceTransactionManager的父類AbstractPlatformTransactionManager的commit
public?abstract?class?AbstractPlatformTransactionManager?implements?PlatformTransactionManager, Serializable?{
?
?public?final?void?commit(TransactionStatus status)?throws?TransactionException {
?????···
?????status 是否已完成或是否需要rollback
????···
????????//正常流程會調(diào)用processCommit
????????????this.processCommit(defStatus);
????????}
????????}
????????}
??}
```private?void?processCommit(DefaultTransactionStatus status)?throws?TransactionException {
????try?{
????????boolean?beforeCompletionInvoked = false;
????????try?{
????????????boolean?unexpectedRollback = false;
????????????//commit準備
????????????this.prepareForCommit(status);
????????????
????????????//BeforeCommit:commit前對springTransaciton、sqlsession的commit,大部分未實現(xiàn)這個的功能
????????????
????????????this.triggerBeforeCommit(status);
????????????
????????????//BeforeCompletion:Complete前對springTransaciton、sqlsession的commit
????????????
????????????this.triggerBeforeCompletion(status);
????????????beforeCompletionInvoked = true;
????????????···
????????????是否有記錄回調(diào)點/是否需要回調(diào)
????????????···
????????????// 在DataSourceTransactionManager中實現(xiàn)對connection的commit
????????????this.doCommit(status);
????????????···
???????????異常處理
????????????···
????
????????try?{
??????????
??????????//對TransactionSynchronizationManager中的synchronizations用Iterator計數(shù)器遍歷刪除
????????????this.triggerAfterCommit(status);
????????????
????????} finally?{
????????
????????// 若上一步?jīng)]清理完成,則再次清理一次,用synchronizations用Iterator計數(shù)器遍歷刪除
????????????this.triggerAfterCompletion(status, 0);
????????????
????????}
????} finally?{
????
????????// 清空TransactionSynchronizationManager,若有掛起的事務(wù),則恢復(fù)執(zhí)行
????????this.cleanupAfterCompletion(status);
????}
}// 清空TransactionSynchronizationManager,若有掛起的事務(wù),則恢復(fù)執(zhí)行,這和definiton設(shè)置的事務(wù)級別有關(guān)
private?void?cleanupAfterCompletion(DefaultTransactionStatus status) {
????????status.setCompleted();
????????if?(status.isNewSynchronization()) {
????????????TransactionSynchronizationManager.clear();
????????}
????????if?(status.isNewTransaction()) {
????????????this.doCleanupAfterCompletion(status.getTransaction());
????????}
????????if?(status.getSuspendedResources() != null) {
????????????if?(status.isDebug()) {
????????????????this.logger.debug("Resuming suspended transaction after completion of inner transaction");
????????????}
????????????Object transaction = status.hasTransaction() ? status.getTransaction() : null;
????????????
????????????//SuspendedResourcesHolder用靜態(tài)方法放置在JVM的方法區(qū),可以直接調(diào)用
????????????
????????????this.resume(transaction, (AbstractPlatformTransactionManager.SuspendedResourcesHolder)status.getSuspendedResources());
????????}這便是整個spring與mybatis集成的事務(wù)控制了。
這里提個建議,如果有好的借鑒理解的書或其他資源要參照著學(xué)習(xí)會效果更好,我寫完后發(fā)現(xiàn)Spring揭秘一書中有許多細節(jié)仍是需要注意,對于Spring的講解也更有系統(tǒng),可惜自己一直只顧著看源碼忘了


??? ?
感謝點贊支持下哈?
