spring boot默认继承事务,只要在方法上添加@Transaction注解即可,但是这种只适用与单数据源,在多数据源下就不再适用,比如在一个service方法中,执行两个操作,往数据库1中插入一条数据,往数据库2中插入一条数据,但如果往数据库2中插入数据时失败,需要将数据库1事务回滚,此时只简单的使用@Transaction就无法实现了,此时就用到了分布式事务。 spring2.5以前,对分布式多数据源不支持,但是在spring3.0之后针对这个问题的做出了解决方案,具体你们可以看看这篇博文:被忽略的Spring3小改进—支持多数据源的@Transactional事务注解 ,但由于本文使用的不是spring3.0,所以我们这里给出了另一种解决方案:使用springboot+jta+atomikos 实现分布式事物管理
项目目录结构如下:
第一个数据源配置:
package com.tfjybj.intern.config; import org.springframework.boot.context.properties.ConfigurationProperties; // 与application.properties配置文件前缀一致 @ConfigurationProperties(prefix = "spring.datasource.test1") public class DBConfig1 { private String url; private String username; private String password; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }第二个数据源信息:
package com.tfjybj.intern.config;/* import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "spring.datasource.test2") public class DBConfig2 { private String url; private String username; private String password; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }创建第一个数据源
package com.tfjybj.intern.config;/* import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import javax.sql.DataSource; import java.sql.SQLException; @Configuration // 扫描的Dao层 @MapperScan(basePackages = "com.tfjybj.intern.provider.dao1", sqlSessionTemplateRef = "test1SqlSessionTemplate") public class TestMyBatisConfig1 { // 配置数据源 @Primary @Bean(name = "test1DataSource") public DataSource testDataSource(DBConfig1 testConfig) throws SQLException { // 创建MYsql实现XA规范的分布式数据源 MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource(); // 设置连接信息 mysqlXaDataSource.setUrl(testConfig.getUrl()); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); mysqlXaDataSource.setPassword(testConfig.getPassword()); mysqlXaDataSource.setUser(testConfig.getUsername()); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); // 数据源改为Atomikos,将事务交给Atomikos统一管理 AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(mysqlXaDataSource); xaDataSource.setUniqueResourceName("test1DataSource"); // xaDataSource.setMinPoolSize(testConfig.getMinPoolSize()); // xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize()); // xaDataSource.setMaxLifetime(testConfig.getMaxLifetime()); // xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout()); // xaDataSource.setLoginTimeout(testConfig.getLoginTimeout()); // xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval()); // xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime()); // xaDataSource.setTestQuery(testConfig.getTestQuery()); return xaDataSource; } @Bean(name = "test1SqlSessionFactory") public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper1/UserMapper.xml")); // 通常项目中配置为*,扫描所有的mapper // bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper1/*.xml")); return bean.getObject(); } @Bean(name = "test1SqlSessionTemplate") public SqlSessionTemplate testSqlSessionTemplate( @Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }创建第二个数据源
package com.tfjybj.intern.config;/* import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import javax.sql.DataSource; import java.sql.SQLException; @Configuration @MapperScan(basePackages = "com.tfjybj.intern.provider.dao2", sqlSessionTemplateRef = "test2SqlSessionTemplate") public class TestMyBatisConfig2 { // 配置数据源 @Bean(name = "test2DataSource") public DataSource testDataSource(DBConfig1 testConfig) throws SQLException { MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource(); mysqlXaDataSource.setUrl(testConfig.getUrl()); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); mysqlXaDataSource.setPassword(testConfig.getPassword()); mysqlXaDataSource.setUser(testConfig.getUsername()); mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true); AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean(); xaDataSource.setXaDataSource(mysqlXaDataSource); xaDataSource.setUniqueResourceName("test2DataSource"); // xaDataSource.setMinPoolSize(testConfig.getMinPoolSize()); // xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize()); // xaDataSource.setMaxLifetime(testConfig.getMaxLifetime()); // xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout()); // xaDataSource.setLoginTimeout(testConfig.getLoginTimeout()); // xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval()); // xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime()); // xaDataSource.setTestQuery(testConfig.getTestQuery()); return xaDataSource; } @Bean(name = "test2SqlSessionFactory") public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper2/ResumeMapper.xml")); return bean.getObject(); } @Bean(name = "test2SqlSessionTemplate") public SqlSessionTemplate testSqlSessionTemplate( @Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }1.在进行Mapper扫描时@MapperScan(basePackages = “com.tfjybj.intern.provider.dao2”, sqlSessionTemplateRef = “test2SqlSessionTemplate”),basePackages不同的数据源要设置为不同的,放在一个文件夹中我这报错了 2.SqlSessionTemplate是个线称安全的类,每运行一个SqlSessionTemplate时,它就会重新获取一个新的SqlSession,所以每个方法都有一个独立的SqlSession,这意味着它是线称安全的 3.核心思想:将事务(数据源)交给Automikos管理
@EnableConfigurationProperties注解的作用是:使使用 @ConfigurationProperties 注解的类生效,如果一个配置类只配置@ConfigurationProperties注解,而没有使用@Component,那么在IOC容器中是获取不到properties 配置文件转化的bean。说白了 @EnableConfigurationProperties 相当于把使用 @ConfigurationProperties 的类进行了一次注入
@EnableConfigurationProperties(value = {DBConfig1.class, DBConfig2.class}) @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class InternApplication { public static void main(String[] args) { SpringApplication.run(InternApplication.class, args); } }如果不添加改注解,在创建数据源代码中会报错:Could not autowire. No beans of ‘DBConfig1’ type found.
在本事务中,任何地方出错,事务都会回滚,即这两个数据库都不会插入数据,这样就实现了多数据源的分布式事务
1. JTA: 即Java Transaction API,JTA允许应用程序执行分布式事务处理——在两个或多个网络计算机资源上访问并且更新数据。JTA事务比JDBC事务更强大。一个JTA事务可以有多个参与者,而一个JDBC事务则被限定在一个单一的数据库连接.更多详情参见:https://blog.csdn.net/wrs120/article/details/90136782#32_JTA_45
2. Atomikos: 是一个为Java平台提供增值服务的并且开源类事务管理器TM,将所有的事务将给Atomikos统一管理
