Mybatis底層原理學習(三):查詢結果集的處理原理

深夜程猿發表於2018-03-06

【福利】關注微信公眾號:深夜程猿,回覆關鍵字即可獲取學習視訊、簡歷模版

Mybatis底層原理學習(三):查詢結果集的處理原理

Mybatis支援多結果集,也就是結果集中的元素也可以是結果集,返回結果集的時候會呼叫相應的結果集處理器來處理結果集。我們來從原始碼角度看看具體的實現原理

首先,在查詢操作,執行sql語句返回查詢結果集後,呼叫預設的結果集處理器handleResultSets方法

DefaultResultSetHandler#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;
    //  獲取第一個結果集,如果沒有結果集,那麼返回null
    // 獲取第一個結果集在於知道sql語句要操作到哪些元素資料(表的列),會獲取到後設資料名稱、Java資料型別、JDBC資料型別
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    // 獲取執行的sql配置的resultMap
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    // 一個resultMap對應一個結果集,依次遍歷resultMap並處理結果集
    while (rsw != null && resultMapCount > resultSetCount) {// 從條件可以看出,結果集個數和resultMap數量一樣
      ResultMap resultMap = resultMaps.get(resultSetCount);
      // 處理結果集
      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);
  }
複製程式碼

我們看一下是怎麼獲取第一個結果集的

DefaultResultSetHandler#getFirstResultSet
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
    ResultSet rs = stmt.getResultSet();
    while (rs == null) {
       // 沒有結果集,也許是資料庫驅動還沒有返回第一個結果集
      if (stmt.getMoreResults()) {
        // 嘗試再一次獲取結果集
        rs = stmt.getResultSet();
      } else {
        if (stmt.getUpdateCount() == -1) {
          //rs == null && stmt.getUpdateCount() == -1表示驅動已經返回,沒有更多結果,沒有結果集
          break;
        }
      }
    }
    return rs != null ? new ResultSetWrapper(rs, configuration) : null;
  }
複製程式碼
ResultSetWrapper#建構函式
// 封裝結果集
public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
    super();
    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.resultSet = rs;
    final ResultSetMetaData metaData = rs.getMetaData();
    final int columnCount = metaData.getColumnCount();  
    // 設定結果集的後設資料
    for (int i = 1; i <= columnCount; i++) {
      columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
      jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
      classNames.add(metaData.getColumnClassName(i));
    }
  }
複製程式碼

我們繼續看下handleResultSet(rsw, resultMap, multipleResults, null);的具體實現

DefaultResultSetHandler #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 {
      // issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());
    }
  }
複製程式碼

接著看handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);的實現

DefaultResultSetHandler#handleRowValues
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) { // 判斷resultMap是否有巢狀的resultMap
      ensureNoRowBounds();
      checkResultHandler();
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  }
複製程式碼

先看下沒有巢狀resultMap的處理邏輯handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);

DefaultResultSetHandler#handleRowValuesForSimpleResultMap
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
    // 跳過處理過的表資料行
    skipRows(rsw.getResultSet(), rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
      // 處理行資料,並封裝成指定的resultType
      Object rowValue = getRowValue(rsw, discriminatedResultMap);
      // 快取物件,修改rowBounds的值
      storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
    }
  }
複製程式碼

再往下分析怎麼獲取行資料並封裝成指定的resultType

DefaultResultSetHandler#getRowValue
// 獲取行資料值
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
    if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    // 行資料物件不是null,並且沒有結果型別處理器
      final MetaObject metaObject = configuration.newMetaObject(rowValue);
      boolean foundValues = this.useConstructorMappings;
      if (shouldApplyAutomaticMappings(resultMap, false)) {
        foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
      }
      // 應用resultMap的屬性對映配置,為行資料物件設定從資料庫獲取回來的值
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
      foundValues = lazyLoader.size() > 0 || foundValues;
      rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;
    }
    // 返回處理過的行資料物件
    return rowValue;
  }
複製程式碼

簡單分析了結果集的處理原理,它基本步驟:

  • 解析SQL語句
  • 執行SQL語句
  • 根據SQL對映配置,比如resultMap或者resulType,resultSets等來對步驟2獲取回來的結果集處理
  • 返回處理過的結果集

相關文章