学习Atomikos整合多个数据源实现JTA分布式事务管理

版权声明:本文为CSDN博主「luzhouyue1024」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/luzhouyue1024/article/details/128887235

在前一篇学习Spring框架下的分布式事务_luzhouyue1024的博客-CSDN博客 的基础上,实战编程一个分布式事务管理的项目

代码采用H2内存数据库,有两个h2数据源,第一个数据源firstDataSource是一个AtomikosDataSourceBean的实例,并注入了链接了DB1的一个内存数据库(数据库类型H2),配置文件如下:

package com.lydia.multipledatasources.config;
 
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 
import javax.sql.DataSource;
 
/**
 * @description:
 * @author: Lydia Lee
 */
@Configuration
//配置 mapper 的扫描位置,指定相应的 sqlSessionTemplate
@MapperScan(basePackages = "com.lydia.multipledatasources.mapper.first", sqlSessionTemplateRef = "firstSqlSessionTemplate")
public class FirstDataSourceConfig {
    @Autowired
    FirstDataSourceHelper dataSourceHelper;
 
    @Bean
    @Primary
    // 读取配置,创建Atomiko数据源
    public DataSource firstDataSource() {
        // 设置数据库连接
        org.h2.jdbcx.JdbcDataSource h2XADataSource = new org.h2.jdbcx.JdbcDataSource();
        h2XADataSource.setUser(dataSourceHelper.getUsername());
        h2XADataSource.setPassword(dataSourceHelper.getPassword());
        h2XADataSource.setURL(dataSourceHelper.getJdbcUrl());
 
        // 事务管理器
        AtomikosDataSourceBean firstDataSource = new AtomikosDataSourceBean();
        firstDataSource.setXaDataSource(h2XADataSource);
        firstDataSource.setUniqueResourceName("firstDataSource");
        return firstDataSource;
    }
 
 
 
    @Bean
    @Primary
    // 创建 SqlSessionFactory
    public SqlSessionFactory firstSqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        // 设置 xml 的扫描路径
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/first/*.xml"));
        org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration();
        config.setMapUnderscoreToCamelCase(true);
        bean.setConfiguration(config);
        return bean.getObject();
    }
 
    @Bean
    @Primary
    // 创建 SqlSessionTemplate
    public SqlSessionTemplate firstSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
 
 
}

第二个数据源secondDataSource类似配置即可,详情见GitHub源代码

配置事务管理类如下,创建 AtomikosTransactionManager 用于分布式事务管理,并把它注入到Spring的事务管理框架JtaTransactionManager中

package com.lydia.multipledatasources.config;
 
import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.transaction.jta.JtaTransactionManager;
 
import javax.sql.DataSource;
import javax.transaction.UserTransaction;
 
/**
 * @description: 配置并为Spring Boot注入了JTA TransactionManager
 * 实现为Atomikos提供的atomikos UserTransactionManager
 * @author: Lydia Lee
 */
 
@Configuration
public class TransactionManagerConfig {
 
    @Bean
    @Primary
    // 创建 AtomikosTransactionManager 用于分布式事务管理
    public UserTransactionManager atomikosTransactionManager(DataSource dataSource) {
        UserTransactionManager transactionManager = new UserTransactionManager();
        transactionManager.setForceShutdown(true);
        return transactionManager;
    }
 
    //配置Spring的JtaTransactionManager,底层委派给atomikos进行处理
    @Bean
    public JtaTransactionManager jtaTransactionManager (UserTransactionManager userTransactionManager) {
        UserTransaction userTransaction = new UserTransactionImp();
        return new JtaTransactionManager(userTransaction, userTransactionManager);
    }
}

主要原理是实例化两个数据源first和second,他们均由AtomikosDataSourceBean管理数据链接,然后在一个全局事务中,两个数据源如果成功就会一起提交,失败则会一起回滚。

让我们把注意力聚焦到业务逻辑UserService里面的insertTwoDBWithTXRollback(String name)这个方法中,虽然firstUser和secondUser已经调用了insert操作,但是由于1/0产生了异常,导致整个事务被JtaTransactionManager回滚,也就是两条记录并没有插入两个数据库。
 

/**
 * @description:
 * @author: Lydia Lee
 */
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    FirstUserMapper firstUserMapper;
    @Autowired
    SecondUserMapper secondUserMapper;
 
    @Autowired
    JtaTransactionManager jtaTransactionManager;
 
    //1.使用注释型@Transactional
    //2.事物开始后,由于除以0的异常,全局事务触发rollback(DB1和DB2都触发rollback)
    //虽然DB1和DB2执行了insert,但是由于两阶段提交模型,在事务结束前抛出了异常,因此DB1和DB2数据并没有更改
    @Transactional(rollbackFor = Throwable.class)
    public void insertTwoDBWithTXRollback(String name) {
        User user = new User();
        user.setName(name);
        firstUserMapper.insert(user);
        secondUserMapper.insert(user);
 
        // 主动触发回滚
        int i = 1/0;
    }
}

而业务逻辑UserService里面的insertTwoDBWithTX能够成功执行,因为没有任何异常发生,所以分别向DB1和DB2插入了数据。

 
/**
 * @description:
 * @author: Lydia Lee
 */
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    FirstUserMapper firstUserMapper;
    @Autowired
    SecondUserMapper secondUserMapper;
 
    @Autowired
    JtaTransactionManager jtaTransactionManager;
 
    @Override
    //1.使用注释型@Transactional
    //2.成功向DB1和DB2插入数据,并提交成功
    @Transactional(rollbackFor = Throwable.class)
    public void insertTwoDBWithTX(String name) {
        User user = new User();
        user.setName(name);
        // 会回滚
        firstUserMapper.insert(user);
        // 不会回滚
        secondUserMapper.insert(user);
    }
 
}

 

可以运行test包下的相关方法运行事务并显示数据库存储内容。

Atomikos对分布式事务的实现,从本demo工程来看,可以在类com.atomikos.icatch.imp.CompositeTransactionImp处设一个断点(下图所示位置),运行debug模式,看到在每次dataSoure执行sql语句的prepareStatement阶段,会注册一个Synchronization。这里不详述,感兴趣的朋友可以看源码。这里简述下,Atomikos对每个分布式事务有一个compositeTransaction,用于记录多个数据源。单个资源参与到分布式事务中时,会注册资源到这个分布式事务的上下文中,那么各个资源都注册后,事务管理器就会直到如何去通知他们一起提交或者回滚。

附上源码的github地址:

GitHub - LuzhouyueLee/spring-demo

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值