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数据源

 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 }
View Code

second数据源

 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 }
View Code

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一致)

posted @ 2022-02-11 10:31  炫舞风中  阅读(540)  评论(0编辑  收藏  举报