多資料來源配置 情況 下的事務管理
阿新 • • 發佈:2019-01-04
可以是分散式事務管理 也可以是簡單的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>