java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > DynamicDataSource解决多数据源的事务

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

作者: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对象的引用。

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

总结一下

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

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

您可能感兴趣的文章:
阅读全文