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;
}
相关推荐
她的男孩11 分钟前
Spring Boot 表单挂 Flowable 审批:设计器绑模型后,业务侧用注解把单子跑起来
spring boot·低代码·工作流引擎
利威尔xu19 分钟前
Go IM 源码实战(7):断线三天重连,水位算错就漏消息
redis·后端·微服务·面试·golang
我的xiaodoujiao19 分钟前
Django 权限系统项目实战详细图文教程 4--系统登录功能实现 2
后端·python·django
月夏24 分钟前
前端第一次接自己的后端:mock 切真接口踩了 2 个坑,组件只改了一行 import
前端·后端·node.js
IT_陈寒27 分钟前
React实战技巧总结:如何优雅处理复杂列表的渲染卡顿问题?
前端·人工智能·后端
wno70428 分钟前
Spring Boot中JdbcTemplate与Mybatis使用PostgreSQL
spring boot·postgresql·mybatis
云上工程笔记41 分钟前
2026年大模型Token成本测算推荐清单:输入输出、缓存、并发与峰值用量
人工智能·spring boot·缓存·token
T量子永生1 小时前
Android SISO 分布式编译集群配置方案
android·分布式·云计算·源代码管理
RuoyiOffice1 小时前
UniApp企业办公:把审批、客户和项目装进手机,能做些什么?
spring boot·vue3·uniapp·crm·flowable·移动办公·企业应用
打工仔折腾 AI1 小时前
后端部署避坑:吃透IPv4地址、网络接口与CIDR前缀的实际应用
arm开发·人工智能·后端·python·性能优化