前言
MyBatis 是一個被普遍應用的持久化框架。一個簡單的使用示例以下所示,先建立會話工廠,而後從會話工廠中打開會話,經過 class 類型和配置生成 Mapper 接口的代理實現,最後使用 Mapper 進行持久化操做。 java
本文將從 MyBatis 中的 SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession 和 Mapper 幾個方面入手簡單分析 MyBatis 的實現原理。在後面的系列文章中會進一步具體分析核心類的細節實現。程序員
SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 使用 Builder 模式去生成 SqlSessionFactory,所以只提供了多個 build 方法。這些方法能夠接受 XML 配置文件的 Reader 或 InputStream 輸入流,也能夠傳入 environment 指定環境或傳入 Properties 做爲屬性。sql
在 build 方法的實現中,首先根據傳入的輸入流、environment 和 Properties 構建 XMLConfigBuilder 對象,而後調用其 parse() 方法解析 XML 文件獲得 Configuration 對象,最後建立 SqlSessionFactory 對象並返回。數據庫
SqlSessionFactory
SqlSessionFactory 是一個工廠接口,默認實現是 DefaultSqlSessionFactory。SqlSessionFactory 的做用是獲取 SqlSession,所以提供了多個 openSession 方法,支持從 DataSource 數據源和一個給定的鏈接 Connection 中建立 SqlSession。緩存
openSession 方法的底層實現能夠分爲 5 步:微信
①從 Configuration 對象中獲取環境配置 Environment;session
②根據環境配置獲得事務工廠 TransactionFactory;app
③從事務工廠獲得事務 Transaction,Transaction 包裝了數據庫鏈接,處理數據庫鏈接的建立、準備、提交、回滾和關閉;框架
④建立執行器 Executor;分佈式
⑤建立 SqlSession,返回 DefaultSqlSession 的實例。
其中從 DataSource 數據源建立 SqlSession 的過程以下所示:
- Transaction tx =null;
- try{
- finalEnvironment environment = configuration.getEnvironment();
- finalTransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
- tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
- finalExecutor executor = configuration.newExecutor(tx, execType);
- returnnewDefaultSqlSession(configuration, executor, autoCommit);
- }catch(Exception e){
- closeTransaction(tx);// may have fetched a connection so lets call close()
- throwExceptionFactory.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(),newClass[]{ mapperInterface }, mapperProxy);
- }
- public T newInstance(SqlSession sqlSession){
- finalMapperProxy<T> mapperProxy =newMapperProxy<T>(sqlSession, mapperInterface, methodCache);
- return newInstance(mapperProxy);
- }
在 MapperProxy 中,實現了 InvocationHandler 的 invoke 方法。methodCache 是一個 ConcurrentHashMap,其中存儲了方法與 MapperMethod 的對應關係。從 methodCache 緩存中獲取或建立 MapperMethod 對象,而後調用 MapperMethod 對象的 execute 方法執行數據庫操做。
- @Override
- publicObject invoke(Object proxy,Method method,Object[] args)throwsThrowable{
- try{
- if(Object.class.equals(method.getDeclaringClass())){
- return method.invoke(this, args);
- }elseif(isDefaultMethod(method)){
- return invokeDefaultMethod(proxy, method, args);
- }
- }catch(Throwable t){
- throwExceptionUtil.unwrapThrowable(t);
- }
- finalMapperMethod mapperMethod = cachedMapperMethod(method);
- return mapperMethod.execute(sqlSession, args);
- }
- privateMapperMethod cachedMapperMethod(Method method){
- MapperMethod mapperMethod = methodCache.get(method);
- if(mapperMethod ==null){
- mapperMethod =newMapperMethod(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 原理淺析--基本原理