起因:
小項目,沒有用分布式,但要操作兩個數據庫。本以為隨便用spring配置兩個數據源就搞定,查詢是沒問題,問題是有一個數據庫老是插不進數據。Google狂搜之后,大概了解到是事務控制的問題。我用的是spring的聲明式事務管理(<tx:annotation-driven/>)。用一般的數據源配置,只有一個數據源的事務生效,其它數據源只能讀不能寫。
有帖子說,要支持多數據源的事務,只能用JTA事務管理(沒用過 -_-||),而且應用服務器還不能是Tomcat(一直在用tomcat,不想換-_-!!),頭疼了。幸虧后面還有說,有第三方的實現支持JTA事務管理,一是JOTM,一是Atomikos。只要用了其中一個,還能繼續用Tomcat。因為名字短,先考慮用JOTM。到官網一看,最后更新日期是2010年。。呃。。轉向Atomikos。在Atomikos的官網看看文檔,看看例子,邊做邊調試,一個下午下來,總算有點成果,高興之余做個記錄。其中會涉及到一些概念,比如分布式事務、JTA、XA,我都有搜來了解一下,因理解膚淺沒法做記錄。下面只是記錄
Atomikos TransactionsEssentials 是一個為Java平台提供增值服務的並且開源類事務管理器,以下是包括在這個開源版本中的一些功能:
l 全面崩潰 / 重啟恢復
l 兼容標准的SUN公司JTA API
l 嵌套事務
l 為XA和非XA提供內置的JDBC適配器
注釋:XA:XA協議由Tuxedo首先提出的,並交給X/Open組織,作為資源管理器(數據庫)與事務管理器的接口標准。目前,Oracle、Informix、DB2和Sybase等各大數據庫廠家都提供對XA的支持。XA協議采用兩階段提交方式來管理分布式事務。XA接口提供資源管理器與事務管理器之間進行通信的標准接口。XA協議包括兩套函數,以xa_開頭的及以ax_開頭的。
以下的函數使事務管理器可以對資源管理器進行的操作:
1)xa_open,xa_close:建立和關閉與資源管理器的連接。
2)xa_start,xa_end:開始和結束一個本地事務。
3)xa_prepare,xa_commit,xa_rollback:預提交、提交和回滾一個本地事務。
4)xa_recover:回滾一個已進行預提交的事務。
5)ax_開頭的函數使資源管理器可以動態地在事務管理器中進行注冊,並可以對XID(TRANSACTION IDS)進行操作。
6)ax_reg,ax_unreg;允許一個資源管理器在一個TMS(TRANSACTION MANAGER SERVER)中動態注冊或撤消注冊。
l 內置的JMS適配器XA-capable JMS隊列連接器
注釋:JMS:jms即Java消息服務(Java Message Service)應用程序接口是一個Java平台中關於面向消息中間件(MOM)的API,用於在兩個應用程序之間,或分布式系統中發送消息,進行異步通信。Java消息服務是一個與具體平台無關的API,絕大多數MOM提供商都對JMS提供支持。
l 通過XA API兼容第三方適配器
l 更好的整合您的項目
l 集成Hibernate
1、依賴包
Atomikos的:
transactions-jdbc
transactions-jta
transactions-api
transactions
atomikos-utils
還有一個不要忘了,是jta的包。
用maven要簡單一點,只需要加入兩個依賴:
- <dependency>
- <groupId>com.atomikos</groupId>
- <artifactId>transactions-jdbc</artifactId>
- <version>3.7.0</version>
- </dependency>
- <dependency>
- <groupId>javax.transaction</groupId>
- <artifactId>jta</artifactId>
- <version>1.1</version>
- </dependency>
2、配置數據源
這一步是比較重要的。要用AtomikosDataSourceBean,而不是以前用的連接池如dbcp。最好也用XA(這東西我還不太懂),注意jdbc的鏈接地址和登陸賬號與普通連接池的配置的格式不一樣。下面是一個mysql數據庫的配置舉例:
- <bean id="dataSource1" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
- <property name="uniqueResourceName" value="ds1"/>
- <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
- <property name="xaProperties">
- <props>
- <prop key="url">jdbc:mysql://localhost/test</prop>
- <prop key="user">test</prop>
- <prop key="password">test</prop>
- </props>
- </property>
- <property name="minPoolSize" value="10" />
- <property name="maxPoolSize" value="100" />
- <property name="borrowConnectionTimeout" value="30" />
- <property name="testQuery" value="select 1" />
- <property name="maintenanceInterval" value="60" />
- </bean>
再來一個sybase的配置舉例:
- <bean id="dataSource2" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
- <property name="uniqueResourceName" value="ds2"/>
- <property name="xaDataSourceClassName" value="com.sybase.jdbc3.jdbc.SybXADataSource"/>
- <property name="xaProperties">
- <props>
- <prop key="serverName">192.168.1.10</prop>
- <prop key="portNumber">2638</prop>
- <prop key="databaseName">test</prop>
- <prop key="user">test</prop>
- <prop key="password">test</prop>
- </props>
- </property>
- <property name="minPoolSize" value="10" />
- <property name="maxPoolSize" value="100" />
- <property name="borrowConnectionTimeout" value="30" />
- <property name="testQuery" value="select 1" />
- <property name="maintenanceInterval" value="60" />
- </bean>
3、使用數據源
這一步與平時好像沒什么不一樣。我做例子的時候是用mybatis,配置如下:
- <bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean">
- <property name="dataSource" ref="dataSource1"/>
- </bean>
- <bean id="sqlSessionFactory2" class="org.mybatis.spring.SqlSessionFactoryBean">
- <property name="dataSource" ref="dataSource2"/>
- </bean>
當然,mybatis還要配置一下映射文件的自動掃描,這里與atomikos無關:
- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
- <property name="basePackage" value="xx.xx;" />
- <property name="sqlSessionFactory" ref="sqlSessionFactory1"/>
- </bean>
- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
- <property name="basePackage" value="yy.yy;" />
- <property name="sqlSessionFactory" ref="sqlSessionFactory2"/>
- </bean>
用spring JdbcTemplate應該與普通使用沒什么不同,用hibernate可能會有點不一樣,沒測試過。
4、配置jta事務管理
這是很關鍵的一步。原理我不太懂,例子如下:
- <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
- <property name="transactionManager">
- <bean class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
- <property name="forceShutdown" value="true"/>
- </bean>
- </property>
- <property name="userTransaction">
- <bean class="com.atomikos.icatch.jta.UserTransactionImp"/>
- </property>
- </bean>
當然,用spring的聲明式事務配置,再加上一行:
- <tx:annotation-driven/>
(注意,本來要配置transaction-manager屬性,如:<tx:annotation-driven transaction-manager="transactionManager"/>。這里沒有配置是因為它的默認值是transactionManager)
5、atomikos的配置文件jta.properties
這個文件一般放在根路徑吧,與log4j.properties類似。jta.properties也可命名為transactions.properties。如果不配置這個文件,項目也能啟動,因為幾乎所有配置項都有默認值。最好還是配置了,詳細配置信息請查看:http://www.atomikos.com/Documentation/JtaProperties。
6、不管是用JdbcTemplate、mybatis還是hibernate,應該都可以寫代碼來測試了。。。