MyBatis使用以及原始碼淺析
MyBatis是一個普遍應用並且十分優秀的持久層框架;本文將簡單介紹MyBatis的使用,同時也將分析其在程式碼層面是如何實現的;本文的演示環境如下:
JDK1.8 mysql 8.0.15 MyBatis 3.4.6
MyBatis的使用
構建SqlSessionFactory
每個基於MyBatis的應用都是以SqlSessionFactory為核心的,SqlSessionFactory可以通過SqlSessionFactoryBuilder來構建;常用的有兩種方式,一種是通過XML配置構建,而另一種則是通過Configuration例項物件來構建;
通過XML構建:

XML配置檔案內容如下:

通過Configuration例項物件來構建:

獲取到SqlSessionFactory以後,我們便可以獲取到SqlSession,最後便可以執行已經對映的SQL語句了;具體程式碼如下:

執行SQL語句
執行SQL語句的方法也並非只用一種,亦可以直接通過SqlSession來執行
通過SqlSession執行

通過獲取Mapper來執行

隨後會在下文針對對映語句,執行語句進行較為詳細的介紹。
對映的SQL語句

當然,對映的SQL語句也是不一定必須通過XML檔案來完成的,也是可以通過註解來實現的

對於簡單的SQL語句,通過註解的方式,你會覺得十分的得心應手;然而實現複雜的SQL則會有些混亂和力不從心;使用者可以根據自己的需求來使用,而不用單單被侷限於其中的某一種形式。
作用域以及生命週期
依賴注入框架會建立執行緒安全的SqlSession和Mapper並將它們注入到你的bean中,因此你可以直接忽略它們的生命週期;如何通過依賴注入框架來使用MyBatis,後面我們將會介紹;你也可以參看MyBatis-Spring。
SqlSessionFactoryBuilder
這個類可以被例項化,建立以及銷燬,一旦在其建立SqlSessionFactory,其實就不需要它了;所以它最好的作用域就是方法作用域(區域性方法),當然你可以通過它建立多個SqlSessionFactory例項,當然最好不要讓SqlSessionFactoryBuilder一直存在,以保證XML資源被用於更重要的事情。
SqlSessionFactory
SqlSessionFactory一旦被建立就應該在應用執行期間一直存在;它不應該被頻繁的銷燬建立,所以它的作用域應該是應用作用域;最好通過單例模式來使用。
SqlSession
每個執行緒都應該有自己的SqlSession,SqlSession不是執行緒安全的,所以它不能被執行緒共享,所以SqlSession不能被置於靜態類,或者一個類的例項變數;所以它的作用域最好是請求或者方法。例如在HTTP請求中,應該每次收到一個請求,便開啟一個SqlSession,響應之後,立即關閉SqlSession。
Mapper 例項
通過使用的Demo,我們也知道Mapper 例項是通過SqlSession獲得的,所以它的作用域也是方法作用域。
以上MyBatis使用指南,主要參考MyBatis官方文件
原始碼解讀
瞭解以上知識,讓我們對MyBatis有了進一步的瞭解;便於我們捕捉原始碼的閱讀方向;我們知道SqlSessionFactory是通過SqlSessionFactoryBuilder來構建的,接下來我們首先來看看它;
SqlSessionFactoryBuilder

我們看到SqlSessionFactoryBuilder中有這各式各樣的build方法;最終都呼叫到如下方法:

以上程式碼也印證了我們主要介紹的兩種方法來構建SqlSessionFactory,最終會返回一個DefaultSqlSessionFactory例項。
SqlSessionFactory

我們可以看到SqlSessionFactory主要提供了開啟SqlSession以及獲取Configuration的方法;知道了介面作用,我們再來看看預設的實現類DefaultSqlSessionFactory。獲取SqlSession最終都交給了兩個私有的方法:openSessionFromDataSource,openSessionFromConnection;顧名思義分別是通過資料來源來獲取,通過連線來獲取;兩個方法大同小異,我們來詳細看看其中一個。

以上便是建立SqlSeesion的過程,利用public DefaultSqlSession(Configuration configuration, Executor executor) 此構造方法來獲取SqlSession例項。
SqlSession

我們發現SqlSession提供了所有對資料庫的操作,各式各樣的增刪改查,以及獲取對映Mapper的方法;接下來我們仔細研讀一下預設的實現類DefaultSqlSession

我們發現在DefaultSqlSession實現類中SqlSeesion封裝的對資料庫的操作最終都是有Executor來執行的;相當於SqlSeesion提供對資料庫相應的操作,而具體的職責是有Executor來完成的。
Executor
下圖是Executor的主要功能

下圖是Executor的繼承實現關係

Executor在SqlSessionFactory開啟SqlSession時建立,程式碼片段如下:

找到Executor以後,我們離真相又近了一步;Executor從設計上就考慮到了快取,我們可以從createCacheKey,clearLocalCache等方法看到其設計的巧妙之處;這裡我們先忽略快取設計(後面會做詳細說明),我們先來看看它的抽象類BaseExecutor;

我們看到BaseExecutor對Executor介面進行了實現,最終呼叫抽象方法doXX;需要交由子類去實現。以doUpdate,doQuery為例,我們來看看子類實現。

最終MappedStatement都是由StatementHandler去執行,至此一個對映語句的完整執行流程就此結束。當然還有一個十分重要的類Configuration,我們再次必須強調一下,Configuration基本存在於整個流程,從通過SqlSessionFactoryBuilder構建SqlSessionFactory,到SqlSessionFactory中Executor的建立,開啟SqlSession,再到對映語句的執行。
Configuration
還記得我們開篇就提到過Configuration例項有兩種構建方式,一種是通過XMLConfigBuilder.parse方法,另一種是利用public Configuration(Environment environment)構造方法;所以Configuration從實質上來說就是XML的java物件表示。

這些元素在Configuration的成員變數中都可以找到。上圖參考
可能有的同學會好奇Mapper檔案是如何配置到Configuration中的,其實在構建Configuration例項的時候,呼叫的addMappers方法就將Mapper檔案註冊好了,我們通過以下程式碼一起來梳理一下

還記得上面執行語句的時候有兩種方式,一種是通過SqlSession直接執行,一種是getMapper以後,呼叫具體的方法。

我們在DEBUG testQuery 時,發現執行mapper.selectBlog(101);有代理物件來執行,這是為什麼呢?

這是因為我們在session.getMapper(BlogMapper.class); 獲取的實際上是MapperRegistry中代理工廠生產的代理物件

通過對原始碼的分析認識,我們用一張流程圖來總結整個流程

專案除錯地址:https://github.com/sexylowrie/mybatis-teach
