1. 程式人生 > 實用技巧 >Mybatis架構相關的知識

Mybatis架構相關的知識

如上所示,這是一個簡單的Mybaits執行流程.

我們其實可以看到,一直到第三步(Sqlsession)那麼一步,這都是我們的程式裡需要建立的.而之後的步驟才是底層完成的任務.

  • 這裡就有了一個引申的概念,四大物件。

    1 executor 呼叫StatementHandler,以此來執行SQL語句。

    這個是結構圖.

    BaseExecutor:這個執行器執行了最基本的功能,其實如果你用debug就能發現。其餘幾個執行器執行時,也會呼叫這個 BaseExecutor中的方法。

    SimpleExecutor:最簡單的執行器,根據對應的sql直接執行即可,不會做一些額外的操作;拼接完SQL之後,直接交給 StatementHandler 去執行。

    BatchExecutor:通過批量操作來優化效能。通常需要注意的是批量更新操作,由於內部有快取的實現,使用完成後記得呼叫flushStatements來清除快取。

    ReuseExecutor :可重用的執行器,重用的物件是Statement,也就是說該執行器會快取同一個sql的Statement,省去Statement的重新建立,優化效能。

      內部的實現是通過一個HashMap來維護Statement物件的。由於當前Map只在該session中有效,所以使用完成後記得呼叫flushStatements來清除Map。

    CachingExecutor:啟用於二級快取時的執行器;

      採用靜態代理;代理一個 Executor 物件。

      執行 update 方法前判斷是否清空二級快取;

      執行 query 方法前先在二級快取中查詢,命中失敗再通過被代理類查詢。

    它使用StaementHandler實現JDBC的操作的。

    2 parameterHandler:

    這個主要是對引數進行操作。

    public void setParameters(PreparedStatement ps) throws SQLException {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    //獲取一個SQL語句集合的引數列表
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    //如果引數列表不為空
    if (parameterMappings != null)
    {
    //迴圈整個引數列表
    for (int i = 0; i < parameterMappings.size(); i++) {
    //取出每一個引數
    ParameterMapping parameterMapping = parameterMappings.get(i);
    //??????
    if (parameterMapping.getMode() != ParameterMode.OUT) {
    //
    Object value;
    //`獲取javaBean中的屬性名稱(如果你用javaBean作為引數輸入到SQL語句中,SQL不也是會為你分配到相應的sql中嘛。
    //這個就是)
    String propertyName = parameterMapping.getProperty();
    //查詢是否有特殊加入的引數
    if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
    value = boundSql.getAdditionalParameter(propertyName);
    } else if (parameterObject == null) {
    value = null;
    } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
    value = parameterObject;
    } else {
    MetaObject metaObject = configuration.newMetaObject(parameterObject);
    value = metaObject.getValue(propertyName);
    }
    //獲得typeHandler。這是一種可以用來對輸入的值進行處理的函式,將java資料型別轉化為資料庫資料型別。
    //你既可以自己定義,Mybatis也為你提供了一些基本的。
    TypeHandler typeHandler = parameterMapping.getTypeHandler();
    //jdbcType是對應的sql引數(這個是你在XML檔案中設定的,用來對應javaType和jdbcType)JDBCtYPE是一個列舉型別
    // <result property="FLD_NUMBER" column="FLD_NUMBER" javaType="double" jdbcType="NUMERIC"/>
    JdbcType jdbcType = parameterMapping.getJdbcType();
    //如果兩者都為空
    if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
    //
    typeHandler.setParameter(ps, i + 1, value, jdbcType);
    }
    }
    }
    }
    //設定引數
    public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
    如果引數列表為空
    if (parameter == null) {
    對應的jdbcType為空
    if (jdbcType == null) {
    可以為空的引數必須設定jdbcType,否則就報錯。
    throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
    }
    try {
    ps.setNull(i, jdbcType.TYPE_CODE);
    } catch (SQLException e) {
    throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
    "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
    "Cause: " + e, e);
    }
    } else {
    //根據傳入引數的不同,呼叫不同種類的set函式,讓其進行替換。
    setNonNullParameter(ps, i, parameter, jdbcType);
    }
    }

    3 ResultSetHandler

    對結果進行操作,將SQL的查詢加入到java型別之中。

    public interface ResultSetHandler {
    // 將Statement執行後產生的結果集(可能有多個結果集)對映為結果列表
    <E> List<E> handleResultSets(Statement stmt) throws SQLException; <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
    // 處理儲存過程執行後的輸出引數
    void handleOutputParameters(CallableStatement cs) throws SQLException; }

    3. DefaultResultSetHandler

      ResultSetHandler的具體實現類是DefaultResultSetHandler,其實現的步驟就是將Statement執行後的結果集,按照Mapper檔案中配置的ResultType或ResultMap來封裝成對應的物件,最後將封裝的物件返回 。

      以最常用的 handleResultSets 為例進行簡單的分析:

    public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); final List<Object> multipleResults = new ArrayList<Object>(); int resultSetCount = 0;
    // 第一個結果集
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    // 獲取 resultMap
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    // 判斷 ResultMap 是否為空,空則拋異常
    validateResultMapsCount(rsw, resultMapCount);
    // 處理第一個結果集
    while (rsw != null && resultMapCount > resultSetCount) {
    ResultMap resultMap = resultMaps.get(resultSetCount);
    // 將結果集對映為對應的 ResultMap 物件
    handleResultSet(rsw, resultMap, multipleResults, null);
    rsw = getNextResultSet(stmt);
    cleanUpAfterHandlingResultSet();
    resultSetCount++;
    } String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
    // 多個結果集
    while (rsw != null && resultSetCount < resultSets.length) {
    ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
    if (parentMapping != null) {
    String nestedResultMapId = parentMapping.getNestedResultMapId();
    ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
    handleResultSet(rsw, resultMap, null, parentMapping);
    }
    rsw = getNextResultSet(stmt);
    cleanUpAfterHandlingResultSet();
    resultSetCount++;
    }
    } return collapseSingleResultList(multipleResults);
    }

    4 StatementHandler:

    這個引數操作了SQL語句的整個執行流程。

這個我以前有文章進行過講解,所以這個就先算了。就不講了。


既然對於四大介面(四大物件)有了基本的瞭解。那麼我們就看看下面的圖片。我想應該不算很難吧。

引數對映部分算是對應:parameterHandler:

XML配置載入部分可以看看如下這張圖片:

引數對映部分在這篇文章中

之後是SQL語句執行:

這個步驟在我看來是對應StatementHandler

結果對映則是由ResultSetHandler完成的.

(當然,這個就是理解一下四大物件在Mybatis中發生什麼作用)


各種常用的Mybatis主要作用。

  • BoundSql類,封裝mybatis最終產生sql的類,包括sql語句,引數,引數源資料等引數:
  • Configuration類:就像是MyBatis的總管,裡麵包含了所有的資訊。無論是連線