標籤:

MyBatis 原理淺析——基本原理

前言

MyBatis 是一個被廣泛應用的持久化框架。一個簡單的使用示例如下所示,先創建會話工廠,然後從會話工廠中打開會話,通過 class 類型和配置生成 Mapper 介面的代理實現,最後使用 Mapper 進行持久化操作。

本文將從 MyBatis 中的 SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession 和 Mapper 幾個方面入手簡單分析 MyBatis 的實現原理。在後面的系列文章中會進一步具體分析核心類的細節實現。

SqlSessionFactoryBuilder

SqlSessionFactoryBuilder 使用 Builder 模式去生成 SqlSessionFactory,因此只提供了多個 build 方法。這些方法可以接受 XML 配置文件的 Reader 或 InputStream 輸入流,也可以傳入 environment 指定環境或傳入 Properties 作為屬性。

在 build 方法的實現中,首先根據傳入的輸入流、environment 和 Properties 構建 XMLConfigBuilder 對象,然後調用其 parse() 方法解析 XML 文件得到 Configuration 對象,最後創建 SqlSessionFactory 對象並返回。

SqlSessionFactory

SqlSessionFactory 是一個工廠介面,默認實現是 DefaultSqlSessionFactory。SqlSessionFactory 的作用是獲取 SqlSession,因此提供了多個 openSession 方法,支持從 DataSource 數據源和一個給定的連接 Connection 中創建 SqlSession。

openSession 方法的底層實現可以分為 5 步:

①從 Configuration 對象中獲取環境配置 Environment;

②根據環境配置得到事務工廠 TransactionFactory;

③從事務工廠得到事務 Transaction,Transaction 包裝了資料庫連接,處理資料庫連接的創建、準備、提交、回滾和關閉;

④創建執行器 Executor;

⑤創建 SqlSession,返回 DefaultSqlSession 的實例。

其中從 DataSource 數據源創建 SqlSession 的過程如下所示:

Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); }

SqlSession

SqlSession 是一個介面,默認實現是 DefaultSqlSession,提供了多種資料庫操作方式,如 select、selectOne、selectList、insert、update、delete、commit、rollback 和 getMapper 等方法。getMapper 方法用於獲取 Mapper 介面的代理實現。在 MyBatis 中建議使用 Mapper 介面操作資料庫。

資料庫的增刪改查和事務的提交回滾都是通過 Executor 執行的。Executor 有 3 種類型 SIMPLE、REUSE、BATCH,默認使用簡易執行器 SIMPLE,REUSE 類型執行器重用預處理語句,BATCH 類型執行器重用預處理語句和批量更新。Executor 對象的創建在 Configuration 類型的 newExecutor 方法中進行。

Executor 在執行過程中,會用到 StatementHandler、ParameterHandler 和 ResultHandler,其中 StatementHandler 封裝了 java.sql.Statement 的相關操作,ParameterHandler 封裝了 SQL 對參數的處理,ResultHandler 封裝了對返回數據集的處理。Executor 的執行過程,就是對這 3 個對象的調度過程。更多分析在後續文章中進行。

Mapper

Mapper 是通過 JDK 動態代理實現的,在 MapperProxyFactory 中創建 MapperProxy 並進行介面代理封裝。對 Mapper 介面的調用實際上是由 MapperProxy 實現的。

@SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }

在 MapperProxy 中,實現了 InvocationHandler 的 invoke 方法。methodCache 是一個 ConcurrentHashMap,其中存儲了方法與 MapperMethod 的對應關係。從 methodCache 緩存中獲取或創建 MapperMethod 對象,然後調用 MapperMethod 對象的 execute 方法執行資料庫操作。

@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (isDefaultMethod(method)) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; }

創建 MapperMethod 對象時,會在構造函數中初始化 SqlCommand 和MethodSignature。SqlCommand 包含了資料庫操作的名稱,格式為 「介面名.操作名稱」,以及 XML 中配置的操作類型,如 select、update等,把一個 Mapper 介面與 XML中的一個配置結合起來。MethodSignature 是方法的簽名,標記了方法的返回值類型,對於使用 RowBounds(offset 和 limit 配置)、ResultHandler(結果處理回調)作為參數的方法記錄參數位置並初始化參數處理器。

在 MapperMethod 的 execute 方法中,根據 SqlCommand 中的配置選擇 SqlSession 的方法,根據 MethodSignature 的配置處理傳入的參數,調用 SqlSession 的方法進行資料庫操作,最後根據 MethodSignature 的返回值類型返回操作結果。

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

MyBatis 原理淺析--基本原理


推薦閱讀:

Mybatis4:訂單商品模型分析+高級映射
spring mybatis每次訪問資料庫都要創建資料庫鏈接?
Mybatis5:resultMap與resultType總結
Mybatis6:延遲載入
如何在mybatis中調試查看生成的sql語句?

TAG:MyBatis |