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

mybatis?collection和association的區(qū)別解析

 更新時間:2022年07月14日 15:41:57   作者:monianxd  
這篇文章主要介紹了mybatis?collection解析以及和association的區(qū)別,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

1.collection標簽

說到mybatis的collection標簽,我們肯定不陌生,可以通過它解決一對多的映射問題,舉個例子一個用戶對應多個系統權限,通過對用戶表和權限表的關聯查詢我們可以得到好多條記錄,但是用戶信息這部分在多條記錄中是重復的,只有權限不同,我們需要把這多條權限記錄映射到這個用戶之中,這個時候可以通過collection標簽/association標簽來解決(雖然assocation標簽一般是解決一對一問題的,但它實際上也能實現我們的需求,可以通過后面的源碼看出來)

1.1 相關代碼和運行結果

實體類和mapper代碼

public class Test {

  public static void main(String[] args) throws IOException {

    try (InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml")) {
      // 構建session工廠 DefaultSqlSessionFactory
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
      SqlSession sqlSession = sqlSessionFactory.openSession();
      UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
      UserDO userDO = userMapper.getByUserId(1);
      System.out.println(userDO);
    }
  }

}

運行結果如下,可以看到權限記錄映射到屬性permitDOList 的list列表了

1.2 collection部分源碼解析

通過PreparedStatement查詢完之后得到ResultSet結果集,之后需要將結果集解析為java的pojo類中,下面通過源碼簡單講下是如何解析的

 public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    // 是否有嵌套的resultMaps
    if (resultMap.hasNestedResultMaps()) {
      ensureNoRowBounds();
      checkResultHandler();
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
      // 無嵌套
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  }

根據有無嵌套分成兩層邏輯,有嵌套resultMaps就是指<resultMap>標簽下有子標簽<collection>或<association>,分析下第一層

 private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    final DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
    ResultSet resultSet = rsw.getResultSet();
    // 跳過offset行
    skipRows(resultSet, rowBounds);
    // 上一次獲取的數據
    Object rowValue = previousRowValue;
    // 已獲取記錄數量小于limit
    while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
      // 鑒別器解析
      final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
      // 創(chuàng)建緩存key resultMapId + (columnName + columnValue)....
      final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
      // 部分對象(可能存在對象內容缺失未完全合并)
      Object partialObject = nestedResultObjects.get(rowKey);
      // issue #577 && #542
      // 關于resultOrdered的理解,舉例若查詢得到四條記錄a,a,b,a , 相同可以合并。
      // 那么當resultOrdered=true時,最后可以得到三條記錄,第一條和第二條合并成一條、第三條單獨一條、第四條也是單獨一條記錄
      // resultOrdered=false時,最后可以得到兩條記錄,第一條、第二條和第四條會合并成一條,第三條單獨一條記錄
      // 另外存儲到resultHandler的時機也不一樣,resultOrdered=true是等遇到不可合并的記錄的時候才把之前已經合并的記錄存儲,
      // 而resultOrdered=false是直接存儲的后續(xù)有合并的記錄再處理添加到集合屬性中
      if (mappedStatement.isResultOrdered()) {
        // partialObject為null,說明這一條記錄不可與上一條記錄進行合并了,那么清空nestedResultObjects防止之后出現有可合并的記錄的時候繼續(xù)合并
        // 然后將記錄存儲到resultHandler里面
        if (partialObject == null && rowValue != null) {
          nestedResultObjects.clear();
          storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
        }
        rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
      } else {
        // 處理resultSet的當前這一條記錄
        rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
        if (partialObject == null) {
          // 將記錄存儲到resultHandler里面
          storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
        }
      }
    }

這段代碼主要是創(chuàng)建了一個緩存key,主要是根據resultMapId和<id>標簽的column和對應的columvalue來創(chuàng)建的(若沒有<id>標簽,則會使用所有的<result>標簽的column和columnValue來創(chuàng)建),以此緩存鍵來區(qū)分記錄是否可合并。nestedResultObjects是一個儲存結果的map,以緩存鍵為key,實體類(本例中為UserDO)為value,若能以cacheKey取到值,則說明本條記錄可合并。

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix, Object partialObject) throws SQLException {
    final String resultMapId = resultMap.getId();
    Object rowValue = partialObject;
    // rowValue不等于null時,說明此條記錄可合并
    if (rowValue != null) {
      final MetaObject metaObject = configuration.newMetaObject(rowValue);
      putAncestor(rowValue, resultMapId);
      applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
      ancestorObjects.remove(resultMapId);
    } else {
      final ResultLoaderMap lazyLoader = new ResultLoaderMap();
      // 創(chuàng)建result接收對象,本例中是UserDO對象
      rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
      if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
        final MetaObject metaObject = configuration.newMetaObject(rowValue);
        boolean foundValues = this.useConstructorMappings;
        // 是否將查詢出來的字段全部映射 默認false
        if (shouldApplyAutomaticMappings(resultMap, true)) {
          foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
        }
        // 設置需要映射的屬性值,不管有嵌套ResultMap的
        foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
        // 存放第一條數據
        putAncestor(rowValue, resultMapId);
        // 處理有嵌套的resultMapping
        foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
        ancestorObjects.remove(resultMapId);
        foundValues = lazyLoader.size() > 0 || foundValues;
        rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
      }
      // 將最終結果放入到nestedResultObjects中
      if (combinedKey != CacheKey.NULL_CACHE_KEY) {
        nestedResultObjects.put(combinedKey, rowValue);
      }
    }
    return rowValue;
  }

getRowValue方法主要是將ResultSet解析為實體類對象,applyPropertyMappings填充<id><result>標簽的實體屬性值

private boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String parentPrefix, CacheKey parentRowKey, boolean newObject) {
    boolean foundValues = false;
    for (ResultMapping resultMapping : resultMap.getPropertyResultMappings()) {
      // 嵌套id
      final String nestedResultMapId = resultMapping.getNestedResultMapId();
      // resultMapping有嵌套的map才繼續(xù) <association> <collection>
      if (nestedResultMapId != null && resultMapping.getResultSet() == null) {
        try {
          final String columnPrefix = getColumnPrefix(parentPrefix, resultMapping);
          // 獲取嵌套(經過一次鑒權)的ResultMap
          final ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);
          if (resultMapping.getColumnPrefix() == null) {
            // try to fill circular reference only when columnPrefix
            // is not specified for the nested result map (issue #215)
            Object ancestorObject = ancestorObjects.get(nestedResultMapId);
            if (ancestorObject != null) {
              if (newObject) {
                linkObjects(metaObject, resultMapping, ancestorObject); // issue #385
              }
              continue;
            }
          }
          // 構建嵌套map的key
          final CacheKey rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);
          // 合并cacheKey
          final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);
          // 嘗試獲取之前是否已經創(chuàng)建過
          Object rowValue = nestedResultObjects.get(combinedKey);
          boolean knownValue = rowValue != null;
          // 實例化集合屬性 list復制為空列表
          instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject); // mandatory
          // 存在指定的非空列存在空值則返回false
          if (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw)) {
            rowValue = getRowValue(rsw, nestedResultMap, combinedKey, columnPrefix, rowValue);
            if (rowValue != null && !knownValue) {
              // 合并記錄,設置對象-association或將對象添加到集合屬性中-collection
              linkObjects(metaObject, resultMapping, rowValue);
              foundValues = true;
            }
          }
        } catch (SQLException e) {
          throw new ExecutorException("Error getting nested result map values for '" + resultMapping.getProperty() + "'.  Cause: " + e, e);
        }
      }
    }
    return foundValues;
  }

處理嵌套的結果映射,其實就是<collection><association>標簽。同時調用getRowValue方法根據<collection>指定的resultMap獲取實體對象(這里是PermitDO對象),然后調用linkObjects方法將permitDO對象調用add方法添加到permitDOList中

private void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Object rowValue) {
    final Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);
    // 屬性是集合進行添加 <collection>
    if (collectionProperty != null) {
      final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);
      targetMetaObject.add(rowValue);
    } else {
      // 否則是對象 直接進行setter設置 <association>
      metaObject.setValue(resultMapping.getProperty(), rowValue);
    }
  }

最后就把能合并的記錄都合并在一起了,不同的權限映射到permitDOList這個集合中了

1.3 <collection>和<association>的相同的和不同點

從上面的代碼看來,關于<collection>和<association>標簽都屬于嵌套結果集了,處理邏輯也是基本相同的沒啥區(qū)分,換句話來說,把上面的<collection>替換成<association>標簽其實也能得到相同的結果,關鍵還是pojo類中javaType的屬性,若屬性為List則會創(chuàng)建空的list并將嵌套結果映射添加到list中(即使是一對一的那么list中就只有一條記錄),若屬性為普通對象則直接進行setter設置。

從上面的圖中我們可以看到<collection>和<association>標簽屬性基本相同,<collection>比<association>多了一個ofType屬性,這個ofType屬性其實就是collection集合中單個元素的javaType屬性,<collection>的javaType屬性是繼承了Collection接口的list或set等java集合屬性。

另外在使用習慣上因為我們能確認表和表之間的關系是一對一還是一對多的,能夠確認pojo類中的屬性javaType是使用list還是普通對象,所以一般情況下一對一使用<association>標簽,一對多使用<collection>標簽,語義上更清晰更好理解。

最后

如果說的有問題歡迎提出指正討論,代碼提交在gitee上,感興趣的同學可以下載看看

到此這篇關于mybatis collection解析以及和association的區(qū)別的文章就介紹到這了,更多相關mybatis collection內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java多線程synchronized同步方法詳解

    Java多線程synchronized同步方法詳解

    這篇文章主要介紹了Java多線程synchronized同步方法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-03-03
  • java實現數字猜拳小游戲

    java實現數字猜拳小游戲

    這篇文章主要為大家詳細介紹了java實現數字猜拳的小游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-06-06
  • 百度翻譯API使用詳細教程(前端vue+后端springboot)

    百度翻譯API使用詳細教程(前端vue+后端springboot)

    這篇文章主要給大家介紹了關于百度翻譯API使用的相關資料,百度翻譯API是百度面向開發(fā)者推出的免費翻譯服務開放接口,任何第三方應用或網站都可以通過使用百度翻譯API為用戶提供實時優(yōu)質的多語言翻譯服務,需要的朋友可以參考下
    2024-02-02
  • Java求出任意數字的各個位數之和方式

    Java求出任意數字的各個位數之和方式

    這篇文章主要介紹了Java求出任意數字的各個位數之和方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • 基于Spring-AOP實現自定義分片工具詳解

    基于Spring-AOP實現自定義分片工具詳解

    隨著數據量的增長,在與其他系統交互時,批量接口會出現超時現象,發(fā)現原批量接口在實現時,沒有做分片處理。由于與其他系統交互比較多,一個一個接口去做分片優(yōu)化,改動量較大,所以考慮通過AOP解決此問題,感興趣的可以了解一下
    2022-11-11
  • Java實現利用廣度優(yōu)先遍歷(BFS)計算最短路徑的方法

    Java實現利用廣度優(yōu)先遍歷(BFS)計算最短路徑的方法

    這篇文章主要介紹了Java實現利用廣度優(yōu)先遍歷(BFS)計算最短路徑的方法,實例分析了廣度優(yōu)先遍歷算法的原理與使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-04-04
  • Java?精煉解讀遞歸的概念與使用

    Java?精煉解讀遞歸的概念與使用

    一說起遞歸,我想每個人都不陌生。舉個從小就聽過的例子:從前有座山,山里有座廟,廟里有個和尚,和尚在講故事,從前有座山,山里有座廟,廟里有個和尚,和尚在講故事,從前有座山,要理解遞歸,就得先了解什么是遞歸,實際上這句話就是一個遞歸
    2022-03-03
  • springboot健康檢查監(jiān)控全過程

    springboot健康檢查監(jiān)控全過程

    文章介紹了Spring Boot如何使用Actuator和Micrometer進行健康檢查和監(jiān)控,通過配置和自定義健康指示器,開發(fā)者可以實時監(jiān)控應用組件的狀態(tài),Micrometer支持多種監(jiān)控系統,如Prometheus,而Grafana則用于可視化監(jiān)控數據,文章還提供了配置示例和常見問題解決方案
    2025-01-01
  • Java如何使用ReentrantLock實現長輪詢

    Java如何使用ReentrantLock實現長輪詢

    這篇文章主要介紹了如何使用ReentrantLock實現長輪詢,對ReentrantLock感興趣的同學,可以參考下
    2021-04-04
  • springboot jdbctemplate如何實現多數據源

    springboot jdbctemplate如何實現多數據源

    這篇文章主要介紹了springboot jdbctemplate如何實現多數據源問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07

最新評論

贺州市| 崇信县| 社会| 巨野县| 普兰县| 商城县| 宁夏| 米易县| 顺昌县| 台东县| 五家渠市| 裕民县| 平陆县| 黄冈市| 福海县| 林西县| 黄陵县| 五寨县| 精河县| 稷山县| 繁峙县| 沭阳县| 池州市| 安化县| 肇庆市| 得荣县| 丽江市| 喜德县| 太谷县| 蓬溪县| 北海市| 泗水县| 乌拉特后旗| 鄂托克前旗| 石泉县| 双柏县| 伊春市| 克拉玛依市| 贡山| 沙湾县| 富阳市|