上一篇介绍了分库分表插件,但是多库操作涉及到分布式事务问题,大家都知道分布式事务需要涉及到数据库XA驱动,oracle原本就支持,mysql在5.6版本支持了该属性,atomikos插件封装了该特性的一个中间件。现在来介绍一个atomikos的使用。
首先是jar包依赖,maven配置如下:
<dependency> <groupId>com.atomikos</groupId> <artifactId>transactions</artifactId> <version>4.0.4</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-api</artifactId> <version>4.0.4</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>atomikos-util</artifactId> <version>4.0.4</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jdbc-deprecated</artifactId> <version>3.8.0</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jta</artifactId> <version>4.0.4</version> </dependency> <dependency> <groupId>com.atomikos</groupId> <artifactId>transactions-jdbc</artifactId> <version>4.0.4</version> </dependency>
额外的还要增加jta的配置,使用jta管理spring事务:
<dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> </dependency>
然后是srping配置,由于需要使用atomikos数据源,所以对上一篇分库分表插件的数据源配置做了修改:
<?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:rdb="http://www.dangdang.com/schema/ddframe/rdb" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.dangdang.com/schema/ddframe/rdb http://www.dangdang.com/schema/ddframe/rdb/rdb.xsd" > <!-- 配置数据源 --> <bean name="dataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" destroy-method="close"> <!-- <property name="driverClassName" value="${jdbc.driverClassName}" /> --> <property name="uniqueResourceName" value="dataSource" /> <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> <property name="xaProperties"> <props> <prop key="user">${jdbc.username}</prop> <prop key="password">${jdbc.password}</prop> <prop key="url">${jdbc.url}</prop> </props> </property> <property name="minPoolSize" value="5" /> <property name="maxPoolSize" value="50" /> <property name="maxIdleTime" value="60" /> </bean> <!-- 配置数据源 --> <bean name="master0" class="com.atomikos.jdbc.AtomikosDataSourceBean" destroy-method="close"> <!-- <property name="driverClassName" value="${jdbc.driverClassName}" /> --> <property name="uniqueResourceName" value="master0" /> <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> <property name="xaProperties"> <props> <prop key="user">${jdbc.username}</prop> <prop key="password">${jdbc.password}</prop> <prop key="url">jdbc:mysql://127.0.0.1:3306/demodb00</prop> </props> </property> <property name="minPoolSize" value="5" /> <property name="maxPoolSize" value="50" /> <property name="maxIdleTime" value="60" /> </bean> <!-- 配置数据源 --> <bean name="master1" class="com.atomikos.jdbc.AtomikosDataSourceBean" destroy-method="close"> <!-- <property name="driverClassName" value="${jdbc.driverClassName}" /> --> <property name="uniqueResourceName" value="master1" /> <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> <property name="xaProperties"> <props> <prop key="user">${jdbc.username}</prop> <prop key="password">${jdbc.password}</prop> <prop key="url">jdbc:mysql://127.0.0.1:3306/demodb01</prop> </props> </property> <property name="minPoolSize" value="5" /> <property name="maxPoolSize" value="50" /> <property name="maxIdleTime" value="60" /> </bean> <rdb:strategy id="idDbSharding" sharding-columns="id" algorithm-class="com.feng.splitdbtb.DbAlgorithm"/> <rdb:strategy id="idTbSharding" sharding-columns="id" algorithm-class="com.feng.splitdbtb.TbAlgorithm"/> <rdb:data-source id="wholeDataSource"> <rdb:sharding-rule data-sources="master0,master1"> <rdb:table-rules> <rdb:table-rule logic-table="user" actual-tables="user_${0..1}" database-strategy="idDbSharding" table-strategy="idTbSharding"/> </rdb:table-rules> </rdb:sharding-rule> </rdb:data-source> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="wholeDataSource" /> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="mapperLocations" > <list> <value>classpath:com/feng/mapper/user/*.xml</value> </list> </property> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.feng.dao.user" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean> <!-- 定义事务 --> <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close"> <property name="forceShutdown"> <value>true</value> </property> </bean> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"> <property name="transactionTimeout" value="300" /> </bean> <bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManager"> <ref bean="atomikosTransactionManager" /> </property> <property name="userTransaction"> <ref bean="atomikosUserTransaction" /> </property> <property name="allowCustomIsolationLevels" value="true"/> </bean> <tx:advice id="txAdvice" transaction-manager="springTransactionManager"> <tx:attributes> <tx:method name="publish*" /> <tx:method name="save*" /> <tx:method name="add*" /> <tx:method name="update*" /> <tx:method name="insert*" /> <tx:method name="create*" /> <tx:method name="del*" /> <tx:method name="load*" /> <tx:method name="init*" /> <tx:method name="*" read-only="true"/> </tx:attributes> </tx:advice> <!-- AOP配置--> <aop:config> <aop:pointcut id="myPointcut" expression="execution(public * com.feng.service.impl.*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" /> </aop:config> <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactory" /> </bean> </beans>
运行一下,看到以下日志的兄弟,说明已经成功了,内心一阵狂热,胸膛也挺直了。
[DEBUG][2017-04-17 09:58:27,821][com.atomikos.icatch.imp.CompositeTransactionImp]addParticipant ( XAResourceTransaction: 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D313439323339343330373730353030303035:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D35 ) for transaction com.atomikos.spring.jdbc.tm149239430770500005 [DEBUG][2017-04-17 09:58:27,821][com.atomikos.datasource.xa.XAResourceTransaction]XAResource.start ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D313439323339343330373730353030303035:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D35 , XAResource.TMNOFLAGS ) on resource master1 represented by XAResource instance com.mysql.jdbc.jdbc2.optional.JDBC4MysqlXAConnection@2c7e446d [DEBUG][2017-04-17 09:58:27,840][com.atomikos.icatch.imp.CompositeTransactionImp]registerSynchronization ( com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@1f21ef16 ) for transaction com.atomikos.spring.jdbc.tm149239430770500005 [DEBUG][2017-04-17 09:58:27,841][com.atomikos.jdbc.AtomikosConnectionProxy]atomikos connection proxy for com.mysql.jdbc.jdbc2.optional.JDBC4ConnectionWrapper@2015fa5c: calling prepareStatement(INSERT INTO user_1 (id, name, age) VALUES (?, ?, ?),1003,1007,1)... [DEBUG][2017-04-17 09:58:27,866][org.mybatis.spring.SqlSessionUtils]Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@ac88402] [DEBUG][2017-04-17 09:58:27,866][org.mybatis.spring.SqlSessionUtils]Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@ac88402] [DEBUG][2017-04-17 09:58:27,866][org.mybatis.spring.SqlSessionUtils]Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@ac88402] [DEBUG][2017-04-17 09:58:27,866][org.springframework.jdbc.datasource.DataSourceUtils]Returning JDBC Connection to DataSource [DEBUG][2017-04-17 09:58:27,866][com.atomikos.jdbc.AtomikosConnectionProxy]atomikos connection proxy for com.mysql.jdbc.jdbc2.optional.JDBC4ConnectionWrapper@2015fa5c: close()... [DEBUG][2017-04-17 09:58:27,866][com.atomikos.datasource.xa.XAResourceTransaction]XAResource.end ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D313439323339343330373730353030303035:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D35 , XAResource.TMSUCCESS ) on resource master1 represented by XAResource instance com.mysql.jdbc.jdbc2.optional.JDBC4MysqlXAConnection@2c7e446d [DEBUG][2017-04-17 09:58:27,885][org.springframework.transaction.jta.JtaTransactionManager]Initiating transaction rollback [DEBUG][2017-04-17 09:58:27,887][com.atomikos.datasource.xa.XAResourceTransaction]XAResource.rollback ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D313439323339343330373730353030303035:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D35 ) on resource master1 represented by XAResource instance com.mysql.jdbc.jdbc2.optional.JDBC4MysqlXAConnection@2c7e446d [DEBUG][2017-04-17 09:58:27,906][com.atomikos.icatch.imp.CompositeTransactionImp]rollback() done of transaction com.atomikos.spring.jdbc.tm149239430770500005 [DEBUG][2017-04-17 09:58:27,907][com.atomikos.icatch.imp.CompositeTransactionImp]rollback() done of transaction com.atomikos.spring.jdbc.tm149239430770500005
最后提醒一下,业务的相关方法一定不要捕获异常,抛出来RunTimeException也不行,否则atomikos不能回滚,具体原因不明。