Atomikos解决多数据源事务
参考地址:分布式事务
一、引入maven依赖
<!--添加atomikos-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
<!-- druids数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
二、配置多数据源
1、application.yml数据源配置
#数据源配置
spring:
datasource:
#第一个数据源配置:名字自定义
first:
url: jdbc:postgresql://192.168.1.198:5432/datacenter?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&pinGlobalTxToPhysicalConnection=true
username: datacenter
password: datacenter
#第二个数据源配置:名字自定义
second:
url: jdbc:postgresql://192.168.1.198:5432/government?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&pinGlobalTxToPhysicalConnection=true
username: government
password: government@123
2、Java配置数据源
分开两个文件配置,清晰明了,不易出错。
first数据源
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 @Configuration 2 @MapperScan(basePackages = {"com.yzh.demo.mapper.first"},sqlSessionTemplateRef = "firstSqlSessionTemplate") 3 public class DataSourceConfig1 { 4 5 @Value("${spring.datasource.first.url}") 6 private String url; 7 @Value("${spring.datasource.first.username}") 8 private String username; 9 @Value("${spring.datasource.first.password}") 10 private String password; 11 12 /** 13 * 创建first数据源 14 */ 15 @Bean(name = "firstDataSource") 16 @Primary 17 public DataSource createProductDataSource(){ 18 AtomikosDataSourceBean ds = new AtomikosDataSourceBean(); 19 DruidXADataSource dds = new DruidXADataSource(); 20 dds.setUsername(username); 21 dds.setPassword(password); 22 dds.setUrl(url); 23 ds.setXaDataSource(dds); 24 ds.setUniqueResourceName("firstDataSource"); 25 return ds; 26 } 27 28 @Primary 29 @Bean(name = "firstSqlSessionFactory") 30 public SqlSessionFactory firstSqlSessionFactory(@Qualifier(value = "firstDataSource") DataSource dataSource) throws Exception { 31 //这里如果使用SqlSessionFactoryBean 将不能使用mybatis-plus自带的baseMapper中的方法 32 MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean(); 33 //设置数据源 34 sqlSessionFactoryBean.setDataSource(dataSource); 35 //设置别名 36 sqlSessionFactoryBean.setTypeAliasesPackage("com.yzh.demo.entity.first"); 37 //设置驼峰 38 MybatisConfiguration c = new MybatisConfiguration(); 39 c.setMapUnderscoreToCamelCase(true); 40 sqlSessionFactoryBean.setConfiguration(c); 41 //设置映射接口的xml配置文件 42 sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/first/*.xml")); 43 return sqlSessionFactoryBean.getObject(); 44 } 45 46 /** 47 * 创建SqlSessionTemplate 48 */ 49 @Primary 50 @Bean(name = "firstSqlSessionTemplate") 51 public SqlSessionTemplate firstSqlSessionTemplate(@Qualifier("firstSqlSessionFactory") SqlSessionFactory sqlSessionFactory){ 52 return new SqlSessionTemplate(sqlSessionFactory); 53 } 54 }
second数据源
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 @Configuration 2 @MapperScan(basePackages = {"com.yzh.demo.mapper.second"},sqlSessionTemplateRef = "secondSqlSessionTemplate") 3 public class DataSourceConfig2 { 4 5 @Value("${spring.datasource.second.url}") 6 private String url; 7 @Value("${spring.datasource.second.username}") 8 private String username; 9 @Value("${spring.datasource.second.password}") 10 private String password; 11 12 /** 13 * 创建second数据源 14 */ 15 @Bean(name = "secondDataSource") 16 public DataSource createProductDataSource(){ 17 AtomikosDataSourceBean ds = new AtomikosDataSourceBean(); 18 DruidXADataSource dds = new DruidXADataSource(); 19 dds.setUsername(username); 20 dds.setPassword(password); 21 dds.setUrl(url); 22 ds.setXaDataSource(dds); 23 ds.setUniqueResourceName("secondDataSource"); 24 return ds; 25 } 26 27 @Bean(name = "secondSqlSessionFactory") 28 public SqlSessionFactory orderSqlSessionFactory(@Qualifier(value = "secondDataSource") DataSource dataSource) throws Exception { 29 //这里如果使用SqlSessionFactoryBean 将不能使用mybatis-plus自带的baseMapper中的方法 30 MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean(); 31 //设置数据源 32 sqlSessionFactoryBean.setDataSource(dataSource); 33 //设置别名 34 sqlSessionFactoryBean.setTypeAliasesPackage("com.yzh.demo.entity.second"); 35 //设置驼峰 36 MybatisConfiguration c = new MybatisConfiguration(); 37 c.setMapUnderscoreToCamelCase(true); 38 sqlSessionFactoryBean.setConfiguration(c); 39 //设置映射接口的xml配置文件 40 sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/second/*.xml")); 41 return sqlSessionFactoryBean.getObject(); 42 } 43 44 /** 45 * 创建SqlSessionTemplate 46 */ 47 @Bean(name = "secondSqlSessionTemplate") 48 public SqlSessionTemplate orderSqlSessionTemplate(@Qualifier("secondSqlSessionFactory") SqlSessionFactory sqlSessionFactory){ 49 return new SqlSessionTemplate(sqlSessionFactory); 50 } 51 }
3、业务代码
实体类略。
@Service
public class Test1ServiceImpl extends ServiceImpl<Test1Mapper, Test1> implements ITest1Service {
@Autowired
private Test2Mapper test2Mapper;
@Autowired
private Test1Mapper test1Mapper;
@Transactional
@Override
public void test(Integer id,Integer num) {
//先插入first库
Test1 t1 = new Test1();
t1.setId(id);
t1.setNum(num);
test1Mapper.insert(t1);
//再插入second库
Test2 t2 = new Test2();
t2.setId(id);
t2.setNum(num);
test2Mapper.insert(t2);
//抛出异常
int i = 1 / 0;
}
}
4、注意
当遇到入校错误时:
Caused by: org.postgresql.util.PSQLException: ERROR: prepared transactions are disabled
说明参数max_prepared_transactions=0,关闭了预准备事务,需要在postgresql.conf中将该参数的值设置大于1(建议设置成和max_connections一致)