DynamicDataSource怎样解决多数据源的事务问题

 更新时间:2023年07月31日 10:15:58   作者:Abstracted  
这篇文章主要介绍了DynamicDataSource怎样解决多数据源的事务问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

多数据源的真面目DynamicRoutingDataSource

DynamicRoutingDataSource是什么?

该类实现了DataSource接口并在内部维护了一个map,其中存放了多个真实的DataSource,而key则是不同数据源的名称,本质上就是基于静态代理模式代理了多个真实的数据源对象

在用多数据源时可以使用@DS来切换不同的数据源,这依赖于DynamicRoutingDataSource

要完成这个功能需要

@DS+DSProcessor+ThreadLocal+AopMethodInterceptor+DynamicRoutingDataSource#getConnection

相互配合才能成功切换数据源。

多数据源下的事务会有什么问题?

在没有多数据源时,系统中一般只会存在一个Datasource,所谓的HikariDataSource,DruidDatasourceCommonsDbcp2PoolDataSource都是DataSource的实现,但是在系统中总是唯一存在的。因为SqlSessionTemplateFactory中只能存在一个DataSource,SqlSessionTemplate又是通过SqlSessionTemplateFactory生成的,并且SqlSessionTemplate在容器中又只能存在一个,所以事务时不会出现混乱的。而DynamicRoutingDataSource同样也是系统中的唯一的Datasource,只不过它内部代理了多个DataSource。

再来说说spring的事务管理器TransactionSynchronizationManager,内部使用ThreadLocal保存当前事务信息。当程序执行到@Transactional的AOP拦截器时,会在ThreadLocal中保存当前的事务信息,其中就包含了与数据库的Connection对象。在程序执行sql时,spring的SqlSessionTemplate#getSession方法会从事务管理器中获取到与当前线程绑定的connection对象。

综上所述,当使用@DS切换数据源时,没有事务的情况下还好,会使用DynamicRoutingDataSource获取一个新的connection并使用,但是如果是在事务的情况下,会使用事务管理器中获取与当前线程绑定的Connection,而这个connection则是事务被创建时获取的connection,就造成了虽然指定了数据源,但是还是原本的那个connection。导致切换数据源失败。

@Ds与@DsTransactional

通过观察DynamicDataSourceAutoConfiguration自动配置类可以发现,DynamicDataSource默认自动配置了@Ds注解@DsTransactional注解的切面。

分别是dynamicDatasourceAnnotationAdvisordynamicTransactionAdvisor

@Ds的原理

其切面方法拦截器会将指定的ds名称存入DynamicDataSourceContextHolder中的ThreadLocal<Deque<String>>中,因为指定了ThreadLocalMap的泛型为Deque,而Deque的作用更准确的说是栈的作用,所以支持方法嵌套调用时使用不同的ds名称。

@DsTransactional的原理

通过源码可以发现在没有使用seta分布式事务控制的情况下,多数据源的事务是通过dynamicDatasourceAnnotationAdvisor管理的。

dynamicDatasourceAnnotationAdvisor的核心切面拦截器就是DynamicLocalTransactionInterceptor

DynamicLocalTransactionInterceptor多数据源本地事务拦截器

内部实现原理非常简单,其实就是借助于ThreadLocal。

看到这里,你可能已经猜到了,面纱下面的真面目就是这个ConnectionFactory类了。

我们再来看看他张了一幅什么面貌。

我们已经知道了notify方法是@DsTransactional切面环绕通知结束时会被调用的,本着追溯本源的好奇心,你可能开始好奇了,putConnection又是什么时候被调用的呢?

我们继续跟进就来到了AbstractRoutingDataSource

好!又回到了DynamicRoutingDataSource中, AbstractRoutingDataSource就是DynamicRoutingDataSource父类。

在本地事务结束时,TransactionContext会清空本地事物的状态标识,然后分别结束每一个connection的事务状态。

然后清除ConnectionFactory中保存的与本次本地事物有关的所有connection对象的引用。

至此本地事物的创建和结束就完成了闭环。

总结一下

多数据源事务的控制中,参与的核心职责类有哪些

  • DynamicRoutingDataSource: DataSource接口的实现,也是一个DataSource,本质是多个DataSource的静态代理类。自定义了getConnection方法的实现逻辑,使其与多数据源的事务管理紧密配合。
  • TransactionContext:使用ThreadLocal实现。本地事物上下文。负责管理本地事物的状态。
  • ConnectionFactory: 使用ThreadLocal实现。connection连接的静态代理类,也是一个委派者设计模式。负责管理当前本地事务中的所有Connection。
  • ConnectionProxy: 封装了真实的Connection。 目的是将ds数据源名称和connection对应起来,这样可以通过ds数据源名称拿到对应的connection。
  • @Ds:用于指定接下来要使用的数据源名称。
  • @DsTransactional: 多数据源事务的开启注解,用法同@Transactional相同。但是不能指定事务的一些属性,因为其实现的原理,也不需要任何其他的事务的配置。当发生任何Exception时都会执行回滚

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 基于logback.xml不生效问题的解决

    基于logback.xml不生效问题的解决

    这篇文章主要介绍了基于logback.xml不生效问题的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • java提取字符串中数字string以及获取字符串中的整数或小数

    java提取字符串中数字string以及获取字符串中的整数或小数

    这篇文章主要给大家介绍了关于java提取字符串中数字string以及获取字符串中的整数或小数的相关资料,需要的朋友可以参考下
    2023-08-08
  • Java中的异常Exception与处理方式详解

    Java中的异常Exception与处理方式详解

    这篇文章主要介绍了Java中的异常Exception与处理方式详解, Java语言中,将程序执行中发生的不正常情况称为"异常"(开发过程中的语法错误和逻辑错误不是异常),需要的朋友可以参考下
    2024-01-01
  • JAVA多线程之中断机制及处理中断的方法

    JAVA多线程之中断机制及处理中断的方法

    这篇文章主要记录使用 interrupt() 方法中断线程,以及如何对InterruptedException进行处理,感觉对InterruptedException异常进行处理是一件谨慎且有技巧的活儿,需要的朋友可以参考下
    2023-02-02
  • 深入了解MyBatis参数

    深入了解MyBatis参数

    今天小编就为大家分享一篇关于深入了解MyBatis参数,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • SpringBoot操作Redis三种方案全解析

    SpringBoot操作Redis三种方案全解析

    这篇文章主要介绍了SpringBoot操作Redis三种方案全解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-06-06
  • Java 多线程并发ReentrantLock

    Java 多线程并发ReentrantLock

    这篇文章主要介绍了Java 多线程并发ReentrantLock,Java 提供了 ReentrantLock 可重入锁来提供更丰富的能力和灵活性,感兴趣的小伙伴可以参考一下
    2022-06-06
  • DoytoQuery中的关联查询方案示例详解

    DoytoQuery中的关联查询方案示例详解

    这篇文章主要为大家介绍了DoytoQuery中的关联查询方案示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • centos下GitLab+Jenkins持续集成环境搭建(安装jenkins)

    centos下GitLab+Jenkins持续集成环境搭建(安装jenkins)

    这篇文章主要为大家详细介绍了centos下搭建GitLab+Jenkins持续集成环境,安装jenkins的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-04-04
  • 通过Feign进行调用@FeignClient 找不到的解决方案

    通过Feign进行调用@FeignClient 找不到的解决方案

    这篇文章主要介绍了通过Feign进行调用@FeignClient 找不到的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03

最新评论