关于spring boot 支持分布式事务,XA是常用的一种方式。
这里把相关的配置记下,方便以后使用。
首先配置两个不同的数据源 : 订单库、持仓库。
/** * Created by zhangjunwei on 2017/8/2. */ @Configuration public class DataSourceConfig { /** * db1的 XA datasource * * @return */ @Bean(name = "symbolOrder") @Primary @Qualifier("symbolOrder") public AtomikosDataSourceBean symbolOrderBean() { AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean(); atomikosDataSourceBean.setUniqueResourceName("symbolOrder"); atomikosDataSourceBean.setXaDataSourceClassName( "com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"); Properties properties = new Properties(); properties.put("URL","jdbc:mysql://localhost:3306/datamanage"); properties.put("user", "root"); properties.put("password", "123456"); atomikosDataSourceBean.setXaProperties(properties); return atomikosDataSourceBean; } /** * db2的 XA datasource * * @return */ @Bean(name = "symbolPosition") @Qualifier("symbolPosition") public AtomikosDataSourceBean symbolPositionDataSourceBean() { AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean(); atomikosDataSourceBean.setUniqueResourceName("symbolPosition"); atomikosDataSourceBean.setXaDataSourceClassName( "com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"); Properties properties = new Properties(); properties.put("URL", "jdbc:mysql://localhost:3306/symbol_position"); properties.put("user", "root"); properties.put("password", "123456"); atomikosDataSourceBean.setXaProperties(properties); return atomikosDataSourceBean; } /** * transaction manager * * @return */ @Bean(destroyMethod = "close", initMethod = "init") public UserTransactionManager userTransactionManager() { UserTransactionManager userTransactionManager = new UserTransactionManager(); userTransactionManager.setForceShutdown(true); return userTransactionManager; } /** * jta transactionManager * * @return */ @Bean public JtaTransactionManager transactionManager() { JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(); jtaTransactionManager.setTransactionManager(userTransactionManager()); return jtaTransactionManager; } }
顺便把相关的依赖贴上,值得注意的是 spring-boot-starter-jta-atomikos 依赖,这是一个开源的事务管理器类。
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jta-atomikos</artifactId> </dependency> </dependencies>
以上就是全部配置了。接着我们来写测试用例,看下效果怎么样。
模拟场景:用户下单成功后,他的账户持仓应该对应增加,如果持仓更新失败,则他的下单操作也需要回滚。
/** * Created by zhangjunwei on 2017/8/2. */ @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = UserManageStart.class) public class DataSouceTest { @Autowired @Qualifier("symbolOrder") private AtomikosDataSourceBean symbolOrder; @Autowired @Qualifier("symbolPosition") private AtomikosDataSourceBean symbolPosition; @Transactional @Test public void test() { Connection orderConnection = null; Connection positionConnection = null; try { orderConnection = symbolOrder.getConnection(); String sql = "insert into order_symbol (accountId,symbol,amount,price,orderTime) values " + "({0},''{1}'',{2},{3},''{4}'')"; sql = MessageFormat.format(sql,4,"000004.SZ",100,(float)5.5,"2017-07-27 14:31:00"); PreparedStatement orderStatement = orderConnection.prepareStatement(sql); orderStatement.execute(); positionConnection = symbolPosition.getConnection(); sql = "insert into hq_position (accountId,symbol,amount) values " + "({0},''{1}'',{2})"; sql = MessageFormat.format(sql,4,"000002.SZ",200); PreparedStatement positionStatement = positionConnection.prepareStatement(sql); positionStatement.execute(); } catch (SQLException e) { e.printStackTrace(); } finally { try { if (orderConnection != null) { orderConnection.close(); } if (positionConnection != null) { positionConnection.close(); } } catch (SQLException e) { e.printStackTrace(); } } } }