ibatis 核心原理解析

最近查找一個生產問題的緣由,須要深刻研究 ibatis 框架的源碼。雖然最後證實問題的緣由與 ibatis 無關,可是這個過程加深了對 ibatis 框架原理的理解。html

這篇文章主要就來說講 ibatis 框架的原理。java

可能如今不少人已再也不使用 ibatis 或者說也沒聽 ibatis,不過確定瞭解過 Mybatis。ibatis 就是 Mybatis框架的前身,雖然 ibatis 框架已經比較老,可是其核心功能與 Mybatis 一致。

ibatis 解決的痛點

咱們先看一個使用 JDBC 查詢的例子。sql

QueryDaoByJDBC1.png

使用原生 JDBC 查詢,存在兩個痛點:數據庫

  1. 使用很是繁瑣,且須要處理各類數據庫異常,而且還須要關閉各類資源。
  2. 數據轉化麻煩。查詢以前須要從 Java 對象屬性值設置到 PreparedStatement中,查詢返回以後又須要從 ResultSet獲取返回設置到返回對象中。

在 ibatis 中封裝這些繁雜數據庫鏈接查詢代碼,並處理了各種異常以及關閉各類資源。另外 ibatis 自動處理 Java 對象與數據庫類型之間的自動轉化,讓業務代碼與 SQL 代碼之間作到了解耦。數組

數據類型轉化原理

數據類型轉化主要分爲兩類,一,傳入查詢的 Java 對象數據轉化成 SQL 類型數據。二 查詢返回的數據庫信息映射到 Java 對象中。架構

ibatis SQL 須要定義在配置文件中,一個查詢 SQL 語句配置以下:app

<select id="queryName" parameterClass="com.query.QueryDO"  resultClass="com.query.QueryDO" >
        select  * from  TEST_QUERY where ID=#id#

      </select>

ibatis 框架啓動過程將會解析配置文件,生成 MappedStatement 的子類。如 select 配置會生成對應的 SelectStatement 對象。框架

MappedStatement 相關類圖以下。spa

MappedStatement.png

MappedStatement 中將會保存存在兩個重要的對象,ParameterMapResultMap,經過這兩個對象將會完成 Java 類型與數據庫類型的相互轉化。code

Java 對象轉化成數據庫類型

以上面 select 配置爲例,咱們這裏須要作的是從傳入的 com.query.QueryDO對象中獲取屬性值,而後經過 PreparedStatement.setxx 設置到查詢參數中。

ibatis 解析配置中 SQL 語句時,將會獲取 # 之間的內容,將其替換成 ?。而後按照順序保存到一個 ParameterMapping[] 數組中,這個數組將會保存到 ParameterMap 對象中。

ParameterMapping 將會保存解析字段相關信息。

ParameterMapping.png

最終解析後的 SQL 爲:

select  * from  TEST_QUERY where ID=?

該 SQL 就能夠經過 connection.prepareStatement("select * from TEST_QUERY where ID=?"); 生成 PreparedStatement 對象。

接着 ibatis 會根據 ParameterMapping parameterClass 指定的類型建立合適的 dataExchange parameterPlan 對象。

其中 parameterPlan 對象會按照 ParameterMapping 數組中順序保存了變量的 setter 和 getter 方法數組。

dataExchange 會按照 ParameterMapping 數組中的順序使用反射獲取 parameterPlan getter 方法返回值生成 parameters 數組。

最後循環 ParameterMapping 數組,在 TypeHandler 調用 PreparedStatement.setxx 設置相關值。

TypeHandler.png

TypeHandler 存在不少子類,經過這些子類正確處理了 Java 對象與數據庫類型轉化。

轉化的時序圖爲:

Java 對象與數據庫對象轉化

時序圖來源於: https://www.ibm.com/developer...

數據庫字段映射到 Java 對象

SQL 執行結束以後將會返回查詢結果,這裏將會使 SQL 查詢結果轉化爲返回結果 com.query.QueryDO。這裏須要用到上面提到 ResultMap 對象。

當 SQL 執行結束返回 ResultSet 對象以後,使用 ResultSet.getMetaData() 獲取返回信息元數據對象 ResultSetMetaData

ResultSetMetaData 能夠獲取返回結果字段名,類型等信息,而後按照順序存入 ResultMapping 數組中。

而後按照 ResultMapping 數組中使用 TypeHandler調用 ResultSet.getxx 獲取實際返回數據,保存到 columnValues 數組中。

ResultMap 對象會根據 ResultMappingresultClass指定的類型合適的 dataExchange resultPlan對象。resultPlan對象與上面的 parameterPlan 對象同樣也會保存着變量的 setter 和 getter 方法數組。

最後先根據 resultClass 反射生成返回對象,而後使用反射調用 resultPlan setter 方法,依次設置相關值。

映射返回對象時序圖爲:

數據庫字段映射到 Java 對象

時序圖來源於: https://www.ibm.com/developer...

ibatis 樣板代碼

上面講完了 ibatis 數據類型的轉化原理,接着咱們來看下 ibatis 調用 JDBC 樣板代碼。

使用 ibatis 執行查詢語句時,如 queryForObject,調用到 SqlMapExecutorDelegate 。在 SqlMapExecutorDelegate 中將會會作一些前提準備,好比準備事務,最後會將 SQL 語句委託給 SqlExecutor 執行。

image.png

這裏使用委託者模式,接受請求的對象將請求委託給另外一個對象來處理。這種模式的優勢在於解耦了業務代碼與實際執行代碼的聯繫,在於對外隱藏真正執行對象,易於擴展。

SqlExecutor#executeQuery 執行過程主要分爲如下三步。

image.png

第一步,獲取 PreparedStatement,使用 conn.prepareStatement(sql) 獲取。

image.png

第二步調用 PreparedStatement.setxxx 方法設置參數。上文中的 Java 對象類型轉化成 SQL 類型在這裏完成。

第三步,調用 PreparedStatement.execute() 執行 SQL 語句。

第四步,使用 ResultSet 獲取返回值,在這一步將會完成 數據庫類型與 Java 類型的轉化。

幫助連接

深刻分析 iBATIS 框架之系統架構與映射原理

相關文章
相關標籤/搜索