多数据源处理
预备知识-ThreadLocal
ThreadLocal是线程局部变量,所谓的线程局部变量,就是仅仅只能被本线程访问,不能在线程之间进行共享访问的变量。
关键抽象类-AbstractRoutingDataSource
官方注释如下:
- Abstract {@link javax.sql.DataSource} implementation that routes {@link #getConnection()}
- calls to one of various target DataSources based on a lookup key. The latter is usually
- (but not necessarily) determined through some thread-bound transaction context.
大概意思是:
就是getConnection()根据查找lookup key键对不同目标数据源的调用,通常是通过(但不一定)某些线程绑定的事物上下文来实现。
通过这我们知道可以实现:
- 多数据源的动态切换,在程序运行时,把数据源数据源动态织入到程序中,灵活的进行数据源切换。
- 基于多数据源的动态切换,我们可以实现读写分离,这么做缺点也很明显,无法动态的增加数据源。
逻辑思路
- DynamicDataSource继承AbstractRoutingDataSource类,并实现了determineCurrentLookupKey()方法。
- 我们配置的多个数据源会放在AbstractRoutingDataSource的 targetDataSources和defaultTargetDataSource中,然后通过afterPropertiesSet()方法将数据源分别进行复制到resolvedDataSources和resolvedDefaultDataSource中。
- AbstractRoutingDataSource的getConnection()的方法的时候,先调用determineTargetDataSource()方法返回DataSource在进行getConnection()。
实现多数据源
- 步骤1,在spring boot中,增加多数据源的配置
- 步骤2,扩展Spring的AbstractRoutingDataSource抽象类,
AbstractRoutingDataSource中的抽象方法determineCurrentLookupKey是实现多数据
源的核心,并对该方法进行Override - 步骤3,配置DataSource,指定数据源的信息
- 步骤4,通过注解,实现多数据源
- 步骤5、配置加上(exclude={DataSourceAutoConfiguration.class})
关于事务
只支持单库事务,也就是说切换数据源要在开启事务之前执行。 spring DataSourceTransactionManager进行事务管理,开启事务,会将数据源缓存到DataSourceTransactionObject对象中进行后续的commit rollback等事务操作。
使用经验
出现多数据源动态切换失败的原因是因为在事务开启后,数据源就不能再进行随意切换了,也就是说,一个事务对应一个数据源。那么传统的Spring管理事务是放在Service业务层操作的,所以更换数据源的操作要放在这个操作之前进行。也就是切换数据源操作放在Controller层,可是这样操作会造成Controller层代码混乱的结果。故而想到的解决方案是将事务管理在数据持久 (Dao层) 开启,切换数据源的操作放在业务层进行操作,就可在事务开启之前顺利进行数据源切换,不会再出现切换失败了。