SpringBoot 2.0 集成Atomikos、Durid 实现多数据源、分布式事务

本文详细介绍了如何在SpringBoot中使用Atomikos来管理多数据源的事务一致性,解决了使用AbstractRoutingDataSource配置多数据源时事务切换数据源不生效的问题。通过引入spring-boot-starter-jta-atomikos依赖并配置AtomikosDataSourceBean,实现了跨数据源的事务管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作者:殷天文

链接:https://www.jianshu.com/p/f9bac5822d30

之前的 《spring 动态切换、添加数据源实现以及源码浅析》 (https://www.jianshu.com/p/0a485c965b8b)中介绍了如何使用 spring 提供的 AbstractRoutingDataSource 配置多数据源,有了多数据源自然要管理事务的一致性。

上篇文章中提到过配置多数据源的两种方式

  1. 使用AbstractRoutingDataSource

  2. 配置多个 SqlSessionFactory

前言

阅读了一下spring的源码,由于 spring 事务的机制,在开启事务之前spring 会去创建当前数据源的 事务object,直到事务提交,spring 都不会在乎你是否切换了数据源。这就导致了,使用 AbstractRouting DataSource 方式开启事务时,切换数据源不生效。

关于如何解决这个问题,感兴趣的朋友可以去阅读一下:https://www.jianshu.com/p/61e8961c6154

本文只讨论上述第二种方式结合 atomikos 管理多数据源事务。

Atomikos

来自:http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-jta.html

Atomikos is a popular open source transaction manager which can be embedded into your Spring Boot application. You can use the spring-boot-starter-jta-atomikos Starter to pull in the appropriate Atomikos libraries. Spring Boot auto-configures Atomikos and ensures that appropriate depends-on settings are applied to your Spring beans for correct startup and shutdown ordering.

By default, Atomikos transaction logs are written to a transaction-logs directory in your application's home directory (the directory in which your application jar file resides). You can customize the location of this directory by setting a spring.jta.log-dir property in your application.properties file. Properties starting with spring.jta.atomikos.properties can also be used to customize the Atomikos UserTransactionServiceImp. See the AtomikosProperties Javadoc for complete details.

引入spring-boot-starter-jta-atomikos,spring boot 为我们自动配置

Atomikos,我们可以通过 spring.jta.xxx 修改默认配置。

Talk is cheap. Show me the code

  • demo源码:https://gitee.com/yintianwen7/taven-springboot-learning/tree/master/spring-atomikos

  • 添加 maven 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.1</version>
</dependency>
  • application.properties

#spring.jta.log-dir=classpath:tx-logs
spring.jta.transaction-manager-id=txManager
spring.datasource.druid.system-db.name=system-db
spring.datasource.druid.system-db.url=jdbc:mysql://localhost:3306/test1?useSSL=false
spring.datasource.druid.system-db.username=root
spring.datasource.druid.system-db.password=taven753
spring.datasource.druid.system-db.initialSize=5
spring.datasource.druid.system-db.minIdle=5
spring.datasource.druid.system-db.maxActive=20
spring.datasource.druid.system-db.maxWait=60000
spring.datasource.druid.system-db.timeBetweenEvictionRunsMillis=60000
spring.datasource.druid.system-db.minEvictableIdleTimeMillis=30000
spring.datasource.druid.system-db.validationQuery=SELECT 1
spring.datasource.druid.system-db.validationQueryTimeout=10000
spring.datasource.druid.system-db.testWhileIdle=true
spring.datasource.druid.system-db.testOnBorrow=false
spring.datasource.druid.system-db.testOnReturn=false
spring.datasource.druid.system-db.poolPreparedStatements=true
spring.datasource.druid.system-db.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.druid.system-db.filters=stat,wall
spring.datasource.druid.system-db.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
spring.datasource.druid.system-db.useGlobalDataSourceStat=true
spring.datasource.druid.business-db.name=business-db
spring.datasource.druid.business-db.url=jdbc:mysql://localhost:3306/test2?useSSL=false
spring.datasource.druid.business-db.username=root
spring.datasource.druid.business-db.password=taven753
spring.datasource.druid.business-db.initialSize=5
spring.datasource.druid.business-db.minIdle=5
spring.datasource.druid.business-db.maxActive=20
spring.datasource.druid.business-db.maxWait=60000
spring.datasource.druid.business-db.timeBetweenEvictionRunsMillis=60000
spring.datasource.druid.business-db.minEvictableIdleTimeMillis=30000
spring.datasource.druid.business-db.validationQuery=SELECT 1
spring.datasource.druid.business-db.validationQueryTimeout=10000
spring.datasource.druid.business-db.testWhileIdle=true
spring.datasource.druid.business-db.testOnBorrow=false
spring.datasource.druid.business-db.testOnReturn=false
spring.datasource.druid.business-db.poolPreparedStatements=true
spring.datasource.druid.business-db.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.druid.business-db.filters=stat,wall
spring.datasource.druid.business-db.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
spring.datasource.druid.business-db.useGlobalDataSourceStat=true
  • system 数据源的配置类

@Configuration
@MapperScan(basePackages = SystemDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "systemSqlSessionFactory")
public class SystemDataSourceConfig {
    static final String PACKAGE = "com.gitee.taven.mapper.system";
    @Autowired
    private SystemProperties systemProperties;
    @Bean(name = "systemDataSource")
    @Primary
    public DataSource systemDataSource() {
        AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
        ds.setXaProperties(PojoUtil.obj2Properties(systemProperties));
        ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
        ds.setUniqueResourceName("systemDataSource");
        ds.setPoolSize(5);
        ds.setTestQuery("SELECT 1");
        return ds;
    }
    @Bean
    @Primary
    public SqlSessionFactory systemSqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(systemDataSource());
        return sqlSessionFactoryBean.getObject();
    }
}

更新于 2019.6.24,在简友LT的帮助下,发现了一个bug,由于MySQL的wait_timeout 机制,会导致数据库连接在长时间未使用的情况(MySQL默认是8小时)下失效

虽然我们配置了Druid的检查机制,但是还要在AtomikosDataSourceBean 的属性上添加一个属性,如下:

AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
// 省略
ds.setTestQuery("SELECT 1");
  • business 数据源的配置类同上

  • 省略了 mybatis 代码,通过service 测试事务,抛出异常后,事务会回滚

@Service
public class UserService {
    @Autowired private UsersMapper usersMapper;
    @Autowired private UserInformationsMapper userInformationsMapper;
    @Transactional
    public void testJTA() {
        Users u = new Users();
        u.setUsername("hmj");
        u.setPassword("hmjbest");
        usersMapper.insertSelective(u);
        UserInformations ui = new UserInformations();
        ui.setUserid(666l);
        ui.setEmail("dsb");
        userInformationsMapper.insertSelective(ui);
//      int i = 10/0;
    }
}
本文Demo

Github(https://github.com/TavenYin/taven-springboot-learning/tree/master/spring-atomikos)


推荐阅读开源博客项目eblog完整搭建教程!SpringBoot 深度调优,JVM 调优(低调,深度好文)权限设计的一些想法和思考实战篇,你不得不懂的Elasticsearch搜索引擎!ElasticSearch 使用 Logstash 从 MySQL 中同步数据网站开发:从写代码到公网访问整个流程两小时入门 Docker(好文推荐)好文!必须点赞
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值