这个问题困扰我好几天,今天稍微总结一下,以后还得继续 deep dig。不过,今天长沙的阳光真的是甚好啊!
首先,说说这个数据源切换的使用情景:因为项目用到了多个数据源的数据,有时候得切换一下数据源,去另外一个数据源下获取数据。
那么代码到底要怎么写呢?
自定义一个类去继承AbstractRoutingDataSource类并重写其determineCurrentLookupKey()方法
先直接贴我项目中的代码:
一:首先是spring中的数据源配置:
数据源的实现类是 DynamicDataSource ,等下会介绍
然后是两个数据源的配置 :dictdataSource 和defaultdataSource
二:DynamicDataSource
package com.zhiguangyun.modules.empi.util;
/**
* 实现数据库的动态切换
*/
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
return getDataSourceType();
}
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
/**
* @Description: 设置数据源类型
* @param dataSourceType 数据库类型
* @return void
* @throws
*/
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
/**
* @Description: 获取数据源类型
* @param
* @return String
* @throws
*/
public static String getDataSourceType() {
return contextHolder.get();
}
/**
* @Description: 清除数据源类型
* @param
* @return void
* @throws
*/
public static void clearDataSourceType() {
contextHolder.remove();
}
}
关键代码:
三.调用切换数据源的代码
以上就是我项目中实现数据源切换的代码,然后就聊一下这个流程。
首先 DynamicDataSource 继承了AbstractRoutingDataSource 类, 并实现其抽象方法determineCurrentLookupKey()
AbstractRoutingDataSource 继承了 AbstractDataSource类 并且实现了 InitializingBean接口
那么InitializingBean 接口又有什么属性呢?
InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是实现该接口的类,在初始化bean的时候都会执行该方法。这个afterPropertiesSet 方法 甚至 先于 init-method 方法执行。那么当在spring配置中数据源初始化的时候,它就会去调用 afterPropertiesSet 方法。
这是AbstractRoutingDataSource 类中,重写的afterPropertiesSet()方法
从上面可以看到,它将 我们在 spring中配置的 两个数据源的信息存入了 resolveDataSources这个map中,回顾一下先前代码:
(1)AbstractRoutingDataSource 的属性
(2)spring 中的数据源配置
spring 的配置中是将 默认数据源信息存到了 key值为 defaultTargetDataSource 的map中,而要切换的数据源信息存到了key值为 targetDataSources 的map中,这和AbstractRoutingDataSource类中的属性是对应的。当spring 初始化 完bean的属性后,就会调用afterPropertiesSet这个方法,然后将 数据源的信息又存到了 resolveDataSources 这个map中,方便后面根据key从这个map中拿dataSource。
Mybatis只有在真正执行sql操作的时候才会去获取数据库连接。我们看下DataSource中 Connection getConnection() throws SQLException;是如何实现的:
关键在于 determineTargetDataSource()方法中用到了 determineCurrentLookupKey()方法,而determineCurrentLookupKey()是一个抽象方法,要子类是实现。其实这里,就是数据源切换的核心代码了,因为这里返回一个你要切换的数据源去connection。
Object lookupKey = determineCurrentLookupKey();determineCurrentLookupKey()方法是我们DynamicDataSource类实现的。
切换数据源的时候,首先将要切换的那个数据源的名字 通过 setDataSource中,也就是存到ThreadLocal中。然后 determineCurrentLookupKey 再去获取ThreadLocal中保存的 key 值。拿到了这个要切换的key(也就是那个数据源的名字)之后, 再从afterPropertiesSet()中存储好了的resolvedDataSources这个map中获得key对应的dataSource,再返回给 Connection getConnection()去进行数据源的连接。
这就是整个数据源切换的流程。
其实 ,由于基础不是很好,对于这个流程,我自己还有些地方比较困惑,希望看到我这篇博客的朋友,可以指正。