为了账号安全,请及时绑定邮箱和手机立即绑定

springboot atomikos 多数据源分布式事务

标签:
Spring
  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 thespring-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

        <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-logsspring.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=falsespring.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=truespring.datasource.druid.system-db.testOnBorrow=falsespring.datasource.druid.system-db.testOnReturn=falsespring.datasource.druid.system-db.poolPreparedStatements=truespring.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=truespring.datasource.druid.business-db.name=business-db
spring.datasource.druid.business-db.url=jdbc:mysql://localhost:3306/test2?useSSL=falsespring.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=truespring.datasource.druid.business-db.testOnBorrow=falsespring.datasource.druid.business-db.testOnReturn=falsespring.datasource.druid.business-db.poolPreparedStatements=truespring.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 数据源的配置类

import javax.sql.DataSource;import org.apache.ibatis.session.SqlSessionFactory;import org.mybatis.spring.SqlSessionFactoryBean;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 com.gitee.taven.config.prop.SystemProperties;import com.gitee.taven.utils.PojoUtil;@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);        return ds;
    }    @Bean
    @Primary
    public SqlSessionFactory systemSqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(systemDataSource());        return sqlSessionFactoryBean.getObject();
    }
    
}
  • business 数据源的配置类同上

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

@Servicepublic 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;
    }
    
}



作者:殷天文
链接:https://www.jianshu.com/p/f9bac5822d30


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消