1. 程式人生 > >多資料來源配置 情況 下的事務管理

多資料來源配置 情況 下的事務管理

可以是分散式事務管理
也可以是簡單的datasourceTranctionManager.

但是我們建議不做分散式事務管理,儘量保證一個事務下只操作一個數據庫,
 保持服務的功能單一性,如果一個服務會操作到兩個資料庫中的資料,那涉及到的所有表最好放到同一個資料庫中。
 因為分散式事務管理,消耗資源嚴重,效能下降。


如果不是分散式事務管理,又有多個數據源,
我們就要配置多個事務管理器了,哈哈,每個資料來源配置一個。
這樣我們 再服務上配置使用的事務的時候,就要配置指定的事務管理器了。

此外如果我們用到了 mybatis 或者hibernate 框架的話,
sessionfactory  也是需要配置多個的。


現在我們有兩個服務a和b,分別操作一個數據庫,如果a 中 又呼叫b服務,
這時候實際上 a服務  是操作了多個數據源的,
如果要保證事務一致性,就需要 判斷 b服務的返回結果,

如果b服務中,出錯了會拋異常,那我們就try catch b 服務,並且  重新在a 中 把這個異常丟擲。
如果b服務中,始終返回結果,並且結果的狀態是錯誤的,我們就不能 trycatch了,而是判斷返回結果的狀態,
如果是錯誤的就在a 中丟擲異常。

如果是分散式事務管理,意味著專案裡有服務 需要操作多個數據庫,並且要做到事務一致性。
spring的org.springframework.transaction.jta.JtaTransactionManager,提供了分散式事務支援。如果使用WAS的JTA支援,把它的屬性改為WebSphere對應的TransactionManager。 
    在tomcat下,是沒有分散式事務的,不過可以藉助於第三方軟體jotm(Java Open Transaction Manager )和AtomikosTransactionsEssentials實現,在spring中分散式事務是通過jta(jotm,atomikos)來進行實現。 
1、http://jotm.objectweb.org/ 
2、http://www.atomikos.com/Main/TransactionsEssentials 



一、使用JOTM例子 
(1)、Dao及實現 
public interface GenericDao {  
  
    public int save(String ds, String sql, Object[] obj) throws Exception;  
      
    public int findRowCount(String ds, String sql);  
      
}  

public class GenericDaoImpl implements GenericDao{  
  
    private  JdbcTemplate jdbcTemplateA;  
    private  JdbcTemplate jdbcTemplateB;  
  
    public void setJdbcTemplateA(JdbcTemplate jdbcTemplate) {  
        this.jdbcTemplateA = jdbcTemplate;  
    }  
  
    public void setJdbcTemplateB(JdbcTemplate jdbcTemplate) {  
        this.jdbcTemplateB = jdbcTemplate;  
    }  
      
    public int save(String ds, String sql, Object[] obj) throws Exception{  
        if(null == ds || "".equals(ds)) return -1;  
        try{  
            if(ds.equals("A")){  
                return this.jdbcTemplateA.update(sql, obj);  
            }else{  
                return this.jdbcTemplateB.update(sql, obj);  
            }  
        }catch(Exception e){  
            e.printStackTrace();  
            throw new Exception("執行" + ds + "資料庫時失敗!");  
        }  
    }  
  
    public int findRowCount(String ds, String sql) {  
        if(null == ds || "".equals(ds)) return -1;  
          
        if(ds.equals("A")){  
            return this.jdbcTemplateA.queryForInt(sql);  
        }else{  
            return this.jdbcTemplateB.queryForInt(sql);  
        }  
    }  
  
}  

(2)、Service及實現 
public interface UserService {  
      
    public void saveUser() throws Exception;  
      
}  

public class UserServiceImpl implements UserService{  
  
    private GenericDao genericDao;  
      
    public void setGenericDao(GenericDao genericDao) {  
        this.genericDao = genericDao;  
    }  
  
    public void saveUser() throws Exception {  
        String userName = "user_" + Math.round(Math.random()*10000);  
        System.out.println(userName);  
          
        StringBuilder sql = new StringBuilder();  
        sql.append(" insert into t_user(username, gender) values(?,?); ");  
        Object[] objs = new Object[]{userName,"1"};  
          
        genericDao.save("A", sql.toString(), objs);  
          
        sql.delete(0, sql.length());  
        sql.append(" insert into t_user(name, sex) values(?,?); ");  
        objs = new Object[]{userName,"男的"};//值超出範圍  
        genericDao.save("B", sql.toString(), objs);  
    }  
  
}  

(3)、applicationContext-jotm.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/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd   
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">  
  
    <description>springJTA</description>  
  
    <!--指定Spring配置中用到的屬性檔案-->   
    <bean id="propertyConfig"   
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">   
        <property name="locations">   
            <list>   
                <value>classpath:jdbc.properties</value>   
            </list>   
        </property>   
    </bean>   
      
    <!-- JOTM例項 -->  
    <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean">  
          <property name="defaultTimeout" value="500000"/>  
    </bean>  
  
    <!-- JTA事務管理器 -->  
    <bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">     
        <property name="userTransaction" ref="jotm" />     
    </bean>  
  
    <!-- 資料來源A -->   
    <bean id="dataSourceA" 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="${jdbc.driver}"/>   
               <property name="url" value="${jdbc.url}"/>   
           </bean>   
       </property>   
       <property name="user" value="${jdbc.username}"/>   
       <property name="password" value="${jdbc.password}"/>   
    </bean>   
  
    <!-- 資料來源B -->   
    <bean id="dataSourceB" 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="${jdbc2.driver}"/>   
               <property name="url" value="${jdbc2.url}"/>   
           </bean>   
       </property>   
       <property name="user" value="${jdbc2.username}"/>   
       <property name="password" value="${jdbc2.password}"/>   
    </bean>   
  
    <bean id = "jdbcTemplateA"   
         class = "org.springframework.jdbc.core.JdbcTemplate">   
         <property name = "dataSource" ref="dataSourceA"/>   
    </bean>  
      
    <bean id = "jdbcTemplateB"   
         class = "org.springframework.jdbc.core.JdbcTemplate">   
         <property name = "dataSource" ref="dataSourceB"/>   
    </bean>      
  
    <!-- 事務切面配置 -->   
    <aop:config>   
        <aop:pointcut id="pointCut"  
                expression="execution(* com.logcd.service..*.*(..))"/><!-- 包及其子包下的所有方法 -->  
        <aop:advisor pointcut-ref="pointCut" advice-ref="txAdvice"/>   
          
        <aop:advisor pointcut="execution(* *..common.service..*.*(..))" advice-ref="txAdvice"/>  
    </aop:config>   
  
    <!-- 通知配置 -->   
    <tx:advice id="txAdvice" transaction-manager="jtaTransactionManager">   
       <tx:attributes>   
          <tx:method name="delete*" rollback-for="Exception"/>   
          <tx:method name="save*" rollback-for="Exception"/>   
          <tx:method name="update*" rollback-for="Exception"/>   
          <tx:method name="find*" read-only="true" rollback-for="Exception"/>   
       </tx:attributes>   
    </tx:advice>   
  
    <bean id="genericDao"   
            class="com.logcd.dao.impl.GenericDaoImpl" autowire="byName">  
    </bean>  
  
    <bean id="userService"   
            class="com.logcd.service.impl.UserServiceImpl" autowire="byName">  
    </bean>  
  
</beans>  

(4)、測試 
public class TestUserService{  
  
    private static UserService userService;  
      
    @BeforeClass  
    public static void init(){  
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext-jotm.xml");  
        userService = (UserService)app.getBean("userService");  
    }  
      
    @Test  
    public void save(){  
        System.out.println("begin...");  
        try{  
            userService.saveUser();  
        }catch(Exception e){  
            System.out.println(e.getMessage());  
        }  
        System.out.println("finish...");  
    }  
      
}  

二、關於使用atomikos實現 
(1)、資料來源配置 
<bean id="dataSourceA" class="com.atomikos.jdbc.SimpleDataSourceBean" init-method="init" destroy-method="close">  
    <property name="uniqueResourceName">  
        <value>${datasource.uniqueResourceName}</value>  
    </property>  
    <property name="xaDataSourceClassName">   
        <value>${database.driver_class}</value>   
    </property>   
    <property name="xaDataSourceProperties">  
        <value>URL=${database.url};user=${database.username};password=${database.password}</value>   
    </property>   
    <property name="exclusiveConnectionMode">   
        <value>${connection.exclusive.mode}</value>   
    </property>  
    <property name="connectionPoolSize">   
        <value>${connection.pool.size}</value>  
    </property>  
    <property name="connectionTimeout">  
        <value>${connection.timeout}</value>  
    </property>  
    <property name="validatingQuery">   
        <value>SELECT 1</value>   
    </property>   
</bean>  

(2)、事務配置 
<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="${transaction.timeout}"/>   
</bean>  
  
<!-- JTA事務管理器 -->   
<bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">   
    <property name="transactionManager" ref="atomikosTransactionManager"/>   
    <property name="userTransaction" ref="atomikosUserTransaction"/>   
</bean>  
  
<!-- 事務切面配置 -->   
<aop:config>   
    <aop:pointcut id="serviceOperation"  expression="execution(* *..service*..*(..))"/>   
    <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/>   
</aop:config>  
  
<!-- 通知配置 -->  
<tx:advice id="txAdvice" transaction-manager="springTransactionManager">   
    <tx:attributes>  
        <tx:method name="*" rollback-for="Exception"/>   
    </tx:attributes>   
</tx:advice>