簡介
基本信息
Spring配置多數據源的方式和具體使用過程。
Spring對於多數據源,以資料庫表為參照,大體上可以分成兩大類情況:
一是,表級上的跨資料庫。即,對於不同的資料庫卻有相同的表(表名和表結構完全相同)。
二是,非表級上的跨資料庫。即,多個數據源不存在相同的表。
Spring2.x的版本中採用Proxy模式,就是我們在方案中實現一個虛擬的數據源,並且用它來封裝數據源選擇邏輯,這樣就可以有效地將數據源選擇邏輯從Client中分離出來。Client提供選擇所需的上下文(因為這是Client所知道的),由虛擬的DataSource根據Client提供的上下文來實現數據源的選擇。
實現
具體的實現就是,虛擬的DataSource僅需繼承AbstractRoutingDataSource實現determineCurrentLookupKey()在其中封裝數據源的選擇邏輯。
一、動態配置多數據源
1. 數據源的名稱常量類:
package com.frogking.datasource;
public class DataSourceConst {
public static final String Admin="Admin";
public static final String User = "User";
}
2. 建立一個獲得和設定上下文環境的類,主要負責改變上下文數據源的名稱: package com.frogking.datasource;
public class DataSourceContextHolder {
private static final ThreadLocal contextHolder =
new ThreadLocal(); // 執行緒本地環境
// 設定數據源類型
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
// 獲取數據源類型
public static String getDataSourceType() {
return (String) contextHolder.get();
}
// 清除數據源類型
public static void clearDataSourceType () {
contextHolder.remove();
}
}
3. 建立動態數據源類,注意,這個類必須繼承AbstractRoutingDataSource,且實現方法determineCurrentLookupKey,該方法返回一個Object,一般是返回字元串: package com.frogking.datasource;
publicclass DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// 在進行DAO操作前,通過上下文環境變數,獲得數據源的類型
return DataSourceContextHolder. getDataSourceType();
}
}
4. 編寫spring的配置檔案配置多個數據源
<!-- 數據源相同的內容 -->
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="parentDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>1234</value>
</property>
</bean>
<!-- 以下配置各個數據源的特性 -->
<!-- 資料庫test -->
<bean parent="parentDataSource" id="adminDataSource">
<property name="url">
<value>jdbc:mysql://localhost:3306/test</value>
</property>
</bean>
<!-- 不同的資料庫test2 -->
<bean parent="parentDataSource" id="userDataSource">
<property name="url">
<value>jdbc:mysql://localhost:3306/test2</value>
</property>
</bean>
<!-- end 配置各個數據源的特性 -->
5. 編寫spring配置檔案配置多數據源映射關係
<bean class="com.frogking.datasource.DynamicDataSource" id="dataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry value-ref=" adminDataSource " key="Admin"></entry>
<entry value-ref=" userDataSource " key="User"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="adminDataSource" >
</property>
</bean>
在這個配置中第一個property屬性配置目標數據源,<map key-type="java.lang.String">中的key-type必須要和靜態鍵值對照類DataSourceConst中的值的類型相 同;<entry key="User" value-ref="userDataSource"/>中key的值必須要和靜態鍵值對照類中的值相同,如果有多個值,可以配置多個< entry>標籤。第二個property屬性配置默認的數據源。
6. 配置hibernate,和普通的hibernate、spring結合的配置一樣:
<!-- sessionFactory的配置 -->
<BEAN class=org.springframework.orm.hibernate3.LocalSessionFactoryBean id=sessionFactory>
<property name="dataSource">
<REF local="dataSource"> </REF>
</property>
<!-- 實體類資源映射 -->
<property name="mappingResources">
<LIST>
<VALUE>com/frogking/entity/User.hbm.xml </VALUE>
<VALUE>com/frogking/entity/Admin.hbm.xml</VALUE> </LIST>
</property>
<!-- 為sessionFactory 配置Hibernate屬性 -->
<property name="hibernateProperties">
<PROPS>
<PROP key="hibernate.dialect"> org.hibernate.dialect.MySQLDialect
</PROP>
<PROP key="hibernate.show_sql">true</PROP> <PROP key="hibernate.connection.autocommit">false</PROP> <PROP key="hibernate.cache.use_query_cache">false</PROP> <PROP key="hibernate.max_fetch_depth">2</PROP> <PROP key="hibernate.bytecode.use_reflection_optimizer">true</PROP>
</PROPS>
</property>
</BEAN>
<!-- 為dao配置sessionFactory -->
<BEAN class="com.frogking.dao.LoginHibernateDao" id=loginDao>
<property name="sessionFactory">
<REF local="sessionFactory"> </REF>
</property>
</BEAN>
基於spring和ibatis的多數據源切換方案
基本介紹
在僅使用ibatis時,多數據源簡直就是夢魘,每多一個數據源就需要多一份sql-map-config配置檔案。
採用spring的AbstractRoutingDataSource就可以簡單的解決這個問題。
AbstractRoutingDataSource實現了javax.sql.DataSource接口,因此可以理解為一個虛擬的動態DataSource,在需要的時候根據上下文Context動態決定使用哪個數據源。
該方案的優勢
首先,這個方案完全是在spring的框架下解決的,數據源依然配置在spring的配置檔案中,sessionFactory依然去配置它的dataSource屬性,它甚至都不知道dataSource的改變。唯一不同的是在真正的dataSource與sessionFactory之間增加了一個MultiDataSource。
其次,實現簡單,易於維護。這個方案雖然我說了這么多東西,其實都是分析,真正需要我們寫的代碼就只有MultiDataSource、SpObserver兩個類。MultiDataSource類真正要寫的只有getDataSource()和getDataSource(sp)兩個方法,而SpObserver類更簡單了。實現越簡單,出錯的可能就越小,維護性就越高。
最後,這個方案可以使單數據源與多數據源兼容。這個方案完全不影響BUS和DAO的編寫。如果我們的項目在開始之初是單數據源的情況下開發,隨著項目的進行,需要變更為多數據源,則只需要修改spring配置,並少量修改MVC層以便在請求中寫入需要的數據源名,變更就完成了。如果我們的項目希望改回單數據源,則只需要簡單修改配置檔案。這樣,為我們的項目將增加更多的彈性。
該方案的缺點
沒有能夠解決多用戶訪問單例“sessionFactory”時共享“dataSource”變數,導致產生爭搶“dataSource”的結果,本質類似於作業系統中的“生產者消費者”問題。因此當多用戶訪問時,多數據源可能會導致系統性能下降的後果。