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时都会执行回滚

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

相关文章

  • 使用Spring Context实现消息队列的示例代码

    使用Spring Context实现消息队列的示例代码

    在现代分布式系统中,消息队列是一个非常重要的组件,它可以解耦系统的各个部分,提高系统的可伸缩性和可靠性,本文将详细介绍如何使用Spring Boot实现消息队列,包括消息的发送和接收,需要的朋友可以参考下
    2025-10-10
  • Springboot项目如何异步提高接口的响应速度

    Springboot项目如何异步提高接口的响应速度

    这篇文章主要介绍了Springboot项目如何异步提高接口的响应速度方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-06-06
  • mybatis-plus查询无数据问题及解决

    mybatis-plus查询无数据问题及解决

    这篇文章主要介绍了mybatis-plus查询无数据问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • mybatis动态新增(insert)和修改(update)方式

    mybatis动态新增(insert)和修改(update)方式

    这篇文章主要介绍了mybatis动态新增(insert)和修改(update)方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05
  • Spring bean 四种注入方式详解

    Spring bean 四种注入方式详解

    这篇文章主要介绍了Spring bean的实例化和IOC依赖注入详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2021-07-07
  • Sharding-Proxy分库分表的实现方案

    Sharding-Proxy分库分表的实现方案

    本文主要介绍了Sharding-Proxy分库分表的实现方案,包括配置文件修改和数据操作,同时指出分库时可能出现的数据倾斜问题,具有一定的参考价值,感兴趣的可以了解一下
    2025-08-08
  • HttpClient实现表单提交上传文件

    HttpClient实现表单提交上传文件

    这篇文章主要为大家详细介绍了HttpClient实现表单提交上传文件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • Intellij IDEA连接Navicat数据库的方法

    Intellij IDEA连接Navicat数据库的方法

    这篇文章主要介绍了Intellij IDEA连接Navicat数据库的方法,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借价值,需要的朋友可以参考下
    2021-03-03
  • 从概念到实践的深度解析Java 面向对象进阶之多态

    从概念到实践的深度解析Java 面向对象进阶之多态

    多态作为Java面向对象编程的重要特性,通过继承、方法重写和父类引用指向子类对象,实现了同一操作在不同对象上的多样化行为,本文给大家介绍从概念到实践的深度解析Java 面向对象进阶之多态,感兴趣的朋友一起看看吧
    2025-06-06
  • log4j2.xml文件详解及在日志中加入全局guid

    log4j2.xml文件详解及在日志中加入全局guid

    这篇文章主要介绍了log4j2.xml文件详解及在日志中加入全局guid,基于很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12

最新评论