封面:洛小汐
做者:潘潘java
2020 年的大疫情,把世界撕成幾片。git
時至今日,依舊人心惶惶。github
很慶幸,身處這安穩國,spring
兼得一份安穩工。sql
·數據庫
東家常講的一個詞:深秋心態 。apache
大勢時,不跟風、起鬨,緩存
蕭條時,不放棄播種和耕耘的信心,安全
熱時不燥、冷時不棄,微信
這就是深秋心態。
·
大疫情,相信只是大天然的規律,
也恰是咱們保持深秋心態的時候,
默默播種和耕耘吧,
今年,世界會慢慢復甦,但願都會來臨。
2021年要信心滿滿 ヾ(◍°∇°◍)ノ゙
定會收貨滿滿 ~
上圖保存可作朋友圈封面圖 ~
上節咱們介紹了 《 Mybatis系列全解(五):全網最全!詳解Mybatis的Mapper映射文件 》,經此一文,咱們基本能掌握 Mapper 映射器九大頂級元素的基本用法和其中技巧。在本節,咱們開始深刻,我挑選了 Mybatis 框架中幾個比較硬核的 API ,跟你們一塊兒探討,夯實了這些 API ,有助於你學習理解整個 Mybatis 框架,特別是 Mybatis 核心的數據處理層,你絕對會造成一套清晰的脈絡印記,總之,但願你們都能成爲 Mybatis King !
另外, 咱們的 Mybatis 全解系列一直在更新
一、Mybatis 架構與核心API
二、Configuration -- 全局配置對象
三、Resources -- 資源輔助類
四、SqlSessionFactoryBuilder -- 會話工廠構建器
五、SqlSessionFactory -- 會話工廠
六、SqlSession -- 會話
七、Executor -- 執行器
八、StatementHandler -- 語句處理器
九、ParamerHandler -- 參數處理器
十、ResultSetHandler -- 結果集處理器
十一、TypeHandler -- 類型轉換器
十二、MappedStatement -- 語句對象
1三、SqlSource -- SQL源
1四、BoundSql -- SQL語句
不出意外的話,在後續源碼剖析相關文章中,咱們會對 Mybatis 的源碼進行一次大掃蕩,一塊兒挖掘每一處值得你們深刻理解/記憶的知識點。而在本文中,咱們主要先把 Mybatis 的架構/層次鋪開,俯視 Mybatis 架構的設計全貌,再把幾個硬核的 API 詳細消化。
總體順序脈絡,但願讓你有所期待 ~
咱們先簡單揭開 Mybatis 神祕的源碼包,
瞅瞅 Ta 大體目錄結構 :
看,Mybatis 的源代碼包整齊劃一排在 org.apache.ibatis 目錄下,基本設計用途我簡單梳理成上面這張圖,方便你們直觀理解,固然只看源碼包目錄結構,不免會顯得枯燥無物,因此咱們再看一下,其實 Mybatis 的源碼包功能上能夠是這麼劃分:
上圖讓咱們對 Mybatis 的架構有了抽象的理解。
然而,實際上具體的職能分工,核心 API 的場景應用,到底會是怎樣一套流程呈現呢?
看下面這幅功能架構設計,或許你能更好的理解。
根據 Mybatis 功能架構咱們劃分紅三層:
接口層:該層提供一系列接口讓用戶直接參與使用,包含信息配置與實際數據操做調用。配置方式包括:基於 XML 配置方式、基於 Java API 配置方式兩種方式,用戶也能夠經過接口層 API 對數據庫發起增刪改查等操做的請求, 本層接口會把接收到的調用請求交給數據處理層的構件去處理。
數據處理層:該層是 Mybatis 的核心層,負責數據處理,主要包括SQL 參數映射解析、SQL 語句的實際執行、執行結果集的映射處理等。
咱們知道,Mybatis 框架讓用戶只須要提供配置信息,而且專一於 SQL 的編寫便可,對於鏈接管理數據庫/事務,或實際的 SQL 參數映射/語句執行/結果集映射等操做,做爲用戶都並不須要操心和參與。
可是,好奇的咱們其實想知道,Mybatis 核心部分的數據處理在總體流程中,是如何支撐用戶請求?同時各個構件之間交互,又是怎樣流轉呢?
很巧,我這裏有一張圖,介紹了總體流程:
根據以上框架流程圖進行講解:
針對以上整體框架流程涉及到的這些硬核 API,下面咱們逐個展開介紹,但不會詳細剖析源碼與原理,包括構建細節,由於這些咱們在後續的源碼剖析章節中都會詳細分析。
對於 Mybatis 的全局配置對象 Configuration,我相信不管是初學者仍是資深玩家,都不會陌生。整個 Mybatis 的宇宙,都圍繞着 Configuration 轉。Configuration 對象的結構和 config.xml 配置文件的內容幾乎相同,涵蓋了properties (屬性),settings (設置),typeAliases (類型別名),typeHandlers (類型處理器),objectFactory (對象工廠),mappers (映射器)等等,以前咱們有專門的一篇文章詳細進行介紹,感興趣的朋友往上翻到目錄列表,找到 《Mybatis系列全解(四):全網最全!Mybatis配置文件XML全貌詳解》 一文詳細品味一番吧。
配置對象 Configuration 經過解析器 XMLConfigBuilder 進行解析,把全局配置文件 Config.xml 與 映射器配置文件 Mapper.xml 中的配置信息所有構建成完整的 Configuration 對象,後續咱們源碼分析時詳細剖析整個過程。
咱們知道,像 Configuration 和 Mapper 的配置信息存放在 XML 文件中,Mybatis 框架在構建配置對象時,必須先把 XML 文件信息加載成流,再作後續的解析封裝,而 Resources 做爲資源的輔助類,偏偏乾的就是這個活,不管是經過加載本地資源或是加載遠程資源,最終都會經過 類加載器 訪問資源文件並輸出文件流。
//加載核心配置文件 InputStream resourceAsStream = Resources.getResourceAsStream("Config.xml");
Resources 實實在在提供了一系列方法分分鐘解決你的文件讀取加載問題:
咱們一撞見 xxxBuilder ,就大體能知道它是某類對象的構建器,這裏 SqlSessionFactoryBuilder 也是同樣,它是 Mybatis 中的一個會話工廠構建器,在資源輔助類 Resources 讀取到文件流信息以後,它負責解析文件流信息並構建會話工廠 SqlSessionFactory。(解析的配置文件包含:全局配置 Configuration 與映射器 Mapper)
在程序應用端,咱們通常使用 SqlSessionFactoryBuilder 直接構建會話工廠:
// 得到sqlSession工廠對象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
固然,若是你集成了 Spring 框架的項目,則不須要本身手工去構建會話工廠,直接在 Spring 配置文件中指定便可,例如指定一個 bean 對象,id 是 sqlSessionFactory,而 class 類指定爲 org.mybatis.spring.SqlSessionFactoryBean 。
SqlSessionFactoryBuilder 內部經過解析器 XMLConfigBuilder 解析了文件流,同時封裝成爲配置對象 Configuration ,再把 Configuration 對象進行傳遞並構建實例。
public SqlSessionFactory build( InputStream inputStream, String environment, Properties properties) { // 配置解析器解析 XMLConfigBuilder parser = new XMLConfigBuilder( inputStream,environment, properties); // 最終實例會話工廠 return build(parser.parse()); }
最終實例會話工廠,其實 Mybatis 默認實現是 new 了一個DefaultSqlSessionFactory 實例。
// 最終實例會話工廠 public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
會話工廠構建器 SqlSessionFactoryBuilder 應用了構建者模式,主要目的就是爲了構建 SqlSessionFactory 對象,以便後續生產 SqlSession 對象,這個構造器基本上算是 Mybatis 框架的入口構建器,它提供了一系列多態方法 build(),支持用戶使用 XML 配置文件或 Java API (Properties)來構建會話工廠 SqlSessionFactory 實例。
SqlSessionFactoryBuilder 的一輩子只爲成就 SqlSessionFactory,當 SqlSessionFactory 一經實例,SqlSessionFactoryBuilder 使命完成,即可消亡,即可被丟棄。
所以 SqlSessionFactoryBuilder 實例的最佳做用域是 方法做用域(也就是局部方法變量)。 你能夠重用 SqlSessionFactoryBuilder 來建立多個 SqlSessionFactory 實例,但最好不要一直保留着它,以保證全部的 XML 解析資源能夠被釋放給更重要的事情。
SqlSessionFactoryBuilder 中靈活構建會話工廠的一系列接口:
會話工廠 SqlSessionFactory 是一個接口,做用是生產數據庫會話對象 SqlSession ,有兩個實現類:
在介紹會話工廠構建器 SqlSessionFactoryBuilder 的時候,咱們瞭解到構建器默認建立了 DefaultSqlSessionFactory 實例,而且會話工廠自己會綁定一個重要的屬性 Configuration 對象,在生產會話時,最終也會把 Configuration 配置對象傳遞並設置到會話 SqlSession 上。
會話工廠能夠簡單建立 SqlSession 實例:
// 建立 SqlSession 實例 SqlSession session = sqlSessionFactory.openSession();
會話工廠建立 SqlSession 時,會綁定數據源、事務處理、執行器等等,默認會話工廠實現類 DefaultSqlSessionFactory 在建立會話對象時,最終都會調用 openSessionFromDataSource 方法 ,便是如此實現:
// 每個 openSession 最終都會調用此處 private SqlSession openSessionFromDataSource( ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { // 環境配置 final Environment environment = configuration.getEnvironment(); // 事務工廠 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); // 事務 Transaction tx = transactionFactory.newTransaction( environment.getDataSource(), level, autoCommit); // 執行器 final Executor executor = configuration.newExecutor(tx, execType); // 最終生成會話 return new DefaultSqlSession( configuration, executor, autoCommit); }
另外,會話工廠其實提供了一系列接口來靈活生產會話 SqlSession,你能夠指定:
SqlSessionFactory 一旦被建立就應該在 應用的運行期間 一直存在,沒有任何理由丟棄它或從新建立另外一個實例。 使用 SqlSessionFactory 的最佳實踐是在應用運行期間不要重複建立屢次,屢次重建 SqlSessionFactory 被視爲一種代碼「壞習慣」。所以 SqlSessionFactory 的最佳做用域是 應用做用域。 最簡單的就是使用單例模式或者靜態單例模式。
請記住,建立 SqlSessionFactory ,一次就好!
每一個數據庫對應一個 SqlSessionFactory 實例。SqlSessionFactory 一旦被建立, 它的生命週期應該與應用的生命週期相同 。因此,若是你想鏈接兩個數據庫,就須要建立兩個 SqlSessionFactory 實例,每一個數據庫對應一個;而若是是三個數據庫,就須要三個實例,依此類推。
SqlSession 是一個接口,有兩個實現類:
簡單來講,經過會話工廠構建出 SqlSession 實例以後,咱們就能夠進行增刪改查了,默認實例 DefaultSqlSession 提供瞭如此多的方法供用戶使用,有超過30個:
sqlSession 的方法除了 CURD,還提供了事務的控制例如提交/關閉/回滾等、提供了配置對象的獲取例如 getConfiguration()、提供了批量語句的執行更新例如 flushStatements()、提供了緩存清除例如 clearCache() 、提供了映射器的使用 getMapper() 等等。
對於客戶端應用層面來講,熟悉 sqlSession 的 API 基本就能夠任意操做數據庫了,不過咱們但願想進一步瞭解 sqlSession 內部是如何執行 sql 呢?其實 sqlSession 是 Mybatis 中用於和數據庫交互的 頂層類,一般將它與本地線程 ThreadLocal 綁定,一個會話使用一個 SqlSession,而且在使用完畢以後進行 關閉。
之因此稱 SqlSession 爲數據交互的 頂層類,是它其實沒有完成實質的數據庫操做。根據以前的架構設計流程咱們已經清晰的知道,SqlSession 對數據庫的操做都會轉發給具體的執行器 Executor 來完成 ;固然執行器也是甩手掌櫃,執行器 Executor 會再分派給語句處理器 StatementHandler ,語句處理器會結合參數處理器 ParameterHandler ,共同完成最終的數據庫執行處理操做(底層仍是封裝了 JDBC Statement 操做)。並在每一個語句處理器 StatementHandler 處理完成數據庫操做以後, 經過結果結處理器 ResultSetHandler 以及類型處理器 TypeHandler ,對底層 JDBC 返回的結果集進行映射封裝,最終才返回預期的封裝對象。
關注如下圖示 sqlSession 紅色高亮位置,詳細描述了會話的實際執行路徑:
SqlSession 能夠理解爲一次數據庫會話,一次會話當中既能夠執行一次 sql ,也容許你批量執行屢次,可是一旦會話關閉以後想要再執行 sql,那就必須從新建立會話。
每一個線程都應該有它本身的 SqlSession 實例,SqlSession 的實例不是線程安全的,所以是不能被共享的,因此它的最佳的做用域是 請求(request)或方法(method) 做用域。 絕對不能將 SqlSession 實例的引用放在一個類的靜態域,甚至一個類的實例變量也不行。 也毫不能將 SqlSession 實例的引用放在任何類型的託管做用域中,好比 Servlet 框架中的 HttpSession。 若是你如今正在使用一種 Web 框架,考慮將 SqlSession 放在一個和 HTTP 請求類似的做用域中。 換句話說,每次收到 HTTP 請求,就能夠打開一個 SqlSession,返回一個響應後,就關閉它。 這個關閉操做很重要,爲了確保每次都能執行關閉操做,你應該把這個關閉操做放到 finally 塊中。
Spring 集成 Mybatis 以後,經過依賴注入能夠建立線程安全的、基於事務的 SqlSession ,並管理他們的生命週期,推薦搭配使用。
Executor 是一個執行器接口,是 Mybatis 的調度核心,它定義了一組管理 Statement 對象與獲取事務的方法,並負責 SQL 語句的生成和一級/二級查詢緩存的維護等,SqlSessionFactory 在建立 SqlSession 時會同時建立執行器,並指定執行器類型,默認使用 SimpleExecutor 。執行器接口有5個子孫實現類,其中 BaseExecutor 是抽象類,另外4個子孫實現類分別是:SimpleExecutor 、BatchExecutor、ReuseExecutor、CachingExecutor。
BaseExecutor:基礎執行器(抽象類),對Executor接口進行了基本實現,爲下一級實現類執行器提供基礎支持。BaseExecutor 有三個子類分別是 SimpleExecutor、ResuseExecutor、BatchExecutor。
SimpleExecutor:普通執行器,繼承 BaseExecutor 抽象類,是 MyBatis 中 默認 使用的執行器. 每執行一次 update 或 select ,就開啓一個 Statement 對象,用完馬上關閉 Statement 對象。(能夠是 Statement 或 PrepareStatement 對象)。
ReuseExecutor:複用執行器,繼承 BaseExecutor 抽象類,這裏的複用指的是重複使用 Statement . 它會在內部利用一個 Map 把建立的 Statement 都緩存起來,每次在執行一條 SQL語 句時,它都會去判斷以前是否存在基於該 SQL 緩存的 Statement 對象,存在且以前緩存的 Statement 對象對應的 Connection 尚未關閉則會繼續使用以前的 Statement 對象,不然將建立一個新的 Statement 對象,並將其緩存起來。由於每個新的 SqlSession 都有一個新的 Executor 對象,因此咱們緩存在 ReuseExecutor 上的 Statement 的做用域是同一個 SqlSession 。
BatchExecutor:批處理執行器,繼承 BaseExecutor 抽象類,經過批量操做來提升性能,用於將多個 sql 語句一次性輸送到數據庫執行。因爲內部有緩存的實現,因此使用完成後須要調用 flushStatements() 來清除緩存。
Mybatis 在構建 Configuration 配置類時默認把 ExecutorType.SIMPLE 做爲執行器類型,當咱們的會話工廠 DefaultSqlSessionFactory 開始生產 SqlSession 會話時,會同時構建執行器,此時就會依據配置類 Configuration 構建時指定的執行器類型來實例具體執行器 ,流程以下:
// 一、Configuration配置類構建時 // 指定了默認執行器類型爲:普通執行器 protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE; // 二、Configuration配置類中 // 提供方法獲取默認執行器類型 public ExecutorType getDefaultExecutorType() { return defaultExecutorType; } // 三、會話工廠建立 SqlSession 實例時 SqlSession session = sqlSessionFactory.openSession(); // 四、openSession 實際邏輯 public SqlSession openSession() { return openSessionFromDataSource( // 這裏可就獲取了默認執行器 configuration.getDefaultExecutorType(), null, false ); }
這裏,確定有人想知道,咱們可否指定其它執行器呢?
答案是:固然能夠,有兩種方式指定:
// 建立 SqlSession 實例 SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.REUSE) // ExecutorType是一個枚舉 // 有三個值SIMPLE, REUSE, BATCH
<settings> <!--取值範圍 SIMPLE, REUSE, BATCH --> <setting name="defaultExecutorType" value="REUSE"/> </settings>
對於第二種 settings 的配置方式,其實以前咱們在介紹 Mybatis 的配置文件中已經講過,這裏再簡單說明一下,像上面配置 settings 中的屬性 defaultExecutorType ,基本這些屬性都是 Mybatis 額外提供給咱們靈活設置的,就算咱們不設置 Mybatis 也會有默認值,例如像 defaultExecutorType 的默認值就是 SIMPLE。你看一下 Mybatis 在解析 Configuration 配置時的默認構建,就會明白:
解析 Configuration 的解析器(類與具體方法的代碼路徑):
org.apache.ibatis.builder.xml.XMLConfigBuilder#settingsElement
咱們截取部分代碼邏輯,下面是設置默認執行器類型屬性 defaultExecutorType 的內容:
// 配置文件解析器 public class XMLConfigBuilder { // 最終解析到的 Configuration 對象 protected final Configuration configuration; // 爲 Configuration 對象設置屬性 private void settingsElement(Properties props) { // 設置默認執行器類型,默認是 SIMPLE configuration.setDefaultExecutorType( ExecutorType.valueOf( props.getProperty( "defaultExecutorType", "SIMPLE"))); // .... 固然這裏還有不少屬性設置 // .... 只要你在<settings>中配置便可 } }
注意,到此咱們知道能夠根據業務須要指定執行器類型,例如 SIMPLE(普通執行器), REUSE(複用執行器), BATCH(批處理執行器)。
可是,有朋友就好奇了,那緩存執行器 CachingExecutor 好像不見說明呢?
確實如此,由於緩存執行器個其它三個執行器還不太同樣,CachingExecutor 是須要咱們開啓二級緩存纔會有,這裏你們先不要思考什麼是一級緩存,什麼二級緩存,後續咱們有一文會詳細講緩存整個知識內容。
你們先了解一個概念便可,就是 Mybatis 的一級緩存是默認開啓的,無論你要不要,都會有一級緩存,而二級緩存呢,是默認關閉的,但容許咱們手工開啓。
對比開啓二級緩存先後,執行器執行的區別吧!
其實咱們實際操做數據庫,不會直接接觸到執行器 Executor ,不過咱們確實能夠了解一下基本的執行原理,下面列出了執行器接口提供的衆多重載方法,基本用於事務/緩存/數據庫管理與訪問,能夠知道一下:
到此,對於執行器有了基本的認識,可是實際上,咱們知道執行器自身沒有去具體執行 SQL 語句,而是分派到語句處理器 StatementHandler ,語句處理器會結合參數處理器 ParameterHandler ,最終進行數據庫操做。
StatementHandler 是一個語句處理器接口,它封裝了 JDBC Statement 操做,負責對 JDBC Statement 的操做,如 設置參數、結果集映射,是實際跟數據庫作交互的一道。StatementHandler 語句處理器實例,是在執行器具體執行 CRUD 操做時構建的,默認使用 PrepareStatementHandler。語句處理器接口有5個子孫實現類,其中 BaseStatementHandler 是抽象類,另外4個子孫實現類分別是:SimpleStatementHandler、PrepareStatementHandler、CallableStatementHandler、RoutingStatementHandler。
BaseStatementHandler:基礎語句處理器(抽象類),它基本把語句處理器接口的核心部分都實現了,包括配置綁定、執行器綁定、映射器綁定、參數處理器構建、結果集處理器構建、語句超時設置、語句關閉等,並另外定義了新的方法 instantiateStatement 供不一樣子類實現以便獲取不一樣類型的語句鏈接,子類能夠普通執行 SQL 語句,也能夠作預編譯執行,還能夠執行存儲過程等。
SimpleStatementHandler:普通語句處理器,繼承 BaseStatementHandler 抽象類,對應 java.sql.Statement 對象的處理,處理普通的不帶動態參數運行的 SQL,即執行簡單拼接的字符串語句,同時因爲 Statement 的特性,SimpleStatementHandler 每次執行都須要編譯 SQL (注意:咱們知道 SQL 的執行是須要編譯和解析的)。
PreparedStatementHandler:預編譯語句處理器,繼承 BaseStatementHandler 抽象類,對應 java.sql.PrepareStatement 對象的處理,相比上面的普通語句處理器,它支持可變參數 SQL 執行,因爲 PrepareStatement 的特性,它會進行預編譯,在緩存中一旦發現有預編譯的命令,會直接解析執行,因此減小了再次編譯環節,可以有效提升系統性能,並預防 SQL 注入***(因此是系統默認也是咱們推薦的語句處理器)。
其實普通語句處理器、預執行語句處理器以及存儲過程處理器,只是 Mybatis 對於 JDBC 的語句執行對象的簡單包裝而已,沒有特別神祕,看如下 JDBC 的語句執行對象的類圖關係也就可以清楚。
// 一、執行器構建語句處理器實例 public StatementHandler newStatementHandler(...) { // 構建路由語句處理器便可! StatementHandler statementHandler = new RoutingStatementHandler(...); // 其它邏輯忽略... return statementHandler; } // 二、實際構造方法(路由關係) public RoutingStatementHandler(...) { // 根據指定類型構造委託實例 switch (ms.getStatementType()) { case STATEMENT: delegate = new SimpleStatementHandler(...); break; case PREPARED: delegate = new PreparedStatementHandler(...); break; case CALLABLE: delegate = new CallableStatementHandler(...); break; default: throw new ExecutorException( "Unknown statement type: " + ms.getStatementType()); } }
咱們前面介紹了執行器具體執行 CRUD 操做時,構造的語句處理器默認使用 PrepareStatementHandler ,不過有些好奇的腦殼就想問問,那咱們能不能指定語句處理器類型呢?
固然能夠,例如咱們指定更新用戶語句適用預編譯處理語句處理器:
<!--取值範圍 STATEMENT, PREPARED, CALLABLE --> <update id="updateUser" statementType="STATEMENT"> update t_user set name = #{newName} </update>
當 Mybatis 在解析映射器中的每條語句時,會設置語句處理器類型:
// 語句對象解析器 public class XMLStatementBuilder { // 解析語句節點 public void parseStatementNode() { // 設置語句處理器類型 // 默認是 PREPARED 類型 StatementType statementType = StatementType.valueOf( context.getStringAttribute( "statementType", StatementType.PREPARED.toString() ) ); } }
因此,語句執行器與數據庫的交互過程:
固然,語句處理器接口 StatementHandler 提供了基本接口,通常咱們不必自定義實現類,因此能夠簡單看一下便可:
ParameterHandler 是一個參數處理器接口,它負責把用戶傳遞的參數轉換成 JDBC Statement 所須要的參數,底層作數據轉換的工做會交給類型轉換器 TypeHandler,後面會介紹。
很顯然,須要對傳入的參數進行轉換處理的 StatementHandler 實例只有兩個,分別是:
上面在介紹語句處理器的時候,咱們有介紹說基礎語句處理器 BaseStatementHandler 在進行實例構建時,會同時構建參數處理器與結果集處理器,因此參數處理器就是在此時被構建。
// 基礎語句處理器 public abstract class BaseStatementHandler{ // 構造實例時 protected BaseStatementHandler(...){ // 其它邏輯可忽略... // 一、構建參數處理器 this.parameterHandler = conf.newParameterHandler(...); // 二、構建結果集處理器 this.resultSetHandler = conf.newResultSetHandler(...); } }
對於參數處理器接口,相對簡單,只有1個默認實現類 DefaultParameterHandler ,該接口只有兩個方法,分別是:
// 有2個處理器會使用,分別是: // 預編譯處理器 PreparedStatementHandler // 存儲過程處理器 CallableStatementHandler public void parameterize(Statement statement) { //使用ParameterHandler對象來完成對Statement的設值 parameterHandler.setParameters(statement); }
應用場景例如查詢用戶對象時,設置姓名,參數處理器結合類型處理器把 name 屬性佔位符進行賦值。
<select id="queryUSer"> select * from t_user where name = #{name} </select>
// 默認結果集處理器 public class DefaultResultSetHandler{ // 處理輸出參數 public void handleOutputParameters(...) { // 獲取參數 final Object parameterObject = parameterHandler.getParameterObject(); // 其它存儲過程輸出參數處理邏輯... } }
ResultSetHandler 是一個結果集處理器接口,它負責負責將 JDBC 返回的結果集 resultSet 對象轉換爲 List 類型的集合,是在語句處理器構建實例時被同時建立,底層作數據轉換的工做會交給類型轉換器 TypeHandler,它有1個默認實現類 DefaultResultSetHandler,該接口有3個方法,分別是:
結果集處理器對於 JDBC 返回的結果集的基本處理,是先獲取咱們在映射器 Mapper 中指定 resultType 或 resultMap 映射關係,而後遍歷解析結果集中的每一列數據,底層經過 MetaObject 對象作相關的反射處理。
對於詳細的源碼邏輯,咱們後續源碼剖析部分詳細講。
不講不是中國人 O(∩_∩)O ~
TypeHandler 是一個類型轉換器/處理器接口,它負責 Java 數據類型和 JDBC 數據類型之間的映射與轉換,當對 Statement 對象設置參數時,由 JavaType 轉換爲 JdbcType,當對 Statement 返回結果集進行封裝映射時,又會將 JdbcType 轉換爲 JavaType。
通常,咱們能夠直接使用 Mybatis 內置的類型處理器,簡單看了一下有 65+ 個,固然咱們是能夠根據業務須要自定義類型處理器的,以便處理複雜類型或非標類型。
具體作法爲:
一、實現 org.apache.ibatis.type.TypeHandler
接口;
二、繼承 org.apache.ibatis.type.BaseTypeHandler
類。
其中 BaseTypeHandler 類做爲抽象類就已經實現了 TypeHandler 接口。
咱們看到接口 TypeHandler 定義了四個方法:
public interface TypeHandler<T> { // 設置參數 void setParameter( PreparedStatement ps, int i, T parameter, JdbcType jdbcType); // 根據列名獲取轉換結果 T getResult(ResultSet rs, String columnName); // 根據列下標獲取轉換結果 T getResult(ResultSet rs, int columnIndex); // 根據列下標獲取【存儲過程】的輸出結果 T getResult(CallableStatement cs, int columnIndex); }
其實,我以前在介紹 Mybatis 核心配置的時候,有大力介紹過類型處理器,不必重複寫(實際上是懶),感興趣的朋友能夠直接看咱們以前的文章 《Mybatis系列全解(四):全網最全!Mybatis配置文件XML全貌詳解》中對類型處理器 TypeHandler 的介紹。
MappedStatement 語句對象,就是咱們在映射器 Mapper 中維護的每一條語句,例如 <select|update|delete|insert>,Mybatis 中經過語句構造器 XMLStatementBuilder 對每個語句進行解析:
整個解析過程分爲4步驟:
// Configuration 配置解析器 public class XMLConfigBuilder{ // 解析映射器 private void mapperElement(){ // 建立映射器解析實例 XMLMapperBuilder mapperParser = new XMLMapperBuilder(...); // 開始解析 mapperParser.parse(); } }
二、映射對象解析器 XMLMapperBuilder 解析語句
// 映射對象解析器 public class XMLMapperBuilder{ // 一、解析入口 public void parse() { // 解析映射器文件 configurationElement( parser.evalNode("/mapper")); } // 二、節點解析 private void configurationElement(XNode context) { // 構建語句對象 buildStatementFromContext( context.evalNodes( "select|insert|update|delete")); } // 三、最終調用語句解析器 private void buildStatementFromContext(){ // 建立語句解析實例 XMLStatementBuilder statementParser = new XMLStatementBuilder(); // 解析語句節點 statementParser.parseStatementNode(); } }
三、語句解析器 XMLStatementBuilder 解析每個節點
// 語句解析器 public class XMLStatementBuilder{ // 解析語句節點 public void parseStatementNode() { // 經過語句輔助類構建語句對象 builderAssistant.addMappedStatement(...) } }
四、語句輔助類 MapperBuilderAssistant 添加進語句集合中
// 語句輔助類 public class MapperBuilderAssistant{ // 添加語句對象 public MappedStatement addMappedStatement( // 最終添加到配置類的語句集合中 configuration.addMappedStatement(statement); } }
SqlSource 是一個 SQL 源接口,它會結合用戶傳遞的參數對象 parameterObject,動態地生成 SQL 語句,並最終封裝成 BoundSql 對象。SqlSource 接口有5個實現類,分別是:StaticSqlSource、DynamicSqlSource、RawSqlSource、ProviderSqlSource、VelocitySqlSource (這只是一個測試用例,而非真正模板 Sql 源實現類)。
SqlSource 實例在配置類 Configuration 解析階段就被建立,Mybatis 框架會依據3個維度的信息來選擇構建哪一種數據源實例:
SqlSource 接口只有一個方法 getBoundSql ,就是建立 BoundSql 對象。
public interface SqlSource { BoundSql getBoundSql(Object parameterObject); }
BoundSql 對象存儲了動態生成的 SQL 語句以及相應的參數信息,BoundSql 對象是在執行器具體執行 CURD 時經過實際的 SqlSource 實例所構建。經過 BoundSql 可以獲取到實際數據庫執行的 SQL 語句,系統可根據 SQL 語句構建 Statement 或者 PrepareStatement 。
public class BoundSql { //該字段中記錄了SQL語句,該SQL語句中可能含有"?"佔位符 private final String sql; //SQL中的參數屬性集合 private final List<ParameterMapping> parameterMappings; //客戶端執行SQL時傳入的實際參數值 private final Object parameterObject; //複製 DynamicContext.bindings 集合中的內容 private final Map<String, Object> additionalParameters; //經過 additionalParameters 構建元參數對象 private final MetaObject metaParameters; }
本文整整2周才基本修整完善,順着 Mybatis 的數據庫核心執行流程,咱們大體介紹了 Mybatis 中幾個相對核心的 API,咱們是一邊構建核心架構功能設計圖示,一邊梳理 API 相關知識脈絡,目前基本算是捋清。
其中比較苦惱的問題就是,對於文章內容範圍尺度的把控,每篇文章我都但願講得全面,講得細緻,這兩個堅持就註定了文章的內容不可能全是乾貨,致使略懂的人只能選擇跳躍式閱讀,對於有養分的知識點須要挑挑揀揀,自我取捨;同時還會致使內容篇幅巨長,致使總體閱讀耗時過長,不易快速吸取。
後續嘗試改變一下寫做方式,儘可能輸出乾貨、內容點到爲止,對於須要鉅細剖析的內容,咱們單立文章解讀。
本篇完,本系列下一篇咱們講《 Mybatis系列全解(七):Dao層兩種實現方式 》。
文章持續更新,微信搜索「潘潘和他的朋友們」第一時間閱讀,隨時有驚喜。本文會在 GitHub https://github.com/JavaWorld 收錄,關於熱騰騰的技術、框架、面經、解決方案、摸魚技巧、教程、視頻、漫畫等等等等,咱們都會以最美的姿式第一時間送達,歡迎 Star ~ 咱們將來 不止文章!想進讀者羣的朋友歡迎撩我我的號:panshenlian,備註「加羣」咱們羣裏暢聊, BIU ~