使用AbstractRoutingDataSource實(shí)現(xiàn)數(shù)據(jù)源動(dòng)態(tài)切換的實(shí)例
實(shí)現(xiàn)原理
AbstractRoutingDataSource
繼承自 Spring 的 AbstractDataSource
類,并實(shí)現(xiàn)了 DataSource
接口。它內(nèi)部維護(hù)了一個(gè)映射(Map),用于存儲數(shù)據(jù)源標(biāo)識和對應(yīng)的數(shù)據(jù)源實(shí)例。當(dāng)需要獲取連接時(shí),AbstractRoutingDataSource
會調(diào)用一個(gè)抽象方法 determineCurrentLookupKey()
來確定當(dāng)前的數(shù)據(jù)源標(biāo)識,然后根據(jù)這個(gè)標(biāo)識從映射中獲取對應(yīng)的數(shù)據(jù)源,并從該數(shù)據(jù)源中獲取連接。
關(guān)鍵方法解析
determineCurrentLookupKey(): 這是一個(gè)抽象方法,需要用戶自己實(shí)現(xiàn)。這個(gè)方法用于確定當(dāng)前的數(shù)據(jù)源標(biāo)識,其返回值將用作查找映射中對應(yīng)數(shù)據(jù)源的關(guān)鍵字。通常,可以在這個(gè)方法中獲取當(dāng)前線程的一些狀態(tài)或上下文信息,來動(dòng)態(tài)決定使用哪個(gè)數(shù)據(jù)源。
afterPropertiesSet(): 這個(gè)方法是在設(shè)置完所有屬性后被調(diào)用的。在這個(gè)方法中,
AbstractRoutingDataSource
會驗(yàn)證數(shù)據(jù)源映射是否已經(jīng)被正確設(shè)置,并且如果設(shè)置了默認(rèn)數(shù)據(jù)源,也會進(jìn)行檢查。getConnection() 和 getConnection(String username, String password): 這兩個(gè)方法是
DataSource
接口要求實(shí)現(xiàn)的方法。在AbstractRoutingDataSource
中,這兩個(gè)方法的實(shí)現(xiàn)邏輯是先調(diào)用determineCurrentLookupKey()
方法獲取當(dāng)前數(shù)據(jù)源標(biāo)識,然后根據(jù)這個(gè)標(biāo)識從映射中找到對應(yīng)的數(shù)據(jù)源,并調(diào)用該數(shù)據(jù)源的getConnection()
方法獲取連接。
場景實(shí)例
我們設(shè)計(jì)一個(gè)這樣的場景,比如一個(gè)多租戶(multi-tenant)應(yīng)用,其中每個(gè)租戶都有自己的數(shù)據(jù)庫。在這個(gè)場景中,系統(tǒng)根據(jù)當(dāng)前用戶的租戶ID動(dòng)態(tài)切換數(shù)據(jù)源。我們將使用 Spring Boot、Spring Data JPA 和 AbstractRoutingDataSource
來實(shí)現(xiàn)這個(gè)場景。
步驟 1: 定義租戶識別邏輯
首先,我們需要一個(gè)機(jī)制來識別當(dāng)前請求屬于哪個(gè)租戶。假設(shè)每個(gè)請求的HTTP頭部都包含一個(gè)X-TenantID
字段來標(biāo)識租戶ID:
@Component public class TenantInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String tenantId = request.getHeader("X-TenantID"); if (tenantId != null) { TenantContext.setCurrentTenant(tenantId); } return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { TenantContext.clear(); } }
在這里,TenantContext
是一個(gè)存儲當(dāng)前線程租戶ID的類:
public class TenantContext { private static final ThreadLocal<String> currentTenant = new ThreadLocal<>(); public static void setCurrentTenant(String tenantId) { currentTenant.set(tenantId); } public static String getCurrentTenant() { return currentTenant.get(); } public static void clear() { currentTenant.remove(); } }
步驟 2: 配置動(dòng)態(tài)數(shù)據(jù)源
接下來,我們需要配置動(dòng)態(tài)數(shù)據(jù)源來根據(jù)租戶ID選擇正確的數(shù)據(jù)庫:
public class MultiTenantDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return TenantContext.getCurrentTenant(); } } @Configuration public class DataSourceConfig { @Bean public DataSource dataSource() { MultiTenantDataSource dataSource = new MultiTenantDataSource(); Map<Object, Object> targetDataSources = new HashMap<>(); // 假設(shè)已經(jīng)有一個(gè)方法來獲取所有租戶的數(shù)據(jù)源配置 Map<String, DataSource> tenantDataSources = tenantDataSources(); targetDataSources.putAll(tenantDataSources); dataSource.setTargetDataSources(targetDataSources); dataSource.setDefaultTargetDataSource(defaultDataSource()); // 默認(rèn)數(shù)據(jù)源 dataSource.afterPropertiesSet(); return dataSource; } private Map<String, DataSource> tenantDataSources() { // 這里應(yīng)該從配置文件或數(shù)據(jù)庫中加載所有租戶的數(shù)據(jù)源配置 // 舉例,假設(shè)有兩個(gè)租戶tenantA和tenantB Map<String, DataSource> result = new HashMap<>(); result.put("tenantA", createDataSource("jdbc:mysql://localhost:3306/tenantA")); result.put("tenantB", createDataSource("jdbc:mysql://localhost:3306/tenantB")); return result; } private DataSource createDataSource(String url) { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl(url); dataSource.setUsername("username"); dataSource.setPassword("password"); return dataSource; } private DataSource defaultDataSource() { // 定義默認(rèn)數(shù)據(jù)源 return createDataSource("jdbc:mysql://localhost:3306/defaultDb"); } }
步驟 3: 集成到Spring MVC
最后,我們需要確保每個(gè)請求都會通過我們的TenantInterceptor
,以便正確設(shè)置租戶上下文:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private TenantInterceptor tenantInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(tenantInterceptor); } }
最后
以上就是使用AbstractRoutingDataSource實(shí)現(xiàn)數(shù)據(jù)源動(dòng)態(tài)切換的實(shí)例的詳細(xì)內(nèi)容,更多關(guān)于AbstractRoutingDataSource數(shù)據(jù)源切換的資料請關(guān)注腳本之家其它相關(guān)文章!
- SpringBoot+ThreadLocal+AbstractRoutingDataSource實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源
- SpringBoot基于AbstractRoutingDataSource實(shí)現(xiàn)多數(shù)據(jù)源動(dòng)態(tài)切換
- Spring(AbstractRoutingDataSource)實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換示例
- Spring AbstractRoutingDatasource 動(dòng)態(tài)數(shù)據(jù)源的實(shí)例講解
相關(guān)文章
java虛擬機(jī)之JVM調(diào)優(yōu)詳解
這篇文章主要介紹了java虛擬機(jī)之JVM調(diào)優(yōu)詳解,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)Java虛擬機(jī)的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04關(guān)于Springboot在新增和修改下上傳圖片并顯示的問題
這篇文章主要介紹了關(guān)于Springboot在新增和修改下上傳圖片并顯示的問題及解決方法,在這里 springboot中已經(jīng)內(nèi)嵌了上傳圖片的依賴包,因此不需要再添加額外依賴,具體實(shí)現(xiàn)代碼跟隨小編一起看看吧2021-04-04JavaScript中的isTrusted屬性及其應(yīng)用場景詳解
在現(xiàn)代 Web 開發(fā)中,JavaScript 是構(gòu)建交互式應(yīng)用的核心語言,隨著前端技術(shù)的不斷發(fā)展,開發(fā)者需要處理越來越多的復(fù)雜場景,例如事件處理、數(shù)據(jù)傳遞和狀態(tài)管理等,本文將通過一個(gè)實(shí)際案例,深入探討 isTrusted 屬性的來源、作用,需要的朋友可以參考下2025-01-01Java實(shí)現(xiàn)上傳文件到服務(wù)器的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何使用Java實(shí)現(xiàn)上傳文件到服務(wù)器,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-04-04Java實(shí)現(xiàn)一鍵生成表controller,service,mapper文件
這篇文章主要為大家詳細(xì)介紹了如何利用Java語言實(shí)現(xiàn)一鍵生成表controller,service,mapper文件,文中的示例代碼講解詳細(xì),需要的可以收藏一下2023-05-05Java OOM 異常場景與排查過程(堆、棧、方法區(qū))
這篇文章主要介紹了Java OOM 異常場景與排查過程(堆、棧、方法區(qū)),具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03springboot中@PostConstruct注解使用小結(jié)
本文主要介紹了springboot中@PostConstruct注解使用小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-01-01