這個問題困擾我好幾天,今天稍微總結一下,以後還得繼續 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()去進行數據源的連接。
這就是整個數據源切換的流程。
其實 ,由於基礎不是很好,對於這個流程,我自己還有些地方比較困惑,希望看到我這篇博客的朋友,可以指正。