Spring實(shí)現(xiàn)動(dòng)態(tài)切換多數(shù)據(jù)源的解決方案
前言
Spring動(dòng)態(tài)配置多數(shù)據(jù)源,即在大型應(yīng)用中對(duì)數(shù)據(jù)進(jìn)行切分,并且采用多個(gè)數(shù)據(jù)庫(kù)實(shí)例進(jìn)行管理,這樣可以有效提高系統(tǒng)的水平伸縮性。而這樣的方案就會(huì)不同于常見(jiàn)的單一數(shù)據(jù)實(shí)例的方案,這就要程序在運(yùn)行時(shí)根據(jù)當(dāng)時(shí)的請(qǐng)求及系統(tǒng)狀態(tài)來(lái)動(dòng)態(tài)的決定將數(shù)據(jù)存儲(chǔ)在哪個(gè)數(shù)據(jù)庫(kù)實(shí)例中,以及從哪個(gè)數(shù)據(jù)庫(kù)提取數(shù)據(jù)。
Spring2.x以后的版本中采用Proxy模式,就是我們?cè)诜桨钢袑?shí)現(xiàn)一個(gè)虛擬的數(shù)據(jù)源,并且用它來(lái)封裝數(shù)據(jù)源選擇邏輯,這樣就可以有效地將數(shù)據(jù)源選擇邏輯從Client中分離出來(lái)。Client提供選擇所需的上下文(因?yàn)檫@是Client所知道的),由虛擬的DataSource根據(jù)Client提供的上下文來(lái)實(shí)現(xiàn)數(shù)據(jù)源的選擇。
實(shí)現(xiàn)
具體的實(shí)現(xiàn)就是,虛擬的DataSource僅需繼承AbstractRoutingDataSource實(shí)現(xiàn)determineCurrentLookupKey()
在其中封裝數(shù)據(jù)源的選擇邏輯。
一、動(dòng)態(tài)配置多數(shù)據(jù)源
1. 數(shù)據(jù)源的名稱(chēng)常量類(lèi):
/** * 動(dòng)態(tài)配置多數(shù)據(jù)源 * 數(shù)據(jù)源的名稱(chēng)常量類(lèi) * @author LONGHUI_LUO * */ public class DataSourceConst { public static final String TEST="test"; public static final String USER="User"; }
2. 建立一個(gè)獲得和設(shè)置上下文環(huán)境的類(lèi),主要負(fù)責(zé)改變上下文數(shù)據(jù)源的名稱(chēng):
/** * 獲得和設(shè)置上下文環(huán)境 主要負(fù)責(zé)改變上下文數(shù)據(jù)源的名稱(chēng) * * @author LONGHUI_LUO * */ public class DataSourceContextHolder { private static final ThreadLocal contextHolder = new ThreadLocal(); // 線(xiàn)程本地環(huán)境 // 設(shè)置數(shù)據(jù)源類(lèi)型 public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } // 獲取數(shù)據(jù)源類(lèi)型 public static String getDataSourceType() { return (String) contextHolder.get(); } // 清除數(shù)據(jù)源類(lèi)型 public static void clearDataSourceType() { contextHolder.remove(); } }
3. 建立動(dòng)態(tài)數(shù)據(jù)源類(lèi),注意,這個(gè)類(lèi)必須繼承AbstractRoutingDataSource,且實(shí)現(xiàn)方法determineCurrentLookupKey,該方法返回一個(gè)Object,一般是返回字符串:
/** * 建立動(dòng)態(tài)數(shù)據(jù)源 * * @author LONGHUI_LUO * */ public class DynamicDataSource extends AbstractRoutingDataSource { protected Object determineCurrentLookupKey() { // 在進(jìn)行DAO操作前,通過(guò)上下文環(huán)境變量,獲得數(shù)據(jù)源的類(lèi)型 return DataSourceContextHolder.getDataSourceType(); } }
4. 編寫(xiě)spring的配置文件配置多個(gè)數(shù)據(jù)源
<!-- 數(shù)據(jù)源相同的內(nèi)容 --> <bean id="parentDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" /> <property name="username" value="sa" /> <property name="password" value="net2com" /> </bean> <!-- start以下配置各個(gè)數(shù)據(jù)源的特性 --> <bean parent="parentDataSource" id="testDataSource"> <propertynamepropertyname="url" value="jdbc:sqlserver://localhost:1433;databaseName=test" /> </bean> <bean parent="parentDataSource" id="UserDataSource"> <property name="url" value="jdbc:sqlserver://localhost:1433;databaseName=User" /> </bean> <!-- end 配置各個(gè)數(shù)據(jù)源的特性 -->
5. 編寫(xiě)spring配置文件配置多數(shù)據(jù)源映射關(guān)系
<bean class="com.xxxx.datasouce.DynamicDataSource" id="dataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry value-ref="testDataSource" key="test"></entry> <entry value-ref="UserDataSource" key="User"></entry> </map> </property> <property name="defaultTargetDataSource" ref="testDataSource" ></property> </bean>
在這個(gè)配置中第一個(gè)property屬性配置目標(biāo)數(shù)據(jù)源,<map key-type="java.lang.String">
中的key-type必須要和靜態(tài)鍵值對(duì)照類(lèi)DataSourceConst中的值的類(lèi)型相 同;<entry key="User" value-ref="userDataSource"/>
中key的值必須要和靜態(tài)鍵值對(duì)照類(lèi)中的值相同,如果有多個(gè)值,可以配置多個(gè)< entry>標(biāo)簽。第二個(gè)property屬性配置默認(rèn)的數(shù)據(jù)源。
動(dòng)態(tài)切換是數(shù)據(jù)源
DataSourceContextHolder.setDataSourceType(DataSourceConst.TEST);
該方案的優(yōu)勢(shì)
首先,這個(gè)方案完全是在spring的框架下解決的,數(shù)據(jù)源依然配置在spring的配置文件中,sessionFactory依然去配置它的dataSource屬性,它甚至都不知道dataSource的改變。唯一不同的是在真正的dataSource與sessionFactory之間增加了一個(gè)MultiDataSource。
其次,實(shí)現(xiàn)簡(jiǎn)單,易于維護(hù)。這個(gè)方案雖然我說(shuō)了這么多東西,其實(shí)都是分析,真正需要我們寫(xiě)的代碼就只有MultiDataSource、SpObserver兩個(gè)類(lèi)。MultiDataSource類(lèi)真正要寫(xiě)的只有getDataSource()
和getDataSource(sp)
兩個(gè)方法,而SpObserver類(lèi)更簡(jiǎn)單了。實(shí)現(xiàn)越簡(jiǎn)單,出錯(cuò)的可能就越小,維護(hù)性就越高。
最后,這個(gè)方案可以使單數(shù)據(jù)源與多數(shù)據(jù)源兼容。這個(gè)方案完全不影響B(tài)US和DAO的編寫(xiě)。如果我們的項(xiàng)目在開(kāi)始之初是單數(shù)據(jù)源的情況下開(kāi)發(fā),隨著項(xiàng)目的進(jìn)行,需要變更為多數(shù)據(jù)源,則只需要修改spring配置,并少量修改MVC層以便在請(qǐng)求中寫(xiě)入需要的數(shù)據(jù)源名,變更就完成了。如果我們的項(xiàng)目希望改回單數(shù)據(jù)源,則只需要簡(jiǎn)單修改配置文件。這樣,為我們的項(xiàng)目將增加更多的彈性。
該方案的缺點(diǎn)
沒(méi)有能夠解決多用戶(hù)訪(fǎng)問(wèn)單例“sessionFactory”時(shí)共享“dataSource”變量,導(dǎo)致產(chǎn)生爭(zhēng)搶“dataSource”的結(jié)果,本質(zhì)類(lèi)似于操作系統(tǒng)中的“生產(chǎn)者消費(fèi)者”問(wèn)題。因此當(dāng)多用戶(hù)訪(fǎng)問(wèn)時(shí),多數(shù)據(jù)源可能會(huì)導(dǎo)致系統(tǒng)性能下降的后果。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流。
- Spring Boot 動(dòng)態(tài)數(shù)據(jù)源示例(多數(shù)據(jù)源自動(dòng)切換)
- Springboot如何設(shè)置多數(shù)據(jù)源,隨時(shí)切換
- SpringBoot AOP方式實(shí)現(xiàn)多數(shù)據(jù)源切換的方法
- spring boot+mybatis 多數(shù)據(jù)源切換(實(shí)例講解)
- Spring配置多數(shù)據(jù)源切換
- Spring整合多數(shù)據(jù)源實(shí)現(xiàn)動(dòng)態(tài)切換的實(shí)例講解
- 詳解Spring多數(shù)據(jù)源如何切換
- Spring通過(guò)攔截器實(shí)現(xiàn)多數(shù)據(jù)源切換的示例代碼
相關(guān)文章
Java實(shí)現(xiàn)文件上傳至服務(wù)器的方法
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)文件上傳至服務(wù)器的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01Java并發(fā)中的ABA問(wèn)題學(xué)習(xí)與解決方案
這篇文章主要介紹了Java并發(fā)中的ABA問(wèn)題學(xué)習(xí)與解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05MyBatis XML方式的基本用法之多表查詢(xún)功能的示例代碼
這篇文章主要介紹了MyBatis XML方式的基本用法之多表查詢(xún)功能的示例代碼,本文通過(guò)示例文字相結(jié)合的形式給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-07-07SpringBoot結(jié)合JWT實(shí)現(xiàn)用戶(hù)登錄、注冊(cè)、鑒權(quán)
用戶(hù)登錄、注冊(cè)及鑒權(quán)是我們基本所有系統(tǒng)必備的,也是很核心重要的一塊,本文主要介紹了SpringBoot結(jié)合JWT實(shí)現(xiàn)用戶(hù)登錄、注冊(cè)、鑒權(quán),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2023-05-05SSH框架網(wǎng)上商城項(xiàng)目第17戰(zhàn)之購(gòu)物車(chē)基本功能
這篇文章主要為大家詳細(xì)介紹了SSH框架網(wǎng)上商城項(xiàng)目第17戰(zhàn)之購(gòu)物車(chē)基本功能的實(shí)現(xiàn)過(guò)程,感興趣的小伙伴們可以參考一下2016-06-06mybatisplus添加真正的批量新增、批量更新的實(shí)現(xiàn)
這篇文章主要介紹了mybatisplus添加真正的批量新增、批量更新的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03利用Spring IOC技術(shù)實(shí)現(xiàn)用戶(hù)登錄驗(yàn)證機(jī)制
這篇文章主要為大家詳細(xì)介紹了Spring IOC技術(shù)實(shí)現(xiàn)用戶(hù)登錄驗(yàn)證機(jī)制的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10Java項(xiàng)目向Maven遷移的實(shí)戰(zhàn)示例
本文主要介紹了Java項(xiàng)目向Maven遷移實(shí)戰(zhàn)指南,提升自動(dòng)化構(gòu)建、依賴(lài)管理和項(xiàng)目信息管理的能力,具有一定的參考價(jià)值,感興趣的可以了解一下2025-07-07MybatisPlus 主鍵策略的幾種實(shí)現(xiàn)方法
MybatisPlus-Plus支持多種主鍵生成策略,可以通過(guò)@TableId注解的type屬性配置,主要策略包括AUTO、INPUT、ASSING_ID、ASSING_UUID和NONE,每種策略適用于不同的場(chǎng)景,下面就來(lái)介紹一下2024-10-10