mybatis plus動態數據源切換及查詢過程淺析

mybatis plus多數據源切換

mybatis plus多數據源切換使用註解 @DS
DS註解作為多數據源切點,具體實現作用主要由兩個類完成
DynamicDataSourceAnnotationAdvisor
DynamicDataSourceAnnotationInterceptor

DS多數據源切換實現

1.DynamicDataSourceAnnotationAdvisor類實現切面配置,其中AnnotationMatchingPointcut用於尋找切點,進入可看到支持類和方法的切點,多個切點會執行多次,根據代碼順序,方法的切點執行晚於類切點,所以方法的切點會覆蓋類,但是都會被執行

在這裡插入圖片描述

在這裡插入圖片描述

2.DynamicDataSourceAnnotationInterceptor 實現切面功能,匹配到切點後,根據切點值(數據源id)設置當前線程有效數據源私有變量,用於執行查詢時動態數據源能獲取,執行完畢會清楚此變量,此存儲功能由DynamicDataSourceContextHolder提供

在這裡插入圖片描述

3.執行時動態數據源確定,mybatisplus動態數據源實現類為DynamicRoutingDataSource,其維護一個map保存所有配置的數據源,以數據源ID作為key,執行查詢時,獲取連接,交由spring事務管理器SpringManagedTransaction進行連接獲取,若當前存在連接則直接返回,不存在是創建連接,隻有創建連接時,才有機會切換數據源,此處需要註意的是,同一事務下,使用的同一個sqlSession,執行查詢時使用的是同一個執行器executor,最終使用的是同一個事務管理器,所以獲取連接時無法創建新連接,mybatisplus切換數據源功能失效,代碼無法被執行

在這裡插入圖片描述

此處邏輯較多,一筆帶過有點草率,後續附帶mybatisplus查詢過程,感興趣的老鐵一會在看。

4.動態數據源敲定,接第3步,若當前事務管理器還未創建連接,那就打開一個連接,使用DataSourceUtils獲取一個連接,入參為mybatisplus的動態數據源DynamicRoutingDataSource,一步步往下巴拉,忽略不需要代碼,最終執行到瞭這一句:
Connection con = dataSource.getConnection();
使用DynamicRoutingDataSource獲取連接,瞧,兜兜轉轉,最終怎麼找連接,敲定數據源又交給瞭mybatisplus,getConnection方法在其父級AbstractRoutingDataSource中,使用this.determineDataSource().getConnection()獲取連接,.getConnection()是數據源獲取連接方法,那確定數據源顧名思義就是determineDataSource方法瞭,這個方法的實現就在DynamicRoutingDataSource中,來瞧瞧!是不是瞬間舒服瞭,根據第二部切面設置的數據源,這個返回對應的數據源。

在這裡插入圖片描述

5.主要邏輯已經清楚瞭,那麼來延伸下,如何手動切換數據源mybatisplus切換數據源主要是使用DynamicDataSourceContextHolder的線程獨享變量,那麼如果沒有DS切點,無法自動切換數據源,需要切換數據源時就可以使用DynamicDataSourceContextHolder.setDataSourceLookupKey 設置數據源,使用完後再清除掉(默認數據源生效),這個方法同第4步所提的事務問題,存在事務則無法切換(壓根就不執行 隻設置無法執行切換代碼),如果將要執行的代碼已存在切點,則執行前手動設置也是無效的,因為切點會覆蓋你的設置。

註:如果需要在事務存在的情況下切換數據源,則估計要覆蓋掉或替換掉spring的事務管理器,此處待後續再議。如果事務內仍需要切換數據源,則需要單獨定義service並設置切點,設置此切點的事務傳播行為為PROPAGATION__REQUIRES_NEW,則執行切面方法時單獨創建一個事務,數據源會自動切換。

mybatisplus執行查詢過程

現分析mybatisplus執行查詢過程

1.執行selectById方法,執行return this.baseMapper.selectById(id);經過springaop切面進行一系列巴拉巴拉的處理,最終進入mybatisplus PageMapperMethod類中執行execute方法,根據sql類型進行不同處理,分新增,修改,刪除,查詢,我們本次隻關註查詢,查詢裡也有很多東西。

可以看到此處提供多種返回值的查詢,有空返回值,多個,map,遊標,及啥也不是。空值查詢猜測是另有處理器直接處理返回值,此處不做延伸瞭,有需要再議,回到按照ID查詢及進入啥也不是分支,本次查詢不是分頁查詢,直接進入selectOne ,result = sqlSession.selectOne(this.command.getName(), param); 這裡的sqlSession是SqlSessionTemplate,執行selectOne時首先獲取sqlSession(默認為DefaultSelSession)

在這裡插入圖片描述

2.獲取真正的sqlSession並執行selectOne查詢,首先使用事務管理器獲取緩存的sqlSession的持有者(sqlSession包裝類),不存在則創建並緩存註冊。

在這裡插入圖片描述

在這裡插入圖片描述

得到真正的sqlSession後,執行selectOne,發現還是執行的selectList,然後就是大眾寫法,size=1返回get(0),否則為空或者報錯結果集太多。

3.來看看selectList在幹嘛,先獲取查詢語句相關的MappedStatement,然後使用執行器executor執行查詢 this.executor.query
執行器是啥?怎麼來的?

執行器是sqlSession內部的一個屬性,sqlSession其實也是個外包裝,提供瞭一堆規范化的操作,但是並不直接實現這些操作,而是交給執行器,執行器來執行增刪改查,默認使用的是SimpleExecutor,就以他入手,SimpleExecutor繼承抽象類BaseExecutor,BaseExecutor實現瞭Executor接口,這個query就在BaseExecutor中:這裡主要是獲取執行的sql,以及根據執行語句和入參來生成一個緩存的key,會緩存查詢結果,如果下次再來個一毛一樣的查詢 就直接用緩存瞭,不查瞭,這個就是mybatis的一級緩存,緩存使用的是一個封裝的類PerpetualCache,最終對應的就是一個map :
private Map<Object, Object> cache = new HashMap();

在這裡插入圖片描述

在這裡插入圖片描述

4.執行數據庫查詢queryFromDatabase -> doQuery,這裡和spring一個習慣,真正做事情的都是doXXX前面都是鋪墊前戲,看的人雲裡霧裡的
這個doQuery的實現在SimpleExecutor中,進入查看,
先創建瞭一個Statement,這個我知道,是jdbc裡的東西,jdbc大概就是加載驅動,獲取連接,打開一個執行語句塊,然後執行獲取結果。具體這個東西學名和作用還是百度下吧:
Statement 對象用於把 SQL 語句發送到 DBMS。你隻須簡單地創建一個 Statement 對象並且然後執行它,使用適當的方法執行你發送的 SQL 語句。
這個也就是jdbc層面的sqlSession瞭吧。

創建完Statement後就會執行查詢,先看來看Statement如何創建

5.Statement創建,創建第一步獲取connection,這個就涉及到數據源瞭吧,進入查看,使用事務管理器Transaction獲取連接,默認的事務管理器是SpringManagedTransaction,然後獲取數據源,最終創建或使用一個已有的連接並返回,進而創建出一個Statement,其他細節已無心再細究,不影響本次分析目的。

6.執行查詢,直接略過看最終執行處,doFinish-> query ,執行jdbc的PreparedStatement.execute,之後的代碼就先不看瞭,看意思就是將原始ResultSet結果集轉化為list。

在這裡插入圖片描述

到此這篇關於mybatis plus動態數據源切換及查詢過程淺析的文章就介紹到這瞭,更多相關mybatis plus動態數據源切換內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: