亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

MyBatis中ResultSetHandler的具體使用

 更新時間:2025年09月07日 10:55:52   作者:靈魂獵手  
本文主要介紹了MyBatis中的ResultSetHandler及其核心功能和實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

StatementHandler參數(shù)處理&結(jié)果處理,分別委托給ParameterHandler&ResultSetHandler(注意不是ResultHandler)。上一篇文章介紹了ParameterHandler,本文介紹ResultSetHandler。

一、ResultSetHandler接口

我們先看下ResultSetHandler接口定義:

public interface ResultSetHandler {
  // 處理結(jié)果集,返回結(jié)果列表
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;

  // 處理游標結(jié)果集,返回Cursor對象(用于流式處理)
  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

  // 處理存儲過程的輸出參數(shù)
  void handleOutputParameters(CallableStatement cs) throws SQLException;
}

可以看到,ResultSetHandler接口定義了三種不同場景下,處理JDBC結(jié)果集的方法。其核心職責是將數(shù)據(jù)庫返回的ResultSet轉(zhuǎn)換為Java對象

  • handleResultSets:核心方法,處理JDBC的Statement的結(jié)果集,注意普通查詢和存儲過程查詢,都使用這個方法的結(jié)果集。
  • handleCursorResultSets:處理Mapper的返回值是Cursor的方法。
  • handleOutputParameters:處理存儲過程的輸出參數(shù),存儲過程會使用handleResultSets處理結(jié)果集,使用本方法,處理輸出類型的參數(shù)。

二、DefaultResultSetHandler 的核心實現(xiàn)

MyBatis僅提供一個默認實現(xiàn)類DefaultResultSetHandler,我們這里分析下核心的handleResultSets方法源碼。

1. 整體流程:handleResultSets 方法

handleResultSets是處理查詢結(jié)果集的核心方法,負責把Statement的結(jié)果集轉(zhuǎn)換為Java對象,需要注意,無論是普通的查詢還是存儲過程查詢,都是調(diào)用這個方法。handleOutputParameters只是處理存儲過程的參數(shù)的(處理參數(shù)類型為OUT/INOUT類型的)。

注意這個方法名字,handleResultSets,有個s,暗示了結(jié)果集可能是多個

    @Override
    public List<Object> handleResultSets(Statement stmt) throws SQLException {
      ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

      final List<Object> multipleResults = new ArrayList<>();
      int resultSetCount = 0;

      // 獲取第一個結(jié)果集
      ResultSetWrapper rsw = getFirstResultSet(stmt);
      // 獲取MappedStatement中定義的結(jié)果集映射(可能有多個,對應多結(jié)果集)
      List<ResultMap> resultMaps = mappedStatement.getResultMaps();
      int resultMapCount = resultMaps.size();
      validateResultMapsCount(rsw, resultMapCount);

      // 第一部分:處理 MappedStatement 中定義的 resultMaps 對應的結(jié)果集
      while (rsw != null && resultSetCount < resultMaps.size()) {
        ResultMap resultMap = resultMaps.get(resultSetCount);
        // 處理當前結(jié)果集,映射為Java對象
        handleResultSet(rsw, resultMap, multipleResults, null);
        // 獲取下一個結(jié)果集(用于存儲過程多結(jié)果集場景)
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }

      // 第二部分:處理MappedStatement中resultSets指定的附加結(jié)果集(用于解決N+1)
      String[] resultSets = mappedStatement.getResultSets();
      if (resultSets != null) {
        while (rsw != null && resultSetCount < resultSets.length) {
          ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
          // 解決N+1,向父對象設置屬性。
          if (parentMapping != null) {
            String nestedResultMapId = parentMapping.getNestedResultMapId();
            ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
            handleResultSet(rsw, resultMap, multipleResults, parentMapping);
          }
          rsw = getNextResultSet(stmt);
          cleanUpAfterHandlingResultSet();
          resultSetCount++;
        }
      }

      // 合并結(jié)果(如果只有一個結(jié)果集,直接返回該列表)
      return collapseSingleResultList(multipleResults);
    }

核心流程分為兩部分

  1. 處理MappedStatement中定義的 resultMaps 對應的結(jié)果集。
  2. 處理MappedStatement中resultSets指定的附加結(jié)果集,用于解決N+1問題。

兩部分都可能是多個?。?!

原來,Mybatis可以支持一次獲取多個結(jié)果集,很神奇。那么如何返回多個結(jié)果集呢?

  1. Mybatis 如何返回多個結(jié)果集
  2. 也可以通過存儲過程,這個官網(wǎng)有例子XML 映射器-結(jié)果映射(搜索ResultSets,可以解決N+1問題,這是第二個循環(huán)的來源)。

2. 單結(jié)果集處理:handleResultSet 方法

handleResultSet 負責處理單個結(jié)果集,根據(jù)是否有自定義 ResultHandler 選擇不同的處理邏輯:

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
  try {
    if (parentMapping != null) {
      // 處理嵌套結(jié)果(就是上個步驟中的第二個循環(huán))
      handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
    } else {
      if (resultHandler == null) {
        // 無自定義ResultHandler時,使用默認的DefaultResultHandler收集結(jié)果
        DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
        handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
        multipleResults.add(defaultResultHandler.getResultList());
      } else {
        // 有自定義ResultHandler時,使用其處理結(jié)果
        handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
      }
    }
  } finally {
    // 關(guān)閉結(jié)果集
    closeResultSet(rsw.getResultSet());
  }
}

3. 行記錄映射:handleRowValues 與 getRowValue 方法

無論有無自定義ResultHandler,都是調(diào)用handleRowValues方法:

    // 處理結(jié)果集中的行記錄
    public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
      if (resultMap.hasNestedResultMaps()) {
        // 處理嵌套結(jié)果映射(如包含association/collection)
        ensureNoRowBounds();
        checkResultHandler();
        handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
      } else {
        // 處理普通結(jié)果映射(無嵌套)
        handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
      }
    }

看下處理普通映射的方法(處理嵌套的有點復雜,平時不怎么用,看起來頭暈):

    // 處理普通結(jié)果映射(逐行映射)
    private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
      DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
      // 跳過RowBounds指定的偏移量,內(nèi)存分頁
      skipRows(rsw.getResultSet(), rowBounds);
      // 循環(huán)處理每行記錄,直到達到RowBounds的限制或結(jié)果集結(jié)束
      while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
        // 解析鑒別器(discriminator),動態(tài)選擇ResultMap
        ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
        // 將當前行映射為Java對象
        Object rowValue = getRowValue(rsw, discriminatedResultMap);
        // 存儲對象(調(diào)用ResultHandler處理)
        storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
      }
    }

Q:discriminator是什么鬼東西??

A:動態(tài)識別返回對象類型,比如我們定義了一個動物類,查詢結(jié)果需要動態(tài)轉(zhuǎn)為阿貓阿狗,就可以使用這個特性,可以在官網(wǎng)中查看使用方法:XML 映射器-結(jié)果映射(搜索discriminator)。

接下來看下核心的getRowValue方法。

    // 將單行記錄映射為Java對象
    private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
      final ResultLoaderMap lazyLoader = new ResultLoaderMap();
      // 創(chuàng)建結(jié)果對象(通過ObjectFactory)
      Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
      if (rowValue != null && !typeHandlerRegistry.hasTypeHandler(rowValue.getClass())) {
        // 獲取結(jié)果對象的元信息(用于反射設置屬性)
        final MetaObject metaObject = configuration.newMetaObject(rowValue);
        boolean foundValues = this.useConstructorMappings;
        // 處理結(jié)果映射(ResultMapping),設置對象屬性
        if (shouldApplyAutomaticMappings(resultMap, false)) {
          // 自動映射(未在ResultMap中顯式配置的字段)
          foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
        }
        // 顯式映射(ResultMap中配置的result/association/collection)
        foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
        foundValues = lazyLoader.size() > 0 || foundValues;
        rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
      }
      return rowValue;
    }

getRowValue 方法的核心邏輯:

  1. 創(chuàng)建結(jié)果對象:通過 ObjectFactory 實例化目標對象(如 User)。
  2. 自動映射:對 ResultMap 中未顯式配置的字段,根據(jù)列名與屬性名的匹配關(guān)系自動映射。
  3. 顯式映射:根據(jù) ResultMap 中配置的 result、associationcollection 等標簽,通過 TypeHandler 將 ResultSet 中的列值轉(zhuǎn)換為對象屬性。

4. applyAutomaticMappings

除了使用ResultMap標簽進行映射,Mybatis還支持自動映射,也就是通過蛇形轉(zhuǎn)駝峰的方式,將數(shù)據(jù)庫字段,映射成Java屬性:

private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, 
                                     MetaObject metaObject, String columnPrefix) throws SQLException {
  // 1. 創(chuàng)建自動映射列表:篩選出未顯式配置但存在匹配的字段
  List<UnMappedColumnAutoMapping> autoMappings = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
  if (autoMappings.isEmpty()) {
    return false;
  }

  boolean foundValues = false;
  // 2. 遍歷自動映射列表,逐個處理字段
  for (UnMappedColumnAutoMapping mapping : autoMappings) {
    // 2.1 通過 TypeHandler 從結(jié)果集獲取字段值(并轉(zhuǎn)換為 Java 類型)
    final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
    if (value != null) {
      foundValues = true;
    }
    // 2.2 通過 MetaObject 給對象屬性賦值(支持 null 值處理配置)
    if (value != null || configuration.isCallSettersOnNulls() && !mapping.primitive) {
      // 給屬性賦值(如 user.setName(value))
      metaObject.setValue(mapping.property, value);
    }
  }
  return foundValues;
}

關(guān)鍵步驟解析

  1. 創(chuàng)建自動映射列表(createAutomaticMappings):獲取未映射的字段,與JavaBean的屬性建立映射關(guān)系,并根據(jù)JDBC的類型,綁定TypeHandler,帶有緩存,防止每次都重復映射。
  2. 通過TypeHandler轉(zhuǎn)換類型 :熟悉的TypeHandler,與上一節(jié)相反,這個是從JDBC轉(zhuǎn)為Java類型。
  3. 通過MetaObject賦值:利用 MyBatis 的反射工具 MetaObject 給目標對象的屬性賦值,支持配置 callSettersOnNulls(即使值為null也調(diào)用 setter 方法),以及對基本類型(primitive)的特殊處理(避免給基本類型賦null導致異常)。

applyPropertyMappings方法,為Java對象賦值的邏輯與這個相同,但還有些處理嵌套和延遲加載的邏輯,這里不深入研究了。

三、小結(jié)

ResultSetHandler是負責將JDBC 結(jié)果集(ResultSet)轉(zhuǎn)換為Java對象的核心組件。本文對他的實現(xiàn)做了淺顯的分析,復雜的嵌套結(jié)果映射,以及嵌套結(jié)果的延遲加載,本文沒有涉及(有點復雜)。

到此為止,MyBatis四大核心組件(Executor、StatementHandler、ParameterHandler、ResultSetHandler)已經(jīng)分析完成,后續(xù)準備分析下Mybatis的插件機制,這個可能是我們了解Mybatis源碼最重要的用途。

到此這篇關(guān)于MyBatis中ResultSetHandler的具體使用的文章就介紹到這了,更多相關(guān)MyBatis ResultSetHandler內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java中實現(xiàn)用戶之間的通訊方式

    Java中實現(xiàn)用戶之間的通訊方式

    在Java中實現(xiàn)用戶間通訊主要有兩種方法:Socket編程和WebSocket,Socket編程允許兩個設備間進行數(shù)據(jù)交換,適用于基本的網(wǎng)絡通訊,本文提供了兩種方法的基本實現(xiàn)代碼和相關(guān)配置,幫助開發(fā)者根據(jù)需求選擇合適的通訊方式
    2024-09-09
  • WebDriver實現(xiàn)自動化打開IE中的google網(wǎng)頁并實現(xiàn)搜索

    WebDriver實現(xiàn)自動化打開IE中的google網(wǎng)頁并實現(xiàn)搜索

    這篇文章主要介紹了WebDriver實現(xiàn)自動化打開IE中的google網(wǎng)頁并實現(xiàn)搜索,需要的朋友可以參考下
    2014-04-04
  • Mybatis防止sql注入原理分析

    Mybatis防止sql注入原理分析

    這篇文章主要介紹了Mybatis防止sql注入原理分析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Spring boot如何集成kaptcha并生成驗證碼

    Spring boot如何集成kaptcha并生成驗證碼

    這篇文章主要介紹了Spring boot如何集成kaptcha并生成驗證碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-07-07
  • Springboot結(jié)合Mybatis-Plus實現(xiàn)業(yè)務撤銷回滾功能

    Springboot結(jié)合Mybatis-Plus實現(xiàn)業(yè)務撤銷回滾功能

    本文介紹了如何在Springboot結(jié)合Mybatis-Plus實現(xiàn)業(yè)務撤銷回滾功能,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-12-12
  • SpringMVC處理Form表單實例

    SpringMVC處理Form表單實例

    這篇文章主要介紹了使用SpringMVC處理Form表單實例,非常具有參考借鑒價值,感興趣的朋友一起學習吧
    2016-10-10
  • 解決Spring使用@MapperScan問題

    解決Spring使用@MapperScan問題

    這篇文章主要介紹了解決Spring使用@MapperScan問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • java實現(xiàn)屏幕共享功能實例分析

    java實現(xiàn)屏幕共享功能實例分析

    這篇文章主要介紹了java實現(xiàn)屏幕共享功能的方法,以實例形式分析了屏幕共享功能的客戶端與服務端的詳細實現(xiàn)方法,是非常具有實用價值的技巧,需要的朋友可以參考下
    2014-12-12
  • Java接口的回調(diào)機制的實現(xiàn)

    Java接口的回調(diào)機制的實現(xiàn)

    本文主要介紹了Java中回調(diào)機制的實現(xiàn)方式,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-01-01
  • Spring事務中的事務傳播行為使用方式詳解

    Spring事務中的事務傳播行為使用方式詳解

    Spring框架作為一個輕量級的開源框架,在企業(yè)應用開發(fā)中被廣泛使用,在Spring事務管理中,事務傳播行為是非常重要的一部分,它定義了方法如何參與到已經(jīng)存在的事務中或者如何開啟新的事務,本文將詳細介紹Spring事務中的幾種事務傳播行為,詳細講解具體使用方法
    2023-06-06

最新評論