1. 程式人生 > >Mybatis框架及原理分析

Mybatis框架及原理分析

框架主要類層次結構

1、Mybatis主要做的工作:

1、根據JDBC規範建立與資料庫的連線
2、通過反射打通Java物件與資料庫引數互動之間相互轉化關係

2、主要類的層次關係

SqlSessionFactoryBuilder類通過各種構造器方法,將所有的配置資訊維持在Configuration類中,返回一個SqlSessionFactory;SqlSessionFactory中的openSession方法會產生一個SqlSession的會話,SqlSession這邊就到了CRUD操作了,然而具體的執行者卻是實現了Executor介面的各種類。具體的初始化過程在下面的4.1裡會詳訴
這裡寫圖片描述

3、主要的類

  • SqlSession:作為mybatis工作的主要頂層API,表示和資料庫互動的會話,完成必要資料庫增刪改查功能
  • Executor:mybatis執行器,是mybatis排程的核心,賦值SQL語句的生成和查詢快取的維護
  • StatementHandler:封裝了JDBC Statement操作,負責對JDBC statement的操作,如設定引數、將Statement結果集轉換成list集合
  • ParameterHandler:負責對使用者傳遞的引數轉換成JDBC Statement所需要的引數
  • ResultSetHandler:負責將JDBC返回的ResultSet結果集物件轉換成List型別的集合
  • TypeHandler:負責java資料型別和jdbc資料型別之間的對映和轉換
  • MappedStatement:維護了一條< select|update|delete|insert>節點的封裝
  • SqlSource:負責根據使用者傳遞的parameterObject,動態地生成SQL語句, 將資訊封裝到BoundSql物件中,並返回
  • BoundSql:表示動態生成的SQL語句以及相應的引數資訊
  • Configuration:MyBatis所有的配置資訊都維持在Configuration物件之中

4、執行過程

在別人的部落格的裡看到一張圖,畫的很好直接拿來用了,雖然叫層次圖,但是應該叫執行過程更合適一點:
這裡寫圖片描述

4.1、初始化

Mybatis的初始化的過程就是解析配置檔案和初始化Configuration的過程,通過流載入mybatis的配置檔案(其中載入關聯的對映檔案),然後SqlSessionFactoryBuilder類構建SQLSession的工廠,即建立SqlSessionFactory建造者物件(這裡用到的是建造者模式)。
這裡寫圖片描述

在這個過程中,XMLConfigBuilder物件會對XML配置檔案進行解析,依次解析properties/settings/../mappers等節點配置,在解析environments節點時,會根據transactionManager的配置來建立事務管理器,根據DataSource的配置來建立DataSource物件,這裡麵包含了資料庫登陸的相關資訊。

在解析mapper節點時,會讀取該節點下所有的mapper檔案,然後進行解析,並將解析後的結果存到configuration物件中,生成Configuration物件,然後根據Configuration物件來建立SqlSession,到這裡時,mybatis的初始化就完成了。

4.2、Mybatis的Sql查詢流程

前面我們概括Mybatis主要乾的事情就是兩件:根據JDBC規範建立與資料庫的連線和通過反射打通Java物件與資料庫引數互動之間相互轉化關係,下面就來解析具體的查詢流程。
查詢的API介面是在SqlSession中宣告的,建立SqlSession的過程其實就是根據Configuration中的配置來建立對應的類,然後呼叫SqlSession中selectOne方法進行SQL查詢,selectOne方法最後呼叫的是selectList,selectList查詢configuration中儲存的MappedStatement物件,mapper檔案sql語句配置對應MappedStatement
物件,呼叫執行器進行查詢操作。
執行器在query操作中,優先會查詢快取是否命中,命中則直接返回,否則從資料庫中查詢。真正的doQuery操作是有SimplyExecutor代理來完成的,該方法中有2個子流程,一個是SQL引數的設定,另一個是SQL查詢操作和結果集的封裝。

4.2.1、SQL查詢引數的設定

獲取和資料庫connection的連線,然後準備Statement,然後就是設定SQL查詢中的引數值。

// SimpleExecutor類
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
}

// DefaultParameterHandler類
public void setParameters(PreparedStatement ps) {
    ......
}
4.2.1、SQL查詢結果集的封裝

ResultSetWrapper是ResultSet的包裝類,呼叫getFirstResultSet方法獲取第一個ResultSet,同時獲取資料庫的MetaData資料,包括資料庫列名、列的型別、類序號等,這些資訊都儲存在ResultSetWrapper類中了,然後呼叫handleResultSet方法來進行結果集的封裝。

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
        if (parentMapping != null) {
            handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
        } else {
            if (resultHandler == null) {
                DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
                handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
                multipleResults.add(defaultResultHandler.getResultList());
            } else {
                handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
            }
        }
    } finally {
        closeResultSet(rsw.getResultSet());
    }
}

結果值的設定在方法handleRowValues中。

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) {
        ensureNoRowBounds();
        checkResultHandler();
        handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
        // 封裝資料
        handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
}

然後會將表中欄位的型別和java中型別一一對應起來,並最後通過java類中對應的set方法,封裝java類結果集。