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;
}
相关推荐
bug_null18 分钟前
RabbitMQ消息可靠性保证机制7--可靠性分析-rabbitmq_tracing插件
分布式·rabbitmq
kingbal18 分钟前
RabbitMQ:添加virtualHost
分布式·rabbitmq
AI人H哥会Java1 小时前
【Spring】Spring的模块架构与生态圈—Spring MVC与Spring WebFlux
java·开发语言·后端·spring·架构
小马爱打代码1 小时前
SpringCloud(注册中心+OpenFeign+网关+配置中心+服务保护+分布式事务)
分布式·spring·spring cloud
毕设资源大全1 小时前
基于SpringBoot+html+vue实现的林业产品推荐系统【源码+文档+数据库文件+包部署成功+答疑解惑问到会为止】
java·数据库·vue.js·spring boot·后端·mysql·html
Watermelon_Mr1 小时前
Spring(三)-SpringWeb-概述、特点、搭建、运行流程、组件、接受请求、获取请求数据、特殊处理、拦截器
java·后端·spring
唐墨1232 小时前
golang自定义MarshalJSON、UnmarshalJSON 原理和技巧
开发语言·后端·golang
凡人的AI工具箱2 小时前
每天40分玩转Django:Django测试
数据库·人工智能·后端·python·django·sqlite
qyq12 小时前
Django框架与ORM框架
后端·python·django
BUTCHER53 小时前
Kafka安装篇
分布式·kafka