Springboot jta-atomikos 多数据源事务(老技术回顾)

摘要:这个技术本来应该是过时的技术,但是最近的项目比较棘手,时间又比较赶,临时使用jta-atomikos多数据源事务,下面给大家分享如何快速接入jta-atomikos多数据源的分布式事务。不推荐生产环境长时间使用,临时解决问题可以使用。

项目简介

项目基于SpringBoot + mybatis-plus + jta-atomikos + mysql,使用传统的MVC风格代码做案例,写入user用户库和order订单库。

项目开始

创建数据库和表

创建jta-user

sql 复制代码
CREATE TABLE `userinfo` (
  `id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `username` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

创建jta-order

sql 复制代码
CREATE TABLE `order_info` (
  `id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

代码

pom.xml

xml 复制代码
<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.11</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jta-atomikos</artifactId>
    </dependency>

</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

启动类JtaApplication

arduino 复制代码
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class JtaApplication {

    public static void main(String[] args) {
        SpringApplication.run(JtaApplication.class, args);
    }

}

多数据源相关配置

user

实体和mapper接口我就不写出来了很简单

java 复制代码
@Configuration
@MapperScan(basePackages = UserDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "userSqlSessionFactory")
@Slf4j
public class UserDataSourceConfig {

    protected static final String PACKAGE = "com.huzhihui.jta.infrastructure.user.mapper";

    @Bean(name = "userDataSourceProperties")
    @ConfigurationProperties(prefix = "spring.datasource.jta.user")
    public DataSourceProperties userDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Primary
    @Bean(name = "userDataSource")
    public DataSource userDataSource(@Qualifier("userDataSourceProperties") DataSourceProperties dataSourceProperties) {
        DruidXADataSource druidXADataSource = new DruidXADataSource();
        druidXADataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
        druidXADataSource.setUrl(dataSourceProperties.getUrl());
        druidXADataSource.setUsername(dataSourceProperties.getUsername());
        druidXADataSource.setPassword(dataSourceProperties.getPassword());

        AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
        atomikosDataSourceBean.setXaDataSource(druidXADataSource);
        atomikosDataSourceBean.setUniqueResourceName("userDataSource");
        atomikosDataSourceBean.setPoolSize(5);
        return atomikosDataSourceBean;
    }

    @Primary
    @Bean(name = "userSqlSessionFactory")
    public SqlSessionFactory userSqlSessionFactory(@Qualifier("userDataSource") DataSource dataSource) {
        MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
        try{
            sessionFactory.setDataSource(dataSource);
            return sessionFactory.getObject();
        } catch (Exception e) {
            log.error("",e);
        }
        return null;
    }

    @Primary
    @Bean(name = "userSqlSessionTemplate")
    public SqlSessionTemplate organizationSqlSessionTemplate(@Qualifier("userSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    /**
     * 事务配置,只需要配置一个即可
     * @return
     * @throws Exception
     */
    @Bean(name = "transactionManager")
    @Primary
    public JtaTransactionManager regTransactionManager() throws Exception{
        UserTransactionManager userTransactionManager = new UserTransactionManager();
        userTransactionManager.setTransactionTimeout(3600);
        UserTransaction userTransaction = new UserTransactionImp();
        userTransaction.setTransactionTimeout(3600);
        JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(userTransaction, userTransactionManager);
        jtaTransactionManager.setDefaultTimeout(3600);
        return jtaTransactionManager;
    }

}
order

实体和mapper接口我就不写出来了很简单

less 复制代码
@Configuration
@MapperScan(basePackages = OrderDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "orderSqlSessionFactory")
@Slf4j
public class OrderDataSourceConfig {

    protected static final String PACKAGE = "com.huzhihui.jta.infrastructure.order.mapper";

    @Bean(name = "orderDataSourceProperties")
    @ConfigurationProperties(prefix = "spring.datasource.jta.order")
    public DataSourceProperties orderDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean(name = "orderDataSource")
    public DataSource orderDataSource(@Qualifier("orderDataSourceProperties") DataSourceProperties dataSourceProperties) {
        DruidXADataSource druidXADataSource = new DruidXADataSource();
        druidXADataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
        druidXADataSource.setUrl(dataSourceProperties.getUrl());
        druidXADataSource.setUsername(dataSourceProperties.getUsername());
        druidXADataSource.setPassword(dataSourceProperties.getPassword());

        AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
        atomikosDataSourceBean.setXaDataSource(druidXADataSource);
        atomikosDataSourceBean.setUniqueResourceName("orderDataSource");
        atomikosDataSourceBean.setPoolSize(5);
        return atomikosDataSourceBean;
    }

    @Bean(name = "orderSqlSessionFactory")
    public SqlSessionFactory orderSqlSessionFactory(@Qualifier("orderDataSource") DataSource dataSource) {
        MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
        try{
            sessionFactory.setDataSource(dataSource);
            return sessionFactory.getObject();
        } catch (Exception e) {
            log.error("",e);
        }
        return null;
    }

    @Bean(name = "orderSqlSessionTemplate")
    public SqlSessionTemplate orderSqlSessionTemplate(@Qualifier("orderSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

测试接口编写

InitService接口
csharp 复制代码
@Service
public class InitService {

    @Autowired
    private UserinfoMapper userinfoMapper;
    @Autowired
    private OrderMapper orderMapper;

    /**
     * 先写入user再写入order,再抛出异常看数据是否都回滚
     */
    @Transactional(rollbackFor = Exception.class)
    public void test01(){
        userinfoMapper.insert(new UserinfoEntity("多库ID-userinfo","先写入user再写入order,再抛出异常看数据是否都回滚"));
        orderMapper.insert(new OrderEntity("多库ID-order","先写入user再写入order,再抛出异常看数据是否都回滚"));
        int a = 1/0;
    }

    /**
     * user表的事务回滚
     */
    @Transactional(rollbackFor = Exception.class)
    public void test02(){
        userinfoMapper.insert(new UserinfoEntity("单库ID-userinfo","user表的事务回滚"));
        int a = 1/0;
    }

    /**
     * order表的事务回滚
     */
    @Transactional(rollbackFor = Exception.class)
    public void test03(){
        orderMapper.insert(new OrderEntity("单库ID-order","order表的事务回滚"));
        int a = 1/0;
    }

    /**
     * 数据正常写入2个库
     */
    @Transactional(rollbackFor = Exception.class)
    public void test04(){
        userinfoMapper.insert(new UserinfoEntity("多库ID-userinfo-s","先写入user再写入order,都成功"));
        orderMapper.insert(new OrderEntity("多库ID-order-s","先写入user再写入order,都成功"));
    }

}
JtaTestController
typescript 复制代码
@RestController
@RequestMapping(value = "jta-test")
public class JtaTestController {

    @Autowired
    private InitService initService;

    @GetMapping(value = "test01")
    public String test01(){
        initService.test01();
        return "success";
    }

    @GetMapping(value = "test02")
    public String test02(){
        initService.test02();
        return "success";
    }

    @GetMapping(value = "test03")
    public String test03(){
        initService.test03();
        return "success";
    }

    @GetMapping(value = "test04")
    public String test04(){
        initService.test04();
        return "success";
    }

}

实际测试结果

案例一

先写入user再写入order,再抛出异常看数据是否都回滚

bash 复制代码
curl http://localhost:8080/jta-test/test01

结果

案例二

user表的事务回滚

bash 复制代码
curl http://localhost:8080/jta-test/test02

结果

案例三

order表的事务回滚

bash 复制代码
curl http://localhost:8080/jta-test/test03

结果

案例四

先写入user再写入order,都成功

bash 复制代码
curl http://localhost:8080/jta-test/test04

结果

注意点

事务超时时间配置

单位是秒,最长300秒,超出无效

ini 复制代码
@Bean(name = "transactionManager")
@Primary
public JtaTransactionManager regTransactionManager() throws Exception{
    UserTransactionManager userTransactionManager = new UserTransactionManager();
    userTransactionManager.setTransactionTimeout(300);
    UserTransaction userTransaction = new UserTransactionImp();
    userTransaction.setTransactionTimeout(300);
    JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(userTransaction, userTransactionManager);
    jtaTransactionManager.setDefaultTimeout(300);
    return jtaTransactionManager;
}
相关推荐
倔强的石头1064 分钟前
高可用与无感扩容——分布式时序数据库选型指南
数据库·分布式·时序数据库
摇滚侠7 小时前
《SpringBoot 3:入门与应用实战》第 12 章 JDBC 与事务 使用 JdbcTemplate 阅读笔记 32
spring boot·笔记·后端
步行cgn8 小时前
@Configuration 详解:Spring 配置类的核心注解
java·后端·spring
桦说编程10 小时前
【AtomicAgent系列1】变异测试——过去做不起,现在 agent 做得起
后端·ai编程·vibecoding
考虑考虑11 小时前
nginx打印请求日志
运维·后端·nginx
IT_陈寒11 小时前
Python的切片赋值把我坑惨了,这不是bug是特性
前端·人工智能·后端
CoderLiu11 小时前
程序化工具调用(PTC)与动态工作流引擎:深入大模型工具调用的架构演进与实践
前端·人工智能·后端
fireworks9912 小时前
SpringBoot2接入Knife4j
后端
wei_shuo12 小时前
数据迁移工具实战:KDMS自动化评估与量化决策体系解析
后端
Lightpwd12 小时前
Spring Boot 多数据源落地:AbstractRoutingDataSource + 注解切面(附源码)
数据库·后端