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動態資料來源切換內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!