天天看點

搞定SpringBoot多資料源(2):動态資料源

1. 引言

對于多個資料庫的處理,上一篇文章《搞定SpringBoot多資料源(1):多套源政策》已有提及,有多套資料源、動态資料源、參數化變更資料源等方式,本文是第二篇:“動态資料源”。動态資料源可以解決多套資料源的處理不夠靈活、占用資源多等問題。使用者可以根據實際的業務需要,統一操作邏輯,隻要在需要切換資料源的進行處理即可。何為動态,其實是批切換資料源的時機可以動态選擇,在需要的地方進行切換即可。

本文延續上一篇文章的示例,以主從場景為示例,結合代碼,對動态資料源的實作進行講解,内容包括搭建動态資料源原理、動态資料源配置、動态資料源使用,AOP 注解方式切換資料源等。

本文所涉及到的示例代碼:https://github.com/mianshenglee/my-example/tree/master/multi-datasource,讀者可結合一起看。

2. 動态資料源流程說明

Spring Boot 的動态資料源,本質上是把多個資料源存儲在一個 Map 中,當需要使用某個資料源時,從 Map 中擷取此資料源進行處理。而在 Spring 中,已提供了抽象類 AbstractRoutingDataSource 來實作此功能。是以,我們在實作動态資料源的,隻需要繼承它,實作自己的擷取資料源邏輯即可。動态資料源流程如下所示:

搞定SpringBoot多資料源(2):動态資料源

使用者通路應用,在需要通路不同的資料源時,根據自己的資料源路由邏輯,通路不同的資料源,實作對應資料源的操作。本示例中的兩資料庫的分别有一個表 test_user,表結構一緻,為便于說明,兩個表中的資料是不一樣的。兩個表結構可在示例代碼中的 sql 目錄中擷取。

3. 實作動态資料源

本示例中,主要有以下幾個包:

Spring Boot 的預設配置檔案是 application.properties ,由于有兩個資料庫配置,獨立配置資料庫是好的實踐,是以添加配置檔案 jbdc.properties ,添加以下自定義的主從資料庫配置:

根據連接配接資訊,把資料源注入到 Spring 中,添加 DynamicDataSourceConfig 檔案,配置如下:

注意: 此處使用 PropertySource 指定配置檔案,ConfigurationProperties 指定資料源配置字首 使用 MapperScan 指定包,自動注入相應的 mapper 類。 把資料源常量寫在 DataSourceConstants 類中 從此配置可以看到,已經把 SqlSessionFactory 這個配置從代碼中擦除,直接使用 Spring Boot 自動配置的 SqlSessionFactory 即可,無需我們自己配置。

前面的配置已把多個資料源注入到 Spring 中,接着對動态資料源進行配置。

(1) 添加jdbc依賴

(2) 添加動态資料源類

繼承抽象類 AbstractRoutingDataSource ,需要實作方法 determineCurrentLookupKey,即路由政策。 動态路由政策下一步實作,目前政策直接傳回 master 資料源

(3) 設定動态資料源為主資料源

在前面的資料源配置檔案 DynamicDataSourceConfig 中,添加以下代碼:

使用 Map 儲存多個資料源,并設定到動态資料源對象中。 設定預設的資料源是 master 資料源 使用注解 Primary 優先從動态資料源中擷取

同時,需要在 DynamicDataSourceConfig 中,排除 DataSourceAutoConfiguration 的自動配置,否則 會出現The dependencies of some of the beans in the application context form a cycle的錯誤。

(1) 資料源 key 的上下文

前面固定寫了一個資料源路由政策,總是傳回 master,顯然不是我們想要的。我們想要的是在需要的地方,想切換就切換。是以,需要有一個動态擷取資料源 key 的地方(我們稱為上下文),對于 web 應用,通路以線程為機關,使用 ThreadLocal 就比較合适,如下:

以 DATASOURCE_CONTEXT_KEY_HOLDER 存儲需要使用資料源 key getContextKey 時,若 key 為空,預設傳回 master

(2) 設定動态資料 DynamicDataSource 路由政策

我們需要達到的路由政策是,當設定資料源 key 到上下文,則從上下文中得到此資料源 key ,進而知道使用此對應的資料源。是以,修改前面 DynamicDataSource 的 determineCurrentLookupKey 方法如下:

有了上面的動态路由選擇,則不需要像之前的多套資料源那樣,mapper、entity、service等都寫一套相同邏輯的代碼,因為是主從,一般來說資料庫結構是一緻的,隻需要一套entity、mapper、service即可,在需要在不同的資料源進行操作時,直接對上下文進行設定即可。如下:

預設是使用 master 資料源查詢 使用上下文的 setContextKey 來切換資料源,使用完後使用 removeContextKey 進行恢複

經過上面的動态資料源配置,可以實作動态資料源切換,但我們會發現,在進行資料源切換時,都需要做 setContextKey 和 removeContextKey 操作,如果需要切換的方法比多,就會發現很多重複的代碼,如何消除這些重複的代碼,就需要用到動态代理了,如果不了解動态代理,可以參考一下我的這篇文章《java開發必學知識:動态代理》。在 Spring 中,AOP 的實作也是基于動态代理的。此處,我們希望通過注解的方式指定函數需要的資料源,進而消除資料源切換時産品的模闆代碼。

在annotation包中,添加資料源注解 DS,此注解可以寫在類中,也可以寫在方法定義中,如下所示:

定義資料源切面,此切面可以針對使用了 DS 注解的方法或者類,進行資料源切換。

(1)添加aop依賴

(2) 定義切面

注解 Pointcut 使用 annotation 指定注解 注解 Around 使用環繞通知處理,使用上下文進行對使用注解 DS 的值進行資料源切換,處理完後,恢複資料源。

在service層,我們定義一個 TestUserService ,裡面有兩個方法,分别是從 master 和 slave 中擷取資料,使用了注解DS,如下:

這樣定義後,在 controller 層的處理就可以變成:

由此可見,已經把資料庫切換的模闆代碼消除,隻需要關注業務邏輯處理即可。這就是AOP的好處。

4. 再思考一下

經過上面的動态資料源及 AOP 選擇資料源的講解,我們可以看到動态資料源已經很靈活,想切換隻需在上下文中進行設定資料源即可,也可以直接在方法或類中使用注解來完成。現在我們是手動編碼實作的,其實,對于MyBatis Plus ,它也提供了一個動态資料源的插件,有興趣的小夥伴也可以根據它的官方文檔進行實驗使用。

對于動态資料源,還有哪些地方需要考慮或者說值得改進的地方呢?

事務如何處理?其實在開發中應該盡量避免跨庫事務,但如果避免不了,則需要使用分布式事務。

對于目前的動态資料源,相對來說還是固定的資料源(如一主一從,一主多從等),即在編碼時已經确定的資料庫數量,隻是在具體使用哪一個時進行動态處理。如果資料源本身并不确定,或者說需要根據使用者輸入來連接配接資料庫,這時,如何處理呢?這種情況出現得比較多的是在對多個資料庫進行管理時的處理。這種情況,我将在下一篇文章中進行講解,我把它叫做"參數化變更資料源"。

5. 總結

本文對動态資料源的實作進行了講解,主要是動态資料源的配置、實作、使用,另外還使用 AOP 消除切換資料源時的模闆代碼,使我們開發專注于業務代碼,最後對動态資料源的進行了一下擴充思考。希望小夥伴們可以掌握動态資料源的處理。

本文配套的示例,示例代碼,有興趣的可以運作示例來感受一下。