1. 程式人生 > >分散式事務操作之Spring+JTA

分散式事務操作之Spring+JTA

  什麼是分散式事務?在網上找了一段比較容易理解的"定義".

     分散式事務是指事務的參與者、支援事務的伺服器、資源管理器以及事務管理器分別位於分佈系統的不同節點之上,在兩個或多個網路計算機資源上訪問並且更新資料,將兩個或多個網路計算機的資料進行的多次操作作為一個整體進行處理。如不同銀行賬戶之間的轉賬。

     對於在專案中接觸到JTA,大部分的原因是因為在專案中需要操作多個數據庫,同時,可以保證操作的原子性,保證對多個數據庫的操作一致性。

     在正式的專案中應該用springMVC(struts)+spring+hibernate(jpa)+jta,目前,先用spring+jta來完成基本的測試框架。下面我們看看程式碼

    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-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx 
           http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

    <!-- jotm 本地例項 -->
    <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean" />


    <!-- JTA事務管理器 -->
    <bean id="txManager"
        class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="userTransaction" ref="jotm"></property>
    </bean>

    <!-- XAPool配置,內部包含了一個XA資料來源,對應sshdb資料庫 -->
    <bean id="db1" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource"
        destroy-method="shutdown">
        <property name="dataSource">
            <!-- 內部XA資料來源 -->
            <bean class="org.enhydra.jdbc.standard.StandardXADataSource"
                destroy-method="shutdown">
                <property name="transactionManager" ref="jotm" />
                <property name="driverName" value="com.mysql.jdbc.Driver" />
                <property name="url"
                    value="jdbc:mysql://192.168.1.28:3306/sshdb?useUnicode=true&characterEncoding=UTF-8" />
            </bean>
        </property>
        <property name="user" value="root" />
        <property name="password" value="123456" />
    </bean>

    <!-- 另一個XAPool配置,內部包含另一個XA資料來源,對應babasport資料庫 -->
    <bean id="db2" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource"
        destroy-method="shutdown">
        <property name="dataSource">
            <bean class="org.enhydra.jdbc.standard.StandardXADataSource"
                destroy-method="shutdown">
                <property name="transactionManager" ref="jotm" />
                <property name="driverName" value="com.mysql.jdbc.Driver" />
                <property name="url"
                    value="jdbc:mysql://192.168.1.28:3306/babasport?useUnicode=true&characterEncoding=UTF-8" />
            </bean>
        </property>
        <property name="user" value="root" />
        <property name="password" value="123456" />
    </bean>

    <!-- 配置訪問sshdb資料來源的Spring JDBC模板 -->
    <bean id="sshdbTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="db1"></property>
    </bean>

    <!-- 配置訪問babasport資料來源的Spring JDBC模板 -->
    <bean id="babasportTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="db2"></property>
    </bean>
</beans>

小注一下:spring-tx-3.2.4.jar裡面竟然沒有org.springframework.transaction.jta.JotmFactoryBean類,如果你可以選擇spring-tx-2.5.6.jar,或者自己建立一下這個類。

  接下來,看下dao層測試的程式碼

1     @Resource(name = "txManager")
 2     private JtaTransactionManager txManager;
 9 
10     protected JdbcTemplate babasport_jdbcTemplate;
11 
12     /**
13      * sshdb sql jdbcTemplate
14      */
15     protected JdbcTemplate ssh_jdbcTemplate;
16 
17     /**
18      * babasport sql jdbcTemplate
19      * 
20      * @return
21      */
22     public JdbcTemplate getBabasport_jdbcTemplate() {
23         return babasport_jdbcTemplate;
24     }
25 
26     public JdbcTemplate getSsh_jdbcTemplate() {
27         return ssh_jdbcTemplate;
28     }
29 
30     @Resource(name = "babasportTemplate")
31     public void setBabasport_jdbcTemplate(JdbcTemplate babasport_jdbcTemplate) {
32         this.babasport_jdbcTemplate = babasport_jdbcTemplate;
33     }
34 
35     @Resource(name = "sshdbTemplate")
36     public void setSsh_jdbcTemplate(JdbcTemplate ssh_jdbcTemplate) {
37         this.ssh_jdbcTemplate = ssh_jdbcTemplate;
38     }
39 
40     /**
41      * 同時修改兩個資料庫的表中內容
42      * 
43      * @throws RollbackException
44      */
45     public void updateMultiple() {
46 
47         if (null == this.txManager) {
48             System.out.println("txManager為空");
49             return;
50         }
51 
52         UserTransaction userTx = this.txManager.getUserTransaction();
53         if (null == userTx) {
54             System.out.println("userTx為空");
55             return;
56         }
57 
58         try {
59             
60             userTx.begin();
61 
62             this.ssh_jdbcTemplate
63                     .execute("update wyuser set password='wangyong1' where id=8");
64 
65 
66             this.babasport_jdbcTemplate
67                     .execute("update brand set name='wangyong28' where code='14ac8d5b-d19c-40e9-97ea-d82dfbcd84c6'");
68 
69             userTx.commit();
70         } catch (Exception e) {
71             System.out.println("捕獲到異常,進行回滾" + e.getMessage());
72             e.printStackTrace();
73             try {
74                 userTx.rollback();
75             } catch (IllegalStateException e1) {
76                 System.out.println("IllegalStateException:" + e1.getMessage());
77             } catch (SecurityException e1) {
78                 System.out.println("SecurityException:" + e1.getMessage());
79             } catch (SystemException e1) {
80                 System.out.println("SystemException:" + e1.getMessage());
81             }
82             // System.out.println("sql語句操作失敗");
83         }
84     }

  如果,將後一條update語句故意寫錯,就會發現會執行rollback,同時,對上面一個語句的操作也不會生效。基本的簡單框架就是這樣。

     其實,之前也測試了下spring+jpa+jta的框架模式,卻發現,在建立model層實體類的時候會有問題,建立的entity類對映到所有的資料庫中了,於是在jpa中利用屬性<property name="packagesToScan" value="包名" />這種方式的確可以解決實體類entity的對映問題,不過貌似又出現其他問題,待研究.......