最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MyBatis中ResultSetHandler的具體使用

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

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

一、ResultSetHandler接口

我們先看下ResultSetHandler接口定義:

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

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

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

可以看到,ResultSetHandler接口定義了三種不同場景下,處理JDBC結(jié)果集的方法。其核心職責(zé)是將數(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é)果集的核心方法,負責(zé)把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é)果集映射(可能有多個,對應(yīng)多結(jié)果集)
      List<ResultMap> resultMaps = mappedStatement.getResultMaps();
      int resultMapCount = resultMaps.size();
      validateResultMapsCount(rsw, resultMapCount);

      // 第一部分:處理 MappedStatement 中定義的 resultMaps 對應(yīng)的結(jié)果集
      while (rsw != null && resultSetCount < resultMaps.size()) {
        ResultMap resultMap = resultMaps.get(resultSetCount);
        // 處理當(dāng)前結(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,向父對象設(shè)置屬性。
          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 對應(yīng)的結(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 負責(zé)處理單個結(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);
      }
    }

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

    // 處理普通結(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);
        // 將當(dāng)前行映射為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é)果對象的元信息(用于反射設(shè)置屬性)
        final MetaObject metaObject = configuration.newMetaObject(rowValue);
        boolean foundValues = this.useConstructorMappings;
        // 處理結(jié)果映射(ResultMapping),設(shè)置對象屬性
        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 實例化目標(biāo)對象(如 User)。
  2. 自動映射:對 ResultMap 中未顯式配置的字段,根據(jù)列名與屬性名的匹配關(guān)系自動映射。
  3. 顯式映射:根據(jù) ResultMap 中配置的 result、association、collection 等標(biāo)簽,通過 TypeHandler 將 ResultSet 中的列值轉(zhuǎn)換為對象屬性。

4. applyAutomaticMappings

除了使用ResultMap標(biāo)簽進行映射,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,帶有緩存,防止每次都重復(fù)映射。
  2. 通過TypeHandler轉(zhuǎn)換類型 :熟悉的TypeHandler,與上一節(jié)相反,這個是從JDBC轉(zhuǎn)為Java類型。
  3. 通過MetaObject賦值:利用 MyBatis 的反射工具 MetaObject 給目標(biāo)對象的屬性賦值,支持配置 callSettersOnNulls(即使值為null也調(diào)用 setter 方法),以及對基本類型(primitive)的特殊處理(避免給基本類型賦null導(dǎo)致異常)。

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

三、小結(jié)

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

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

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

相關(guān)文章

  • 五分鐘帶你學(xué)會用java解析json字符串

    五分鐘帶你學(xué)會用java解析json字符串

    這篇文章主要給大家介紹了關(guān)于用java解析json字符串的相關(guān)資料,JSON是一種輕量級的、基于文本的、與語言無關(guān)的數(shù)據(jù)交換格式,易于人和機器讀寫,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-07-07
  • 解決ObjectMapper序列換Map時候的坑

    解決ObjectMapper序列換Map時候的坑

    這篇文章主要介紹了解決ObjectMapper序列換Map時候的坑,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • 淺談Redis持久化的幾種方式

    淺談Redis持久化的幾種方式

    這篇文章主要介紹了淺談Redis持久化的幾種方式,前面說到了Redis持久化的 實現(xiàn)方式主要分為了:快照持久化(RDB)、寫日志持久化(AOF)
    ,其中快照持久化方式也就是RDB ,需要的朋友可以參考下
    2023-08-08
  • Java微信公眾平臺之素材管理

    Java微信公眾平臺之素材管理

    這篇文章主要為大家詳細介紹了Java微信公眾平臺之素材管理,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • Sentinel 集成 Nacos 實現(xiàn)規(guī)則動態(tài)推送與持久化的過程

    Sentinel 集成 Nacos 實現(xiàn)規(guī)則動態(tài)推送與持久化的過程

    本文介紹了如何將Sentinel與Nacos集成,實現(xiàn)流量控制規(guī)則的動態(tài)推送和持久化存儲,本文結(jié)合實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2026-02-02
  • 解決安裝mysqlclient的時候出現(xiàn)Microsoft Visual C++ 14.0 is required報錯

    解決安裝mysqlclient的時候出現(xiàn)Microsoft Visual C++ 14.0 is required報錯

    這篇文章主要介紹了解決安裝mysqlclient的時候出現(xiàn)Microsoft Visual C++ 14.0 is required報錯問題,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-11-11
  • Springboot項目刪除項目同步target文件問題解決方案

    Springboot項目刪除項目同步target文件問題解決方案

    這篇文章主要介紹了Springboot項目刪除項目同步target文件問題解決方案,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-12-12
  • 關(guān)于springboot 配置文件中屬性變量引用方式@@解析

    關(guān)于springboot 配置文件中屬性變量引用方式@@解析

    這篇文章主要介紹了關(guān)于springboot 配置文件中屬性變量引用方式@@解析,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-04-04
  • JDK8新出Optional類的方法探索與思考分析

    JDK8新出Optional類的方法探索與思考分析

    這篇文章主要為大家介紹了JDK8新出Optional類的發(fā)方法示例探索與思考分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • 淺析Java語言中狀態(tài)模式的優(yōu)點

    淺析Java語言中狀態(tài)模式的優(yōu)點

    狀態(tài)模式允許對象在內(nèi)部狀態(tài)改變時改變它的行為,對象看起來好像修改了它的類。這個模式將狀態(tài)封裝成獨立的類,并將動作委托到 代表當(dāng)前狀態(tài)的對象,我們知道行為會隨著內(nèi)部狀態(tài)而改變
    2023-02-02

最新評論

宕昌县| 徐汇区| 青神县| 化州市| 西盟| 河南省| 大同市| 诸城市| 繁峙县| 文水县| 于田县| 博罗县| 通州市| 柞水县| 衢州市| 江西省| 嘉善县| 南郑县| 将乐县| 福泉市| 灌云县| 灵川县| 康保县| 集贤县| 金门县| 云安县| 天全县| 理塘县| 手游| 井陉县| 望江县| 永康市| 平塘县| 从化市| 县级市| 外汇| 乌审旗| 宁德市| 乌鲁木齐县| 赣榆县| 五大连池市|