探討Mybatis最硬核的API你知道幾個?

1、Mybatis 架構與核心API

不出意外的話,在後續源碼剖析相關文章中,我們會對 Mybatis 的源碼進行一次大掃蕩,一起挖掘每一處值得大家深入理解/記憶的知識點。而在本文中,我們主要先把 Mybatis 的架構/層次鋪開,俯視 Mybatis 架構的設計全貌,再把幾個硬核的 API 詳細消化。

整體順序脈絡,希望讓你有所期待 ~

我們先簡單揭開 Mybatis 神祕的源碼包,

瞅瞅 Ta 大致目錄結構 :

看,Mybatis 的源代碼包整齊劃一排在 org.apache.ibatis 目錄下,基本設計用途我簡單梳理成上面這張圖,方便大家直觀理解,當然只看源碼包目錄結構,難免會顯得枯燥無物,所以我們再看一下,其實 Mybatis 的源碼包功能上可以是這麼劃分:

上圖讓我們對 Mybatis 的架構有了抽象的理解。

然而,實際上具體的職能分工,核心 API 的場景應用,到底會是怎樣一套流程呈現呢?

看下面這幅功能架構設計,或許你能更好的理解。

根據 Mybatis 功能架構我們劃分成三層:

  • 接口層:該層提供一系列接口讓用戶直接參與使用,包含信息配置與實際數據操作調用。配置方式包括:基於 XML 配置方式、基於 Java API 配置方式兩種方式,用戶也可以通過接口層 API 對數據庫發起增刪改查等操作的請求, 本層接口會把接收到的調用請求交給數據處理層的構件去處理。
  • 數據處理層:該層是 Mybatis 的核心層,負責數據處理,主要包括SQL 參數映射解析、SQL 語句的實際執行、執行結果集的映射處理等。
  • 框架支撐層:該層屬於 Mybatis 的後勤保障層,包括數據庫連接管理、事務把控管理、配置加載與緩存處理、日誌處理、異常處理等,提供基礎支撐能力,保障上層的數據處理。

我們知道,Mybatis 框架讓用戶只需要提供配置信息,並且專注於 SQL 的編寫即可,對於連接管理數據庫/事務,或實際的 SQL 參數映射/語句執行/結果集映射等操作,作爲用戶都並不需要操心和參與。

但是,好奇的我們其實想知道,Mybatis 核心部分的數據處理在整體流程中,是如何支撐用戶請求?同時各個構件之間交互,又是怎樣流轉呢?

很巧,我這裏有一張圖,介紹了整體流程:

根據以上框架流程圖進行講解:

  • 創建配置並調用API :這一環節發生在應用程序端,是開發人員在實際應用程序中進行的兩步操作,第一步創建核心配置文件 Configuration.xml 和映射文件 mapper.xml (通過註解方式也可創建以上兩種配置),準備好基礎配置和 SQL 語句之後;第二步就是直接調用 Mybatis 框架中的數據庫操作接口。
  • 加載配置並初始化 :Mybatis 框架會根據應用程序端提供的核心配置文件與 SQL 映射文件的內容,使用資源輔助類 Resources 把配置文件讀取成輸入流,然後通過對應的解析器解析並封裝到 Configuration 對象和 MappedStatement 對象,最終把對象存儲在內存之中。
  • 創建會話並接收請求 :在 Mybatis 框架加載配置並初始化配置對象之後,會話工廠構建器 SqlSessionFactoryBuilder 同時創建會話工廠 SqlSessionFactory,會話工廠會根據應用程序端的請求,創建會話 SqlSession,以便應用程序端進行數據庫交互。
  • 處理請求 :SqlSession 接收到請求之後,實際上並沒有進行處理,而是把請求轉發給執行器 Executor,執行器再分派到語句處理器 StatementHandler ,語句處理器會結合參數處理器 ParameterHandler ,進行數據庫操作(底層封裝了 JDBC Statement 操作)。
  • 返回處理結果 :每個語句處理器 StatementHandler 處理完成數據庫操作之後,會協同 ResultSetHandler 以及類型處理器 TypeHandler ,對底層 JDBC 返回的結果集進行映射封裝,最終返回封裝對象。

針對以上總體框架流程涉及到的這些硬核 API,下面我們逐個展開介紹,但不會詳細剖析源碼與原理,包括構建細節,因爲這些我們在後續的源碼剖析章節中都會詳細分析。

2、Configuration – 全局配置對象

對於 Mybatis 的全局配置對象 Configuration,我相信無論是初學者還是資深玩家,都不會陌生。整個 Mybatis 的宇宙,都圍繞着 Configuration 轉。Configuration 對象的結構和 config.xml 配置文件的內容幾乎相同,涵蓋了properties (屬性),settings (設置),typeAliases (類型別名),typeHandlers (類型處理器),objectFactory (對象工廠),mappers (映射器)等等,之前我們有專門的一篇文章詳細進行介紹,感興趣的朋友往上翻到目錄列表,找到 《Mybatis系列全解(四):全網最全!Mybatis配置文件XML全貌詳解》 一文詳細品味一番吧。


配置對象 Configuration 通過解析器 XMLConfigBuilder 進行解析,把全局配置文件 Config.xml 與 映射器配置文件 Mapper.xml 中的配置信息全部構建成完整的 Configuration 對象,後續我們源碼分析時詳細剖析整個過程。

3、Resources – 資源輔助類

我們知道,像 Configuration 和 Mapper 的配置信息存放在 XML 文件中,Mybatis 框架在構建配置對象時,必須先把 XML 文件信息加載成流,再做後續的解析封裝,而 Resources 作爲資源的輔助類,恰恰乾的就是這個活,無論是通過加載本地資源或是加載遠程資源,最終都會通過 類加載器 訪問資源文件並輸出文件流。


//加載核心配置文件 
InputStream resourceAsStream =  
    Resources.getResourceAsStream("Config.xml"); 

Resources 實實在在提供了一系列方法分分鐘解決你的文件讀取加載問題:


4、SqlSessionFactoryBuilder – 會話工廠構建器

我們一撞見 xxxBuilder ,就大致能知道它是某類對象的構建器,這裏 SqlSessionFactoryBuilder 也是一樣,它是 Mybatis 中的一個會話工廠構建器,在資源輔助類 Resources 讀取到文件流信息之後,它負責解析文件流信息並構建會話工廠 SqlSessionFactory。(解析的配置文件包含:全局配置 Configuration 與映射器 Mapper)


在程序應用端,我們一般使用 SqlSessionFactoryBuilder 直接構建會話工廠:

當然,如果你集成了 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 中靈活構建會話工廠的一系列接口:


5、SqlSessionFactory – 會話工廠

會話工廠 SqlSessionFactory 是一個接口,作用是生產數據庫會話對象 SqlSession ,有兩個實現類:

  • **DefaultSqlSessionFactory **(默認實現)
  • **SqlSessionManager **(僅多實現了一個 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,你可以指定:

  • 事務處理 :你希望在 session 作用域中使用/開啓事務作用域(也就是不自動提交事務),還是使用自動提交(auto-commit),sqlSession 默認不提交事務,對於增刪改操作時需要手動提交事務。
  • 數據庫連接 :你希望 MyBatis 幫你從已配置的數據源獲取連接,還是使用自己提供的連接,可以動態創建數據源對象 Connection。
  • 執行器類型 :你希望指定某類執行器來創建/執行/預處理語句,可以有普通執行器(SimpleExecutor),或複用執行器(ReuserExecutor)、還是批量執行器(BatchExecutor)等,下面介紹執行器時會詳細說明。
  • 事務隔離級別支持 :支持 JDBC 的五個隔離級別(NONE、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ 和 SERIALIZABLE),對於事務相關的內容,我們後續 《spring 系列全解》 會詳細講,這裏簡單說明一下就是事務隔離級別主要爲了解決例如髒讀、不可重複讀、幻讀等問題,使用不同的事務隔離級別勢必會導致不同的數據庫執行效率,因此我們再不同的系統/功能中,對隔離級別有不同的需求。

SqlSessionFactory一旦被創建就應該在 應用的運行期間 一直存在,沒有任何理由丟棄它或重新創建另一個實例。 使用 SqlSessionFactory 的最佳實踐是在應用運行期間不要重複創建多次,多次重建 SqlSessionFactory 被視爲一種代碼“壞習慣”。因此 SqlSessionFactory 的最佳作用域是 應用作用域 。 最簡單的就是使用單例模式或者靜態單例模式。

請記住,創建 SqlSessionFactory ,一次就好!

每個數據庫對應一個 SqlSessionFactory 實例。SqlSessionFactory 一旦被創建, 它的生命週期應該與應用的生命週期相同 。所以,如果你想連接兩個數據庫,就需要創建兩個 SqlSessionFactory 實例,每個數據庫對應一個;而如果是三個數據庫,就需要三個實例,依此類推。


6、SqlSession – 會話

SqlSession 是一個接口,有兩個實現類:

  • DefaultSqlSession (默認實現)
  • SqlSessionManager (已棄用)

簡單來說,通過會話工廠構建出 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 塊中。

來源:https://www.tuicool.com/articles/a6fiain

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章