? ? ? ?在我們的項(xiàng)目中遇到這樣一個(gè)問題:我們的項(xiàng)目需要連接多個(gè)數(shù)據(jù)庫(kù),而且不同的客戶在每次訪問中根據(jù)需要會(huì)去訪問不同的數(shù)據(jù)庫(kù)。所以就采用了多數(shù)據(jù)源的方式(可以根據(jù)客戶的需求去連接客戶所需要的真正的數(shù)據(jù)源,即提供動(dòng)態(tài)切換數(shù)據(jù)源的功能)。
? ? ? ?多數(shù)據(jù)源配置是怎么個(gè)配置法,其中用到了些什么技術(shù),想必大家都會(huì)有這個(gè)疑問,下面將逐一介紹。
? ? ? ?大概思路是這樣的:在登錄頁(yè)面放置一個(gè)下拉選擇列表(使用的Bootstrap框架的dropdown-menu,不懂的可以百度一下特別好用),下拉列表在加載以前是從后臺(tái)讀取的一個(gè)包含了多個(gè)數(shù)據(jù)庫(kù)信息的Json文件,前臺(tái)通過js循環(huán)渲染出來。另外有一個(gè)子頁(yè)面可以創(chuàng)建新的數(shù)據(jù)庫(kù)保存到剛才的那個(gè)Json文件中。
關(guān)鍵點(diǎn)來了,最主要的是登錄的時(shí)候選了不同的數(shù)據(jù)庫(kù),后臺(tái)是怎么知道并且登錄成功的。
寫一個(gè)DBContextHolder類放一個(gè)多線程的變量記錄當(dāng)前數(shù)據(jù)源,具體實(shí)現(xiàn)類繼承AbstractRoutingDataSource類并且重寫方法determineCurrentLookupKey獲取當(dāng)前數(shù)據(jù)源,如果當(dāng)前數(shù)據(jù)源不存在就新建并且要通知spring容器。
具體代碼如下:
1.datasource.xml配置文件內(nèi)容:
<bean id="datasource"? class="xxxxxxxxxxx.DynamicDataSource">
? ? <property name="targetDataSources"> ? <map></map> ?</property>
</bean>
2.DynamicDataSource.class
package ? ?com.core;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public ?class ?DynamicDataSource ? ?extends ? ?AbstractRoutingDataSource{
? ? /*datasource.xml配置文件中配置數(shù)據(jù)源為此類*/
? ? ?public DynamicDataSource(){ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /*默認(rèn)數(shù)據(jù)源*/
? ? ? ? ? ? ? ? HashMapmap_1 = new HashMap();
? ? ? ? ? ? ? ? ?map_1.put("DRIVER_CLASS", "com.mysql.jdbc.Driver");
? ? ? ? ? ? ? ? map_1.put("dbUrl", "jdbc:mysql://127.0.0.1:3306/ifms? ? ? ? ? ? ? ? ? ? ? ? useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&autoReconnect=true");
? ? ? ? ? ? ? ? ? map_1.put("dbUserName", "root");
? ? ? ? ? ? ? ? ?map_1.put("dbPassword", "123456");
? ? ? ? ? ? ? ? dbMap.put("db0", map_1);
}
? ? @Override
? ? protected ? Object?determineCurrentLookupKey()?{ ? ? ? ? ? /*得到當(dāng)前數(shù)據(jù)源*/
? ? ? ? ? return ? DatabaseContextHolder.getCustomerType();
? ? }
? ? public void setTargetDataSources(MaptargetDataSources) {
? ? ? ? ? ?this._targetDataSources = targetDataSources;
? ? ? ? ? ?super.setTargetDataSources(this._targetDataSources);
? ? ? ? ? ?super.afterPropertiesSet();//當(dāng)我們添加數(shù)據(jù)庫(kù),切換了數(shù)據(jù)源,要通知當(dāng)前spring容器
? ?}
? ? public void addTargetDataSource(String key, BasicDataSource dataSource) {
? ? ? ? ? ?this._targetDataSources.put(key, dataSource);
? ? ? ? ? ?this.setTargetDataSources(this._targetDataSources);
? ?}
? ?public BasicDataSource createDataSource(String driverClassName, String url,
? ? ? ? ? String username, String password) {
? ? ? ? ? BasicDataSource dataSource = new BasicDataSource();
? ? ? ? ? dataSource.setDriverClassName(driverClassName);
? ? ? ? ? dataSource.setUrl(url);
? ? ? ? ? dataSource.setUsername(username);
? ? ? ? ?dataSource.setPassword(password);
? ? ? ? ? dataSource.setTestWhileIdle(true);
? ? ? ? ? return dataSource;
?}
/**
* @param serverId
* @describe 數(shù)據(jù)源存在時(shí)不做處理,不存在時(shí)創(chuàng)建新的數(shù)據(jù)源鏈接,并將新數(shù)據(jù)鏈接添加至緩存
*/
? ? ?public void selectDataSource(String serverId) {
? ? ? ? ? ?Object sid = DBContextHolder.getCustomerType();? ? ? ? ?
? ? ? ? ? ?Object obj = this._targetDataSources.get(serverId);
? ? ? ? ? ? if (obj != null && sid.equals(serverId + "")) {
? ? ? ? ? ? ? ? ? ?return;
? ? ? ? ? ? ?} else {
? ? ? ? ? ? ?System.out.println("---數(shù)據(jù)源不存在,創(chuàng)建數(shù)據(jù)源");
? ? ? ? ? ? ? BasicDataSource dataSource = this.getDataSource(serverId); ?//判斷當(dāng)前數(shù)據(jù)源是否存在
? ? ? ? ? ? ?if (null != dataSource)
? ? ? ? ? ? ? ? ? ? ?this.setDataSource(serverId, dataSource); ? ? ? ? ? ? ? ? ? ? ? ?//設(shè)置當(dāng)前數(shù)據(jù)源
? ? ? ? ? ? ?}
? ? ? ?}
? ? ?public void setDataSource(String serverId, BasicDataSource dataSource) {
? ? ? ? ? this.addTargetDataSource(serverId, dataSource);
? ? ? ? ? DBContextHolder.setCustomerType(serverId);
? ? ?}
}
3.DBContextHolder.class
package ? com.core;
public? class? DBContextHolder{
private ? static ? final ? Thread ? Local?contextHolder?=newThreadLocal();
? ?public ? static ? void ? setCustomerType(String?customerType)?{
? ? ? contextHolder.set(customerType);
? ?}
? ? public ? static ? String?getCustomerType()?{
? ? ? ? return ? contextHolder.get();
? ? ?}
? ?public ? static ? void ? clearCustomerType()?{
? ? ? ?contextHolder.remove();
? ?}
}
? ? ? 其中遇到個(gè)問題,如果將數(shù)據(jù)源變量定義為多線程的時(shí)候,如果前臺(tái)頁(yè)面另起一個(gè)線程并且中途出現(xiàn)異常之后會(huì)獲取不到當(dāng)前數(shù)據(jù)源。所以暫時(shí)改為了一個(gè)靜態(tài)變量但是只能支持單線程。如果有一臺(tái)電腦正用著A數(shù)據(jù)庫(kù),另外一臺(tái)電腦突然用B數(shù)據(jù)庫(kù)登錄,那原來那個(gè)的數(shù)據(jù)庫(kù)也會(huì)變成A。目前還沒有找到好的方法解決這個(gè)問題,找到了會(huì)繼續(xù)更新。如果誰(shuí)有比較好的方法也可以告訴我,灰常感激?。。。。。。。。。。。。。。。。。?/p>