在上一节代码的的Service层做一下测试,人为制造一个被除数为0的异常。然后对该服务对应的Controller方法发送请求。(postman)
@Resource
private JdbcTemplate primaryJdbcTemplate;
@Resource
private JdbcTemplate secondaryJdbcTemplate;@Transactional
public Article saveArticle( Article article) {articleJDBCDAO.save(article,primaryJdbcTemplate);articleJDBCDAO.save(article,secondaryJdbcTemplate);int a = 2/0; //人为制造一个被除数为0的异常return article;
}
secondaryJdbcTemplate的数据插入数据成功,primaryJdbcTemplate的数据插入数据失败。数据库事务不能跨连接, 当然也就不能跨数据源,更不能跨库。一旦出现跨连接的情况,也就成了分布式事务,事务就不能单纯依赖于数据库去处理。我们这一节的实现方式,是通过JTA来实现。
分布式事务就是跨数据库连接的事务。一个事务的数据库操作,要么都成功,要么都失败回滚。后面我们专门做一个章节讲解事务与分布式事务。
org.springframework.boot spring-boot-starter-jta-atomikos
双数据源配置。删掉原有数据库连接配置.
primarydb:uniqueResourceName: primaryxaDataSourceClassName: com.mysql.jdbc.jdbc2.optional.MysqlXADataSourcexaProperties:url: jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=utf-8&useSSL=falseuser: testpassword: 4rfv$RFVexclusiveConnectionMode: trueminPoolSize: 3maxPoolSize: 10testQuery: SELECT 1 from dual #由于采用HikiriCP,用于检测数据库连接是否存活。secondarydb:uniqueResourceName: secondaryxaDataSourceClassName: com.mysql.jdbc.jdbc2.optional.MysqlXADataSourcexaProperties:url: jdbc:mysql://localhost:3306/testdb2?useUnicode=true&characterEncoding=utf-8&useSSL=falseuser: testpassword: 4rfv$RFVexclusiveConnectionMode: trueminPoolSize: 3maxPoolSize: 10testQuery: SELECT 1 from dual #由于采用HikiriCP,用于检测数据库连接是否存活。
MysqlXADataSource的解释:根据jdbc 4.0规范(12.2):XA数据源生成能够在全局/分布式事务中使用的XA连接。如果需要跨多个数据库或JMS调用的事务,则可能需要此类连接。您可以在此处找到有关概念的明确说明:http://www.theserverside.com/discussions/thread.tss?thread_id=21385#95346
如果不使用分布式事务,则在声明驱动程序时无需指定xa-datasource-class。这个xa-datasource-class是专门为分布式事务准备的。
下面是数据源bean的配置,将上面配置文件中的属性加载到Spring Bean里面。也就是将配置参数应用到我们的双数据库数据源实例对象中。
@Configuration
public class DataSourceConfig {//jta数据源primarydb@Bean(initMethod="init", destroyMethod="close", name="primaryDataSource")@Primary@ConfigurationProperties(prefix = "primarydb")public DataSource primaryDataSource() {//这里是关键,返回的是AtomikosDataSourceBean,所有的配置属性也都是注入到这个类里面return new AtomikosDataSourceBean();}//jta数据源secondarydb@Bean(initMethod="init", destroyMethod="close", name="secondaryDataSource")@ConfigurationProperties(prefix = "secondarydb")public DataSource secondaryDataSource() {return new AtomikosDataSourceBean();}//primaryJdbcTemplate使用primaryDataSource数据源@Beanpublic JdbcTemplate primaryJdbcTemplate(@Qualifier("primaryDataSource") DataSource primaryDataSource) {return new JdbcTemplate(primaryDataSource);}//secondaryJdbcTemplate使用secondaryDataSource数据源@Beanpublic JdbcTemplate secondaryJdbcTemplate(@Qualifier("secondaryDataSource") DataSource secondaryDataSource) {return new JdbcTemplate(secondaryDataSource);}
}
负责协调多个JTA数据源实现事务机制。固定写法,不用纠结UserTransaction 、TransactionManager 、JtaTransactionManager 都是什么。或者说:都是帮我们实现JTA分布式事务的事务管理器。
@Configuration
public class TransactionManagerConfig {@Beanpublic UserTransaction userTransaction() throws SystemException {UserTransactionImp userTransactionImp = new UserTransactionImp();userTransactionImp.setTransactionTimeout(10000);return userTransactionImp;}@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")public TransactionManager atomikosTransactionManager() throws Throwable {UserTransactionManager userTransactionManager = new UserTransactionManager();userTransactionManager.setForceShutdown(false);return userTransactionManager;}@Bean(name = "transactionManager")@DependsOn({ "userTransaction", "atomikosTransactionManager" })public PlatformTransactionManager transactionManager() throws Throwable {UserTransaction userTransaction = userTransaction();JtaTransactionManager manager = new JtaTransactionManager(userTransaction, atomikosTransactionManager());return manager;}
}
这时候,再测试一下文首中的测试用例:人为制造一个被除数为0的异常,异常抛出,两个数据库实例中的article表将都无法插入数据。符合事务的要求:正常情况数据操作都成功,异常情况数据操作都失败回滚。
优点: 能够支持分布式事务
缺点: 性能开销大,不适合用于高并发场景