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;
}
相关推荐
herobrineAC7891 天前
Hyperopt 强大的分布式参数优化框架全解析
分布式·其他
明达智控技术1 天前
MR30系列分布式I/O在造型机产线的应用
分布式·物联网·自动化
Moniane1 天前
A2A+MCP构建智能体协作生态:下一代分布式人工智能架构解析
人工智能·分布式·架构
m0_564264181 天前
IDEA DEBUG调试时如何获取 MyBatis-Plus 动态拼接的 SQL?
java·数据库·spring boot·sql·mybatis·debug·mybatis-plus
熊小猿1 天前
在 Spring Boot 项目中使用分页插件的两种常见方式
java·spring boot·后端
paopaokaka_luck1 天前
基于SpringBoot+Vue的助农扶贫平台(AI问答、WebSocket实时聊天、快递物流API、协同过滤算法、Echarts图形化分析、分享链接到微博)
java·vue.js·spring boot·后端·websocket·spring
老华带你飞1 天前
机器人信息|基于Springboot的机器人门户展示系统设计与实现(源码+数据库+文档)
java·数据库·spring boot·机器人·论文·毕设·机器人门户展示系统
小蒜学长1 天前
springboot酒店客房管理系统设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端
橙子家1 天前
Serilog 日志库简单实践(一):文件系统 Sinks(.net8)
后端
Yeats_Liao1 天前
Go Web 编程快速入门 13 - 部署与运维:Docker容器化、Kubernetes编排与CI/CD
运维·前端·后端·golang