<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring 與 MyBatis 事務(wù)管理源碼解析

          共 29658字,需瀏覽 60分鐘

           ·

          2020-07-31 11:18


          點擊上方藍色字體,選擇“標星公眾號”

          優(yōu)質(zhì)文章,第一時間送達


          66套java從入門到精通實戰(zhàn)課程分享

          ? 作?|?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?Classextends?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)象和鎖競爭。

          • 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>
            1. 使用“一站式”的TransactionProxyFactoryBean

            2. 使用BeanNameAutoProxyCreater

            3. 使用Spring2.x的聲明事務(wù)配置方式(我們使用的)


            <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ù)控制。



          • 我們一般通過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)

          • 從上面可以看出,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),可惜自己一直只顧著看源碼忘了


          ??? ?




          感謝點贊支持下哈?

          瀏覽 81
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人免费电影在线观看五月天婷婷 | 青青草视频大香蕉 | 欧美A∨在线观看 | 日韩欧美纯爱电影片在线观看 | 日本亚洲欧洲在线观看 |