SpringBoot 整合jta+atomikos 解决分布式事务 | Eddie'Blog
SpringBoot  整合jta+atomikos 解决分布式事务

SpringBoot 整合jta+atomikos 解决分布式事务

eddie 343 2020-11-23

目录

整合步骤

  1. 准备工作
  2. 新增依赖
  3. 编写YAML样式的配置
  4. 编写实体类读取配置
  5. 编写多数据源与管理事务配置类
  6. 测试效果

官网地址: https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-jta

一、准备工作

1.1 构建SpringBoot项目

1.2 《Linux搭建MySQL主从》基础上追加库表

CREATE DATABASE `xa_246` CHARACTER SET 'utf8mb4'

CREATE TABLE `xa_246`.`xa_246`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
)

CREATE DATABASE `xa_247` CHARACTER SET 'utf8mb4'

CREATE TABLE `xa_247`.`xa_247`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
)

二、新增依赖

<!-- 使用jta+atomikos管理多数据源事务 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>

<!-- 单元测试 -->
<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<scope>test</scope>
</dependency>

<!-- 简化代码 -->
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<optional>true</optional>
</dependency>

三、编写YAML样式的配置

spring:
  application:
    name: jta_atomikos
mysql:
  datasource:
    db246:
      url: "jdbc:mysql://192.168.8.246/xa_246"
      user: "eddie"
      password: "Abc@123456"
      uniqueresourcename: "dataSource246"
    db247:
      url: "jdbc:mysql://192.168.8.247/xa_247"
      user: "eddie"
      password: "Abc@123456"
      uniqueresourcename: "dataSource247"

四、编写实体类读取配置

db246

@Data
@Configuration
@ConfigurationProperties(prefix = "mysql.datasource.db246")
public class DbConfig246 {

    private String url;

    private String user;

    private String password;

    private String uniqueresourcename;

}

db247

@Data
@Configuration
@ConfigurationProperties(prefix = "mysql.datasource.db247")
public class DbConfig247 {

    private String url;

    private String user;

    private String password;

    private String uniqueresourcename;

}

五、编写多数据源与管理事务配置类

db246

@Configuration
@MapperScan(value = "com.example.xademo.db246.dao", sqlSessionFactoryRef = "sqlSessionFactoryBean246")
public class ConfigDb246 {

    @Bean("dataSource246")
    public DataSource dataSource246(DbConfig246 dbConfig246) {
        // MySQL <scope>runtime</scope> 会导致引入不了的
        MysqlXADataSource xaDataSource = new MysqlXADataSource();
        xaDataSource.setUser(dbConfig246.getUser());
        xaDataSource.setPassword(dbConfig246.getPassword());
        xaDataSource.setUrl(dbConfig246.getUrl());

        // Atomikos管理
        AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
        atomikosDataSourceBean.setXaDataSource(xaDataSource);
        atomikosDataSourceBean.setUniqueResourceName(dbConfig246.getUniqueresourcename());

        // 返回 Atomikos 的数据源
        return atomikosDataSourceBean;
    }

    @Bean("sqlSessionFactoryBean246")
    public SqlSessionFactoryBean sqlSessionFactoryBean(@Qualifier("dataSource246") DataSource dataSource) throws IOException {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        // 设置Mybatis
        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
        sqlSessionFactoryBean.setMapperLocations(resourceResolver.getResources("mybatis/dataSource246/*.xml"));
        return sqlSessionFactoryBean;
    }

    /**
     * 事务管理器
     */
    @Bean("xaTransaction")
    public JtaTransactionManager jtaTransactionManager() {
        UserTransaction userTransaction = new UserTransactionImp();
        UserTransactionManager userTransactionManager = new UserTransactionManager();
        return new JtaTransactionManager(userTransaction, userTransactionManager);
    }

}

db247

@Configuration
@MapperScan(value = "com.example.xademo.db247.dao", sqlSessionFactoryRef = "sqlSessionFactoryBean247")
public class ConfigDb247 {

    @Bean("dataSource247")
    public DataSource dataSource247(DbConfig247 dbConfig247) {
        // MySQL <scope>runtime</scope> 会导致引入不了的
        MysqlXADataSource xaDataSource = new MysqlXADataSource();
        xaDataSource.setUser(dbConfig247.getUser());
        xaDataSource.setPassword(dbConfig247.getPassword());
        xaDataSource.setUrl(dbConfig247.getUrl());

        // Atomikos管理
        AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
        atomikosDataSourceBean.setXaDataSource(xaDataSource);
        atomikosDataSourceBean.setUniqueResourceName(dbConfig247.getUniqueresourcename());

        // 返回 Atomikos 的数据源
        return atomikosDataSourceBean;
    }

    @Bean("sqlSessionFactoryBean247")
    public SqlSessionFactoryBean sqlSessionFactoryBean(@Qualifier("dataSource247") DataSource dataSource) throws IOException {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        // 设置Mybatis
        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
        sqlSessionFactoryBean.setMapperLocations(resourceResolver.getResources("mybatis/dataSource247/*.xml"));
        return sqlSessionFactoryBean;
    }
    
}

六、测试效果

启动类

/**
 * @author eddie.lee
 */
@SpringBootApplication
@EnableConfigurationProperties(value = {DbConfig246.class, DbConfig247.class})
public class XaDemoApplication {

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

业务层

@Service
public class XAService {

    @Resource
    private XA246Mapper xa246Mapper;

    @Resource
    private XA247Mapper xa247Mapper;

    /**
     * 指定事务管理器
     */
    @Transactional(transactionManager = "xaTransaction")
    public void testXA() {
        XA246 xa246 = new XA246();
        xa246.setId(1);
        xa246.setName("xa_246");
        xa246Mapper.insert(xa246);

        XA247 xa247 = new XA247();
        xa247.setId(1);
        xa247.setName("xa_247");
        xa247Mapper.insert(xa247);
    }
}

单元测试

@RunWith(SpringRunner.class)
@SpringBootTest
class XaDemoApplicationTests {

    @Autowired
    private XAService xaService;

    @Test
    void contextLoads() {
    }

    @Test
    public void testXA() {
        xaService.testXA();
    }

}
  1. 第一次运行,插入成功
  2. 修改数据库"ALTER TABLE xa_247.xa_247 MODIFY COLUMN name varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL AFTER id"
  3. 第二次运行,出现"### Cause: com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: Data too long for column 'name' at row 1"的错误。
  4. 查看数据库,246与247也没有插入,证明已经成效了分布式事务

TIPS: Mycat 与 ShardingJdbc 默认已经开启XA事务管理


# Java # 分布式事务