黎明的到來,黑暗將消失殆盡。
我喜歡冷的天氣,因爲他可以讓我毫無顧忌的多睡一會。今天早上再微信公衆好看到這樣一句話,挺勵志的,就拿出來跟大家share下。
I am a slow walker,but I never walk backwards.(我希望大家不是用他來安慰自己的,而是讓自己更有動力向前行)
今天跟大家分享的知識是spring中的動態多數據源的修改,直接點就是用戶可以任意的切換數據庫。
一.首先建立一個可以修改數據源名字的一個類:DatasourceContextHolder
給大家貼代碼:
public class DatasourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static void setDatatsource(String datasoruce) {
contextHolder.set(datasoruce);
}
public static String getDatasource() {
return (String) contextHolder.get();
}
public static void clearDatasource() {
contextHolder.remove();
}
}
大家可能對
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();<pre name="code" class="java">/**
* Retrieve the current target DataSource. Determines the
* {@link #determineCurrentLookupKey() current lookup key}, performs
* a lookup in the {@link #setTargetDataSources targetDataSources} map,
* falls back to the specified
* {@link #setDefaultTargetDataSource default target DataSource} if necessary.
* @see #determineCurrentLookupKey()
*/
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
不是很明白他的具體作用,我在這裏說明下:我們再這個類裏聲明瞭一個本地的線程對象,他主要是可以保存當前調用該類的一個string,他是一個局部的變量,這樣我們在不同的用戶切換數據源的時候就不會有衝突。
上面我們已經實現了database的一個上下文的環境,現在我們要考慮的問題就是如何根據我們修改的值把他set到db的connection呢?下面我們就介紹這部分內容。
二.內部修改DBSource的一個實現
首先上代碼,然後解釋:
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class CustomerRoutingDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
// TODO Auto-generated method stub
return DatasourceContextHolder.getDatasource();
}
}
上面是建立動態數據源類,我們必須要繼承AbstractRoutingDataSource,並且要實現determineCurrentLookupKey方法。這是爲什麼呢,我們一步一步來,我先從概念上說明下: 在Spring 2.0.1中引入了AbstractRoutingDataSource, 該類充當了DataSource的路由中介, 能有在運行時, 根據某種key值來動態切換到真正的DataSource上。
而在我們繼承的AbstractRoutingDataSource類中,他同樣繼承了AbstractDataSource(java.sql.datasource裏面的一個類),我們發現在他的getconnection方法中返回了一個值determineTargetDataSource(),然後我們繼續深入發現了好東西,我展示代碼給大家看:
/**
* Retrieve the current target DataSource. Determines the
* {@link #determineCurrentLookupKey() current lookup key}, performs
* a lookup in the {@link #setTargetDataSources targetDataSources} map,
* falls back to the specified
* {@link #setDefaultTargetDataSource default target DataSource} if necessary.
* @see #determineCurrentLookupKey()
*/
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}
在這裏determineTargetDataSource()中,我們的determineCurrentLookupKey()會返回一個dataSource的一個名字,然後
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
這句話會根據所給的lookupkey從resolvedDataSources中map中找對應的key,如果有就會配置相應的數據源,沒有的沒有就使用默認的數據源,下面我先把map中的部分內容展示給大家看:(ep:lookupKey="b2bappstg",map--->key="b2bappstg")
<beans:bean id="dataSource" class="com.xxx.xxx.xxx.xxx.CustomerRoutingDataSource">
<beans:property name="targetDataSources">
<beans:map>
<beans:entry key="test1" value-ref="Test1" />
<beans:entry key="test2" value-ref="Test2" />
<beans:entry key="test3" value-ref="Test3" />
</beans:map>
</beans:property>
<beans:property name="defaultTargetDataSource" ref="Test1" />
</beans:bean>
接下來的工作我們就比較簡單了,就是在spring配置文件中配置你的多數據源。
三.編寫spring中的多數據源
這裏給大家放一個模板就可以了,然後你只需修改你自己的datasource就可以了:
<beans:bean id="Test1" class="org.springframework.jdbc.datasource.DriverManagerDataSource" abstract="true">
<beans:property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<beans:property name="username" value="xxxxxx" />
<beans:property name="password" value="xxxxxx" />
</beans:bean>
<beans:bean id="Test2" class="org.springframework.jdbc.datasource.DriverManagerDataSource" parent="Test2">
<beans:property name="url" value="oracle.jdbc.driver.OracleDriver" />
</beans:bean>
<beans:bean id="Test3" class="org.springframework.jdbc.datasource.DriverManagerDataSource" parent="Test3">
<beans:property name="url" value="oracle.jdbc.driver.OracleDriver" />
</beans:bean>
<pre name="code" class="html"> <beans:bean id="dataSource" class="com.xxx.xxx.xxx.xxx.CustomerRoutingDataSource">
<beans:property name="targetDataSources">
<beans:map>
<beans:entry key="test1" value-ref="Test1" />
<beans:entry key="test2" value-ref="Test2" />
<beans:entry key="test3" value-ref="Test3" />
</beans:map>
</beans:property>
<beans:property name="defaultTargetDataSource" ref="Test1" />
</beans:bean>
<beans:bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><beans:property name="dataSource" ref="dataSource" />
<beans:property name="mapperLocations" value="classpath*:com/XXXX/XXX/XXX/XXX/*.xml" />
</beans:bean> 以上就全部的配置過程,下面你就可以在你的controller或者action中應用下面這句話就可以完成數據源的動態性。
DatasourceContextHolder.setDatatsource("test1 or test2 or test3");
最後讓大家輕鬆下:
一隻小狗爬上你的餐桌,向一隻燒雞爬去,你大怒道:你敢對那隻燒雞怎樣,我就敢對你怎樣,結果小狗舔了一下雞屁股,你昏倒,小狗樂道:小樣看誰狠。