Spring動態切換多數據源解決方案


 

       Spring動態配置多數據源,即在大型應用中對數據進行切分,並且采用多個數據庫實例進行管理,這樣可以有效提高系統的水平伸縮性。而這樣的方案就會不同於常見的單一數據實例的方案,這就要程序在運行時根據當時的請求及系統狀態來動態的決定將數據存儲在哪個數據庫實例中,以及從哪個數據庫提取數據。

       Spring2.x以后的版本中采用Proxy模式,就是我們在方案中實現一個虛擬的數據源,並且用它來封裝數據源選擇邏輯,這樣就可以有效地將數據源選擇邏輯從Client中分離出來。Client提供選擇所需的上下文(因為這是Client所知道的),由虛擬的DataSource根據Client提供的上下文來實現數據源的選擇。

實現

具體的實現就是,虛擬的DataSource僅需繼承AbstractRoutingDataSource實現determineCurrentLookupKey()在其中封裝數據源的選擇邏輯。
 
一、動態配置多數據源
1. 數據源的名稱常量類:
[java]  view plain copy print ?
 
  1. /** 
  2.  * 動態配置多數據源 
  3.  * 數據源的名稱常量類 
  4.  * @author LONGHUI_LUO 
  5.  * 
  6.  */  
  7. public class DataSourceConst {  
  8.     public static final String TEST="test";  
  9.     public static final String USER="User";  
  10. }  

2. 建立一個獲得和設置上下文環境的類,主要負責改變上下文數據源的名稱:
 
[java]  view plain copy print ?
 
  1. /** 
  2.  * 獲得和設置上下文環境 主要負責改變上下文數據源的名稱 
  3.  *  
  4.  * @author LONGHUI_LUO 
  5.  *  
  6.  */  
  7. public class DataSourceContextHolder {  
  8.     private static final ThreadLocal contextHolder = new ThreadLocal(); // 線程本地環境  
  9.   
  10.     // 設置數據源類型  
  11.     public static void setDataSourceType(String dataSourceType) {  
  12.         contextHolder.set(dataSourceType);  
  13.     }  
  14.   
  15.     // 獲取數據源類型  
  16.     public static String getDataSourceType() {  
  17.         return (String) contextHolder.get();  
  18.     }  
  19.   
  20.     // 清除數據源類型  
  21.     public static void clearDataSourceType() {  
  22.         contextHolder.remove();  
  23.     }  
  24.   
  25. }  


3. 建立動態數據源類,注意,這個類必須繼承AbstractRoutingDataSource,且實現方法determineCurrentLookupKey,該方法返回一個Object,一般是返回字符串:

 
[java]  view plain copy print ?
 
  1. /** 
  2.  * 建立動態數據源 
  3.  *  
  4.  * @author LONGHUI_LUO 
  5.  *  
  6.  */  
  7. public class DynamicDataSource extends AbstractRoutingDataSource {  
  8.   
  9.  protected Object determineCurrentLookupKey() {  
  10.   // 在進行DAO操作前,通過上下文環境變量,獲得數據源的類型  
  11.   return DataSourceContextHolder.getDataSourceType();  
  12.  }  
  13.   
  14. }  


4. 編寫spring的配置文件配置多個數據源

[html]  view plain copy print ?
 
  1.         <!-- 數據源相同的內容 -->  
  2. <bean  
  3.         id="parentDataSource"  
  4.         class="org.apache.commons.dbcp.BasicDataSource"  
  5.         destroy-method="close">  
  6.         <property  
  7.             name="driverClassName"  
  8.             value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />  
  9.         <property name="username" value="sa" />  
  10.         <property name="password" value="net2com" />  
  11. </bean>  
[html]  view plain copy print ?
 
  1. <!-- start以下配置各個數據源的特性 -->  
  2. <bean parent="parentDataSource" id="testDataSource">   
  3.         <propertynamepropertyname="url" value="jdbc:sqlserver://localhost:1433;databaseName=test" />  
  4. </bean>   
  5. <bean parent="parentDataSource" id="UserDataSource">   
  6.             <property  
  7.             name="url"  
  8.             value="jdbc:sqlserver://localhost:1433;databaseName=User" />  
  9. </bean>   
[html]  view plain copy print ?
 
  1. <!-- end 配置各個數據源的特性 -->  


5. 編寫spring配置文件配置多數據源映射關系

[html]  view plain copy print ?
 
  1. <bean class="com.xxxx.datasouce.DynamicDataSource" id="dataSource">  
  2.     <property name="targetDataSources">   
  3.        <map key-type="java.lang.String">   
  4.            <entry value-ref="testDataSource" key="test"></entry>  
  5.            <entry value-ref="UserDataSource" key="User"></entry>  
  6.        </map>   
  7.     </property>   
  8.     <property name="defaultTargetDataSource" ref="testDataSource" ></property>  
  9. </bean>  

        在這個配置中第一個property屬性配置目標數據源,<map key-type="java.lang.String">中的key-type必須要和靜態鍵值對照類DataSourceConst中的值的類型相 同;<entry key="User" value-ref="userDataSource"/>中key的值必須要和靜態鍵值對照類中的值相同,如果有多個值,可以配置多個< entry>標簽。第二個property屬性配置默認的數據源。

 

動態切換是數據源

[java]  view plain copy print ?
 
  1. DataSourceContextHolder.setDataSourceType(DataSourceConst.TEST);  


 

該方案的優勢

       首先,這個方案完全是在spring的框架下解決的,數據源依然配置在spring的配置文件中,sessionFactory依然去配置它的dataSource屬性,它甚至都不知道dataSource的改變。唯一不同的是在真正的dataSource與sessionFactory之間增加了一個MultiDataSource。
其次,實現簡單,易於維護。這個方案雖然我說了這么多東西,其實都是分析,真正需要我們寫的代碼就只有MultiDataSource、SpObserver兩個類。MultiDataSource類真正要寫的只有getDataSource()和getDataSource(sp)兩個方法,而SpObserver類更簡單了。實現越簡單,出錯的可能就越小,維護性就越高。
最后,這個方案可以使單數據源與多數據源兼容。這個方案完全不影響BUS和DAO的編寫。如果我們的項目在開始之初是單數據源的情況下開發,隨着項目的進行,需要變更為多數據源,則只需要修改spring配置,並少量修改MVC層以便在請求中寫入需要的數據源名,變更就完成了。如果我們的項目希望改回單數據源,則只需要簡單修改配置文件。這樣,為我們的項目將增加更多的彈性。

該方案的缺點

       沒有能夠解決多用戶訪問單例“sessionFactory”時共享“dataSource”變量,導致產生爭搶“dataSource”的結果,本質類似於操作系統中的“生產者消費者”問題。因此當多用戶訪問時,多數據源可能會導致系統性能下降的后果。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM