dynamic-datasource多数据源事务

Spring 事务管理分为编程式和声明式两种 编程式事务指的是通过编码方式实现事务;声明式事务基于 AOP,将具体的逻辑与事务处理解耦(编程式事务在这边不做过多陈述)。

声明式事务有两种方式 ,一种是在配置文件 (XML)中做相关的事务规则声明,另一种是基于 @Transactional 注解的方式。

先看一张思维导图(来自互联网)

@Transactional注解

@Transactional实质是使用了JDBC的事务来进行事务控制的

@Transactional基于Spring的动态代理的机制

@Transactional实现原理

1:事务开始时,通过AOP机制,生成一个代理connection对象,并将其放入DataSource实例的某个与DataSourceTransactionManager相关的某处容器中。

在接下来的整个事务中,客户代码都应该使用该connection连接数据库,执行所有数据库命令[不使用该connection连接数据库执行的数据库命令,在本事务回滚的时候得不到回滚](物理连接connection逻辑上新建一个会话session;DataSource与TransactionManager配置相同的数据源)

2:事务结束时,回滚在第1步骤中得到的代理connection对象上执行的数据库命令,然后关闭该代理connection对象(事务结束后,回滚操作不会对已执行完毕的SQL操作命令起作用)

事务失效

通过@Transactional实现原理可以发现;

  1. Spring@Transactional不支持跨数据源事物,Spring 事物控制是基于数据库链接进行的,当数据源切换后,数据库链接切换,事物回滚只能回退,当前持有的链接。
  2. Spring开启事物后,会将当前数据库及数据库链接资源进行线程绑定,导致数据源切换失效(数据源切换执行后,并未获取到新的数据库链接)

怎么解决多数据源问题

新平台目前使用的是@DS注解来切换数据源,通过DynamicDataSourceAnnotationInterceptor我们可以看到,实现的是MethodInterceptor,在通过注解进行数据源切换,由于@Transactional对数据库连接绑定,导致@DS切换不生效;

java 复制代码
public Object invoke(MethodInvocation invocation) throws Throwable {
        String dsKey = this.determineDatasourceKey(invocation);
        DynamicDataSourceContextHolder.push(dsKey);

        Object var3;
        try {
            var3 = invocation.proceed();
        } finally {
            DynamicDataSourceContextHolder.poll();
        }

        return var3;
    }

    private String determineDatasourceKey(MethodInvocation invocation) {
        String key = this.dataSourceClassResolver.findKey(invocation.getMethod(), invocation.getThis());
        return key.startsWith("#") ? this.dsProcessor.determineDatasource(invocation, key) : key;
    }

解决方案

由于事务的开启是通过AOP实现的,而service 方法内调用绕过了AOP拦截,为了解决这个问题可以使用

java 复制代码
(Tservice)AopContext.currentProxy().test1();

通过@Transactional原理可知@Transactional是单事务,因此在具体的方法上

```java
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void test1(){

但是这样就没法控制多数据源事务;

mybatis-plus解决方案

mybatis-plus官方提供了@DSTransactional注解

java 复制代码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DSTransactional {
}

在没有seata的情况下,通过DynamicDataSourceAnnotationAdvisor 管理事务,DynamicDataSourceAnnotationAdvisor的拦截器是DynamicLocalTransactionInterceptor

java 复制代码
    @Role(2)
    @Bean
    @ConditionalOnProperty(
        prefix = "spring.datasource.dynamic",
        name = {"seata"},
        havingValue = "false",
        matchIfMissing = true
    )
    public Advisor dynamicTransactionAdvisor() {
        DynamicLocalTransactionInterceptor interceptor = new DynamicLocalTransactionInterceptor();
        return new DynamicDataSourceAnnotationAdvisor(interceptor, DSTransactional.class);
    }

具体的大家可以自行了解一下;

应该怎么做

通过上面的说明,想必大家已经知道,我们应该怎样做

java 复制代码
    @DSTransactional
    public int addEn(Users users) {

        (Tservice)AopContext.currentProxy().insertUser(users);
        (Tservice)AopContext.currentProxy().insertUser1(users);

        if ("aa".equals(users.getName())) {
            throw new RuntimeException();
        }
        return 1;

    }


    public void insertUser(Users users) {
        usersMapper.insertUser(users);
    }


    public void insertUser1(Users users) {
        usersMapper.insertUser1(users);
    }

usermapper

java 复制代码
    @DS("master")
    Long insertUser(Users users);

    @DS("slave")
    Long insertUser1(Users users);

后续改进

Spring Boot整合了Atomikos和Bitronix两个JTA(Java Transaction API)的实现,但在2.3.0的版本中Bitronix不推荐使用Bitronix了。Atomikos提供了两款分布式事务产品级解决方案,分别是ExtremeTransactions和TransactionsEssentials,前者需要商业授权,后者是开源免费使用版本,TransactionsEssentials是免费的,可无缝升级为ExtremeTransactions版本;后续我们可以整合Atomikos,目前若依也给出了整合方案;

相关推荐
柒小毓2 分钟前
将excel导入SQL数据库
数据库
bug菌¹7 分钟前
滚雪球学Oracle[2.5讲]:数据库初始化配置
数据库·oracle·数据库初始化·初始化配置
一休哥助手15 分钟前
Redis 五种数据类型及底层数据结构详解
数据结构·数据库·redis
落落落sss18 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
翔云12345623 分钟前
MVCC(多版本并发控制)
数据库·mysql
代码敲上天.39 分钟前
数据库语句优化
android·数据库·adb
盒马盒马1 小时前
Redis:zset类型
数据库·redis
静听山水1 小时前
mysql语句执行过程
数据库·mysql
虽千万人 吾往矣1 小时前
golang gorm
开发语言·数据库·后端·tcp/ip·golang
mariokkm2 小时前
Django一分钟:在Django中怎么存储树形结构的数据,DRF校验递归嵌套模型的替代方案
数据库·django·sqlite