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 分钟前
SpringBoot原生实现分布式MapReduce计算
spring boot·分布式·mapreduce
iuyou️14 分钟前
Spring Boot知识点详解
java·spring boot·后端
一弓虽26 分钟前
SpringBoot 学习
java·spring boot·后端·学习
姑苏洛言35 分钟前
扫码小程序实现仓库进销存管理中遇到的问题 setStorageSync 存储大小限制错误解决方案
前端·后端
悻运37 分钟前
如何配置Spark
大数据·分布式·spark
懒惰的橘猫1 小时前
Spark集群搭建之Yarn模式
大数据·分布式·spark
光而不耀@lgy1 小时前
C++初登门槛
linux·开发语言·网络·c++·后端
2401_824256861 小时前
Spark-Streaming
大数据·分布式·spark
方圆想当图灵1 小时前
由 Mybatis 源码畅谈软件设计(七):SQL “染色” 拦截器实战
后端·mybatis·代码规范
毅航2 小时前
MyBatis 事务管理:一文掌握Mybatis事务管理核心逻辑
java·后端·mybatis