JTA事務(wù)

JDBC和JTA事務(wù)區(qū)別
簡單的說 jta是多庫的事務(wù) jdbc是單庫的事務(wù)。

jdbc事務(wù)
JDBC事務(wù)由Connnection對象控制管理,也就是說,事務(wù)管理實際上是在JDBC Connection中實現(xiàn)。事務(wù)周期限于Connection的生命周期。java.sql.Connection提供了兩種事務(wù)模式:自動提交和手工提交。

自動提交:缺省是自動提交。一條對數(shù)據(jù)庫的更新(增/刪/改)代表一項事務(wù)操作,操作成功后,系統(tǒng)將自動調(diào)用commit()來提交,否則將調(diào)用rollback()來回滾。
手工提交:通過調(diào)用setAutoCommit(false)來禁止自動提交。這樣就可把多個數(shù)據(jù)庫操作的表達式作為一個事務(wù),在操作完成后調(diào)用commit()來進行整體提交,其中任何一個操作失敗,都不會執(zhí)行到commit(),并產(chǎn)生異常;此時可在異常捕獲時調(diào)用rollback()進行回滾,以保持多次更新操作后,相關(guān)數(shù)據(jù)的一致性,示例如下:

    try {
        conn =DriverManager.getConnection(...);
        conn.setAutoCommit(false);//禁止自動提交,設(shè)置回滾點
        stmt = conn.createStatement();
        stmt.executeUpdate(...); //數(shù)據(jù)庫更新操作1
        stmt.executeUpdate(...); //數(shù)據(jù)庫更新操作2
        conn.commit(); //事務(wù)提交
    }catch(Exception ex) {
        log.error(...);
        try {
            conn.rollback(); //操作不成功則回滾
        }catch(Exception e) {
            log.error(...);
        }
    }

JDBC 事務(wù)的一個缺點是事務(wù)的范圍局限于一個數(shù)據(jù)庫連接。一個JDBC事務(wù)不能跨越多個數(shù)據(jù)庫。也無法在通過RPC的方式調(diào)用中保證事務(wù)。

jta事務(wù)
由于JDBC無法實現(xiàn)分布式事務(wù)。例如操作不同的數(shù)據(jù)庫或MQ(MQ也可以認為是一個數(shù)據(jù)源),這時候就無法使用JDBC來管理事務(wù):

    /** 支付訂單處理 **/
    @Transactional(rollbackFor = Exception.class)
    public void completeOrder() {
        orderDao.update(); // 訂單服務(wù)本地更新訂單狀態(tài)
        accountService.update(); // 調(diào)用資金賬戶服務(wù)給資金帳戶加款
        pointService.update(); // 調(diào)用積分服務(wù)給積分帳戶增加積分
        accountingService.insert(); // 調(diào)用會計服務(wù)向會計系統(tǒng)寫入會計原始憑證
        merchantNotifyService.notify(); // 調(diào)用商戶通知服務(wù)向商戶發(fā)送支付結(jié)果通知
    }

其中調(diào)用了五個服務(wù),這五個服務(wù)都通過RPC的方式調(diào)用。雖然方法中增加了@Transactional注解,但是由于采用調(diào)用了分布式服務(wù),該事務(wù)并不能達到ACID的效果。
JTA(Java Transaction API)提供了跨數(shù)據(jù)庫連接(或其他JTA資源)的事務(wù)管理能力。JTA事務(wù)管理則由JTA容器實現(xiàn)。一個JTA事務(wù)可以有多個參與者,而一個JDBC事務(wù)則被限定在一個單一的數(shù)據(jù)庫連接。下列任一個Java平臺的組件都可以參與到一個JTA事務(wù)中:JDBC連接、JDO PersistenceManager 對象、JMS 隊列、JMS 主題、企業(yè)JavaBeans(EJB)等。

Spring+Atomikos實現(xiàn)JTA:

添加依賴:

  <!-- jta -->
  <dependency>
      <groupId>com.atomikos</groupId>
      <artifactId>transactions-jdbc</artifactId>
      <version>4.0.4</version>
  </dependency>
  <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.6.11</version>
  </dependency>

配置文件applicationContext-jta.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

        <bean id="dataSource_main" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
            <property name="uniqueResourceName" value="mysql/main" /><!-- 取名任意,但不要重復(fù)-- >
            <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
            <property name="xaProperties">
                <props>
                    <prop key="user">yourusername</prop>
                    <prop key="password">yourpswd</prop>
                    <prop key="URL">jdbc:mysql://yourdatabasehost/tmg</prop>
                </props>
            </property>
            <property name="minPoolSize" value="1" />
            <property name="maxPoolSize" value="3" />
            <property name="maxIdleTime" value="60" />
        </bean>

        <bean id="dataSource_other" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
            <property name="uniqueResourceName" value="mysql/other" /><!-- 取名任意,但不要重復(fù)-- >
            <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
            <property name="xaProperties">
                <props>
                    <prop key="user">yourusername</prop>
                    <prop key="password">yourpswd</prop>
                    <prop key="URL">jdbc:mysql://yourdatabasehost/tmg</prop>
                </props>
            </property>
            <property name="minPoolSize" value="1" />
            <property name="maxPoolSize" value="3" />
            <property name="maxIdleTime" value="60" />
        </bean>


        <!-- 配置mybitasSqlSessionFactoryBean -->
        <bean id="sqlSessionFactory_main" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource_main" />
            <property name="configLocation" value="classpath:config/mybatis.xml"/>
            <property name="mapperLocations" value="classpath*:mybatis.mappers.main.*/*.xml" />
        </bean>
        <bean id="sqlSessionFactory_other" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource_other" />
            <property name="configLocation" value="classpath:config/mybatis.xml"/>
            <property name="mapperLocations" value="classpath*:mybatis.mappers.other.*/*.xml" />
        </bean>

        <!-- 配置SqlSessionTemplate -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.tmg.dao.main" />
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory_main" />
        </bean>
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.tmg.dao.other" />
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory_other" />
        </bean>

        <!-- 下面三個Bean可以獨立使用來進行事務(wù)控制 -->
        <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
            <property name="forceShutdown" value="true" />
        </bean>

        <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
            <property name="transactionTimeout" value="3000" />
        </bean>

        <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
            <property name="transactionManager" ref="atomikosTransactionManager"/>
            <property name="userTransaction" ref="atomikosUserTransaction"/>
        </bean>

        <aop:config proxy-target-class="true">
            <aop:advisor pointcut="execution(* *com.tmg.service..*(..))" advice-ref="txAdvice" />
        </aop:config>

        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="insert*"  propagation="REQUIRED"  read-only="true" />
                <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
                <tx:method name="save*"  propagation="REQUIRED"  read-only="true" />
                <tx:method name="delete*"  propagation="REQUIRED"  read-only="true" />
                <tx:method name="del*"  propagation="REQUIRED"  read-only="true" />
                <tx:method name="update*"  propagation="REQUIRED"  read-only="true" />
            </tx:attributes>
        </tx:advice>

    </beans>

Dao層:

Service層:

    @Override
    public void addAndDel(MybtUser user) {
        userMapper_main.insert(user);
        userMapper_other.deleteByPrimaryKey(user.getId());
    }

測試:

    @Test
    public void testJta() throws Exception{
        MybtUser user = new MybtUser(5,3,"E");
        userService.addAndDel(user);
    }

把MQ也加入JTA事務(wù):

<bean id="listenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <property name="connectionFactory" ref="connectionFactory" />
    <property name="messageListener" ref="jmsQueueReceiver" />
    <property name="destination" ref="queueDestination" />
    <property name="sessionTransacted" value="true"/>
    <property name="transactionManager" ref="transactionManager"/>
</bean>

這樣消息監(jiān)聽器進行消息接收和對應(yīng)的數(shù)據(jù)庫訪問就會處于同一數(shù)據(jù)庫控制下,當消息接收失敗或數(shù)據(jù)庫訪問失敗都會進行事務(wù)回滾操作。當指定了transactionManager時,消息監(jiān)聽容器將忽略sessionTransacted的值。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,514評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,743評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,199評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,414評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,951評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,780評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,218評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,673評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內(nèi)容