標籤:

MyBatis 原理淺析 3 ——數據操作

前言

在前文《MyBatis 原理淺析——基本原理》一文中簡單分析了 MyBatis 的實現原理,MyBatis 的資料庫操作是通過 Executor 執行的。Executor 是一個介面,有三個實現類,分別是 SimpleExecutor、ReuseExecutor 和 BatchExecutor。

查詢數據的流程

查詢數據是通過 SqlSession 的方法實現的,SqlSession 封裝了 Executor 的相關操作。以 select 為例,首先根據 SQL 語句關聯的 statement 從 configuration 中獲取 MappedStatement 對象,然後調用 Executor 的 query 方法執行查詢操作。statement 的格式是命名空間+ID,ID 即是 XML 中 select 標籤的 id 屬性。MappedStatement 對象存儲了 XML 中的 SQL 配置,如 ParameterMap、ResultMap 等。

@Overridepublic void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = configuration.getMappedStatement(statement); executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); }}

默認情況下,使用 Executor 介面的 SimpleExecutor 實現。SimpleExecutor 繼承了抽象類 BaseExecutor。BaseExecutor 實現了 Executor 介面的方法,實現了緩存機制,可以從緩存中直接返回查詢結果,但更具體的資料庫操作交給子類 SimpleExecutor 實現。在 SimpleExecutor 類中執行查詢的 doQuery 方法如下所示。首先從 Configuration 中創建 StatementHandler 介面實例,默認使用 RoutingStatementHandler 實現。然後在 prepareStatement 方法中獲取資料庫連接、初始化 Statement、設置參數等。再調用 StatementHandler 的 query 方法,執行數據查詢,查詢結果交給 resultHandler 處理。最後關閉 Statement。

@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); }}

在 RoutingStatementHandler 中會根據配置選擇一個 StatementHandler 的實現用於進一步的數據處理,默認是 PreparedStatementHandler 類。在該類的 query 方法中會執行 Statement 數據查詢請求,請求完成後調用 ResultSetHandler 對查詢結果進行處理和封裝。ResultSetHandler 介面定義了對返回結果集進行處理的方法,默認使用的實現類是 DefaultResultSetHandler,在 handleResultSets 方法中完成結果集到 Java Bean 的映射。

@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.<E> handleResultSets(ps);}

查詢結果的緩存

MyBatis 支持對查詢結果進行緩存,以減少資料庫操作提高效率,默認情況下執行 insert、update、delete 等操作會清理緩存。執行查詢操作前,首先創建 CacheKey 對象,根據 SQL 語句、id、offset、limit 和參數更新 CacheKey 對象,CacheKey 對象會記錄這些數據並更新 hashcode、checksum、count 幾個整數,這些數據都用於判斷 CacheKey 對象是否相等。

public void update(Object object) { int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object); count++; checksum += baseHashCode; baseHashCode *= count; hashcode = multiplier * hashcode + baseHashCode; updateList.add(object);}

select 語句的查詢結果以 Map 形式存放在 PerpetualCache 類中,Map 的 key 是 CacheKey 對象,Map 的 value 是查詢結果。

更新數據的流程

DefaultSqlSession 的 insert、delete 操作都是復用 update 操作實現的,update 的操作流程與 select 類似,不做過多闡述。所不同的是,執行 update 操作前需要先清理緩存,在 update 操作完成以後,還需要返回受影響的行數和 KeyGenerator 配置的自增值。

事務提交與回滾

DefaultSqlSession 的 commit 和 rollback 操作都是在 BaseExecutor 中實現的,先清除緩存,然後清理 Statement,如果有需要再調用 Transaction 的相關方法。如果要求強制執行,或者不自動提交且有臟數據,就會執行 Transaction 的 commit 或 rollback 操作。

每周 3 篇學習筆記或技術總結,面向有一定基礎的 Java 程序員,內容涉及 Java 進階、虛擬機、MySQL、NoSQL、分散式計算、開源框架等多個領域。關注作者或微信公眾號 backend-develop 第一時間獲取最新內容。

MyBatis 原理淺析 3 ——數據操作
推薦閱讀:

Mybatis1:基礎
MyBatis 原理淺析——基本原理
Mybatis7:查詢緩存
Mybatis4:訂單商品模型分析+高級映射

TAG:MyBatis |