Mybatis源碼閱讀(三):結果集映射3.2 —— 嵌套映射

*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新
*************************************優雅的分割線 **********************************
前言

這段時間疫情原因躺在家做咸魚,代碼也沒怎么敲,源碼也沒怎么看,博客拖更了一個月,今天心血來潮繼續讀了點源碼,晚上正好抽空發個博客,證明我還活著。

關于結果集映射,在一個月前的博客中已經將簡單映射給講述完畢,在實際應用中,除了單表查詢以外,還可能通過連表查詢多張表的記錄,這些記錄需要映射成多個java對象,而對象之間存在一對一、一對多等復雜的關聯關系,這時候就需要嵌套映射。
handleRowValues

在前面的一篇博客中提到,結果集映射的核心方法是handleRowValues,在這個方法中,會先判斷ResultMap是否存在嵌套映射,如不存在就視為簡單結果集映射,簡單映射的處理在上一篇博客已經講解完畢,本篇博客講述的是嵌套映射

/*** 結果集映射核心方法** @param rsw* @param resultMap* @param resultHandler* @param rowBounds* @param parentMapping* @throws SQLException*/
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {if (resultMap.hasNestedResultMaps()) {ensureNoRowBounds();checkResultHandler();// 嵌套映射handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);} else {// 簡單結果集映射(單表)handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);}
}

handleRowValuesForNestedResultMap

該方法是處理嵌套映射的核心方法,有以下主要步驟

通過skipRows方法定位到指定的記錄行
通過shouldProcessMoreRows方法檢測是否能夠繼續映射結果集中剩余的記錄行
調用resolveDiscriminatedResultMap方法,根據ResultMap中記錄的Discriminator對象以及參與映射的記錄行中相應的列值,決定映射使用的ResultMap對象。
通過createRowKey方法為該行記錄生成CacheKey,CacheKey作為緩存中的key值,同時在嵌套映射中也作為key唯一標識一個結果集對象。
根據上面步驟生成的CacheKey查詢DefaultRe.nestedResultObjects集合,這個字段是一個HashMap,在處理嵌套映射過程中生成的所有結果對象,都會生成相應的CacheKey并保存到該集合。
檢測<select>節點中resultOrdered屬性的配置,該設置僅對嵌套映射有效。當Ordered屬性為true時,則認為返回一個主結果行
通過getRowValue,完成當前記錄行的映射操作并返回結果對象,其中還會講結果對象添加到nestedResultObjects集合中。
通過storeObject方法將生成的結果對象保存在ResultHandler中。

handleRowValuesForNestedResultMap方法代碼如下。、

/*** 處理嵌套映射* @param rsw* @param resultMap* @param resultHandler* @param rowBounds* @param parentMapping* @throws SQLException*/
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();// 定位到指定的行skipRows(resultSet, rowBounds);Object rowValue = previousRowValue;// 檢測在定位到指定行之后,是否還有需要映射的數據while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {// 得到本次查詢使用的ResultMapfinal ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);// 為該行記錄生成CacheKey,作為緩存中的key值final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);// 根據緩存key先獲取映射緩存Object partialObject = nestedResultObjects.get(rowKey);// 檢測select節點中的resultOrder屬性。該屬性只針對嵌套映射有效。// 當true時則認為返回一個主結果行時,不會記錄nestedResultObjectif (mappedStatement.isResultOrdered()) {// 主結果對象發生變化if (partialObject == null && rowValue != null) {// 清空緩存集合nestedResultObjects.clear();// 保存主結果對象storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);}// 獲取映射結果rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);} else {rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);if (partialObject == null) {// 將生成結果保存到ResultHandlerstoreObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);}}}if (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds)) {storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);previousRowValue = null;} else if (rowValue != null) {previousRowValue = rowValue;}
}

前面一部分代碼的分析在簡單映射中已經描述過,不記得的朋友可以查看一下上一篇源碼閱讀文章,這里從createRowKey方法開始。
createRowKey

createRowKey方法主要負責生成CacheKey,該方法構建CacheKey的過程如下。

嘗試使用<idArg>節點或者<id>節點中定義的列名以及該列在當前記錄行中對應的列值生成CacheKey
如果ResultMap中沒有定義這兩個節點,則有ResultMap中明確要映射的列名以及它們在當前記錄行中對應的列值一起構成CacheKey對象
經過上面兩個步驟后如果依然查不到相關的列名和列值,且ResultMap的type屬性明確指明了結果對象為Map類型,則有結果集中所有列名以及改行記錄行的所有列值一起構成CacheKey
如果映射的結果對象不是Map,則由結果集中未映射的列名以及它們在當前記錄行中的對應列值一起構成CacheKey

createRowKey代碼如下

/*** 創建一個CacheKey,作為緩存中的key值,在嵌套映射中也作為key唯一標識一個結果對象* @param resultMap* @param rsw* @param columnPrefix* @return* @throws SQLException*/
private CacheKey createRowKey(ResultMap resultMap, ResultSetWrapper rsw, String columnPrefix) throws SQLException {final CacheKey cacheKey = new CacheKey();// 將resultMap的id屬性作為CacheKey的一部分cacheKey.update(resultMap.getId());// 查找ResultMapping集合List<ResultMapping> resultMappings = getResultMappingsForRowKey(resultMap);// 沒找到if (resultMappings.isEmpty()) {if (Map.class.isAssignableFrom(resultMap.getType())) {// 由結果集中的所有列名以及當前記錄行的所有列值一起構成CacheKeycreateRowKeyForMap(rsw, cacheKey);} else {// 由結果集中未映射的列名以及它們在當前記錄行中的對應列值一起構成CacheKey對象createRowKeyForUnmappedProperties(resultMap, rsw, cacheKey, columnPrefix);}} else {// 由ResultMapping集合中的列名以及它們在當前記錄行中相應的列值一起構成CacheKeycreateRowKeyForMappedProperties(resultMap, rsw, cacheKey, resultMappings, columnPrefix);}// 如果在上面的過程沒有找到任何列參與構成CacheKey對象,則返回NullCacheKeyif (cacheKey.getUpdateCount() < 2) {return CacheKey.NULL_CACHE_KEY;}return cacheKey;
}

其中,getResultMappingsForRowKey方法首先檢查ResultMap中是否定義了idArg或者id節點,如果是則返回idResultMappings集合,否則返回propertyResultMappings集合

/*** 獲取ResultMapping集合* @param resultMap* @return*/
private List<ResultMapping> getResultMappingsForRowKey(ResultMap resultMap) {//首先檢查resultMap中是否定義了idArg節點或者id節點List<ResultMapping> resultMappings = resultMap.getIdResultMappings();if (resultMappings.isEmpty()) {// propertyResultMappings集合記錄了除id和constructor節點以外的ResultMapping對象resultMappings = resultMap.getPropertyResultMappings();}return resultMappings;
}

createRowKeyForMap、createRowKeyForUnmappedProperties和createRowKeyForMappedProperties三個方法核心邏輯都是通過CacheKey的update方法,將指定的列名以及它們在當前記錄行中相應的列值添加到CacheKey,使之成為CacheKey對象的一部分。

這里只介紹createRowKeyForMappedProperties

/*** 核心邏輯是通過CacheKey.update方法,將指定的列名以及它們在當前記錄行中相應的列值添加到CacheKey* @param resultMap* @param rsw* @param cacheKey* @param resultMappings* @param columnPrefix* @throws SQLException*/
private void createRowKeyForMappedProperties(ResultMap resultMap, ResultSetWrapper rsw, CacheKey cacheKey, List<ResultMapping> resultMappings, String columnPrefix) throws SQLException {for (ResultMapping resultMapping : resultMappings) {// 如果存在嵌套映射,并且resultSet不為空if (resultMapping.getNestedResultMapId() != null && resultMapping.getResultSet() == null) {// 如果存在嵌套映射,遞歸調用該方法處理final ResultMap nestedResultMap = configuration.getResultMap(resultMapping.getNestedResultMapId());createRowKeyForMappedProperties(nestedResultMap, rsw, cacheKey, nestedResultMap.getConstructorResultMappings(),prependPrefix(resultMapping.getColumnPrefix(), columnPrefix));} else if (resultMapping.getNestedQueryId() == null) {// 忽略嵌套查詢// 獲取該列名稱final String column = prependPrefix(resultMapping.getColumn(), columnPrefix);// 獲取該列相應的TypeHandlerfinal TypeHandler<?> th = resultMapping.getTypeHandler();// 獲取映射的列名List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);if (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) {// 獲取列值final Object value = th.getResult(rsw.getResultSet(), column);if (value != null || configuration.isReturnInstanceForEmptyRow()) {// 將列值和列名添加到CacheKey中cacheKey.update(column);cacheKey.update(value);}}}}
}

getRowValue方法

getRowValue方法主要負責對數據集中的一行記錄進行映射。在處理嵌套映射的過程中,會調用getRowValue方法,完成對記錄行的映射,步驟如下。

檢測外層對象是否已經存在

如果外層對象不存在

調用createRowObject方法創建外層對象
將外層對象添加到DefaultResultSetHandler.ancestorObject集合中,其中key是ResultMap的id,value為外層對象。
通過通過applyNestedResultMappings方法處理嵌套映射,其中會將生成的結果對象設置到外層對象的相應的屬性中。
將外層的ancestorObject集合中移除
將外層對象保存到nestedResultObjects集合中。

如果外層對象已存在

將外層對象添加到ancestorObjects集合中
通過applyNestedResultMappings方法處理嵌套映射,其中會將生成的結果對象設置到外層對象的相應屬性中
將外層對象從ancestorObjects集合中移除。

getRowValue方法代碼如下

/*** 完成對嵌套查詢記錄的映射* @param rsw* @param resultMap* @param combinedKey* @param columnPrefix* @param partialObject* @return* @throws SQLException*/
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix, Object partialObject) throws SQLException {final String resultMapId = resultMap.getId();Object rowValue = partialObject;if (rowValue != null) {// 外層對象存在final MetaObject metaObject = configuration.newMetaObject(rowValue);// 將外層對象添加到ancestorObjectsputAncestor(rowValue, resultMapId);// 處理嵌套映射,其中會將生成的結果對象設置到外層對象的相應屬性中applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);// 將外層對象從ancestorObjects移除ancestorObjects.remove(resultMapId);} else {// 外層對象不存在final ResultLoaderMap lazyLoader = new ResultLoaderMap();// 創建外層對象rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {final MetaObject metaObject = configuration.newMetaObject(rowValue);boolean foundValues = this.useConstructorMappings;// 檢測是否開啟自動映射if (shouldApplyAutomaticMappings(resultMap, true)) {// 自動映射foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;}// 處理ResultMap找那個明確需要映射的列foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;putAncestor(rowValue, resultMapId);// 處理嵌套映射,將生成的結果對象設置到外層對象的相應的屬性中foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;// 將外層對象從ancestorObjects集合中移除ancestorObjects.remove(resultMapId);foundValues = lazyLoader.size() > 0 || foundValues;rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;}if (combinedKey != CacheKey.NULL_CACHE_KEY) {// 將外層對象添加到nestedResultObjectsnestedResultObjects.put(combinedKey, rowValue);}}return rowValue;
}

applyNestedResultMappings方法

處理嵌套邏輯的核心在這個方法中,該方法會遍歷ResultMap.propertyResultMappings集合中記錄的ResultMapping對象,并處理其中的嵌套映射。該方法步驟如下。

獲取ResultMapping.nestedResultMapId字段值,該值不為空則表示存在相應的嵌套映射要處理。同時還會檢測ResultMapping.resultSet字段,它指定了要映射的結果及名稱,該屬性的映射在前面的handleResultSets方法中完成。
通過resolveDiscriminatedResultMap方法確定嵌套映射使用的ResultMap對象
處理循環引用的場景,如果不存在循環引用的情況,則繼續后面的映射流程。如果存在循環引用,則不在創建新的對象,而是重用前面的對象
通過createRowKey方法為嵌套對象創建CacheKey。該過程除了根據嵌套對象的信息創建CacheKey,還會與外層對象的CacheKey合并,得到全局唯一的CacheKey
如果外層對象中用于記錄當前嵌套對象的屬性為Collection并且未初始化,則會通過instantiateCollectionPropertyIfAppropriate方法初始化該對象
根據association、collection等節點的notNullColumn屬性,檢測結果集中相應的列是否為空
調用getRowValue方法完成嵌套映射,并生成嵌套對象。嵌套對象可以嵌套多層,也就可以產生多層遞歸。
通過linkObjects方法,將上一步驟得到的嵌套對象保存到外層對象。

applyNestedResultMappings方法代碼如下

/*** 處理嵌套映射的核心代碼* @param rsw* @param resultMap* @param metaObject* @param parentPrefix* @param parentRowKey* @param newObject* @return*/
private boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String parentPrefix, CacheKey parentRowKey, boolean newObject) {boolean foundValues = false;for (ResultMapping resultMapping : resultMap.getPropertyResultMappings()) {// 獲取引用其他的ResultMap的idfinal String nestedResultMapId = resultMapping.getNestedResultMapId();// 如果指定了嵌套映射的id,并且尚未映射if (nestedResultMapId != null && resultMapping.getResultSet() == null) {try {// 獲取列前綴final String columnPrefix = getColumnPrefix(parentPrefix, resultMapping);// 根據上面獲取到的嵌套映射id去從配置中找到對應的ResultMapfinal ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);// 列前綴為空的情況下處理,一般不去用if (resultMapping.getColumnPrefix() == null) {Object ancestorObject = ancestorObjects.get(nestedResultMapId);if (ancestorObject != null) {if (newObject) {linkObjects(metaObject, resultMapping, ancestorObject);}continue;}}// 為嵌套對象創建CacheKey,該過程創建的CacheKey還會與外層對象的CacheKey合并final CacheKey rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);// 合并CacheKeyfinal CacheKey combinedKey = combineKeys(rowKey, parentRowKey);Object rowValue = nestedResultObjects.get(combinedKey);boolean knownValue = rowValue != null;// 如果嵌套對象是集合,并且沒有初始化,會調用該方法對其進行初始化instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);// 根據notNullColumn屬性,檢測結果集中相應的列是否為空if (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw)) {// 獲取映射結果rowValue = getRowValue(rsw, nestedResultMap, combinedKey, columnPrefix, rowValue);if (rowValue != null && !knownValue) {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;
}

結語

距離上一篇源碼分析的博客已經間隔了一個多月,最近在家閑夠了就著手繼續寫博客了,關于這塊的內容不會棄坑,只是偶爾會拖更一下下。。

*************************************優雅的分割線 **********************************

分享一波:程序員賺外快-必看的巔峰干貨

如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程

請關注微信公眾號:HB荷包
在這里插入圖片描述
一個能讓你學習技術和賺錢方法的公眾號,持續更新
*************************************優雅的分割線 **********************************

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/news/536973.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536973.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536973.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

18.requests

多多的的轉載于:https://www.cnblogs.com/yangyangchunchun/p/10368337.html

gridview獲取選中行數據_Word轉Excel,不想熬夜加班,那就掌握這個數據清洗方法...

私信回復關鍵詞【福利】~獲取豐富辦公資源&#xff0c;助你高效辦公早下班&#xff01;小伙伴們&#xff0c;大家好&#xff0c;我是專治各種疑難雜「數」的農夫~今天&#xff0c;我就為大家介紹一種高效的數據清洗方法&#xff0c;助你告別熬夜加班&#xff0c;擁抱美好的夜晚…

如何深入學習python_菜鳥如何學好python

python在我國發展得如火如荼&#xff0c;因其操作簡單&#xff0c;應用廣泛受到很多人的喜歡。下面小編就來說說菜鳥如何學好python&#xff0c;一起來看看吧!1. 了解編程的基礎知識種是變量、編程規范、基本語法等&#xff0c;這也是開始編寫Python代碼的先決條件。第二種是數…

HTML5中本地儲存概念是什么,什么優點 ,與cookie有什么區別?

html5中的Web Storage 包括了兩種存儲方式&#xff1a; sessionStorage 和 localStorage. seessionStorage 用于本地存儲一個會話&#xff08;session&#xff09;中的數據&#xff0c;這些數據只有在同一個會話中的頁面才能訪問并且當會話結束后數據也隨之銷毀。因此session…

Mybatis源碼閱讀(三):結果集映射3.3 —— 主鍵生成策略

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

list最大容量_Java 基礎(四)集合源碼解析 List

List 接口前面我們學習了Iterator、Collection&#xff0c;為集合的學習打下了基礎&#xff0c;現在我們來學習集合的第一大體系 List。List 是一個接口&#xff0c;定義了一組元素是有序的、可重復的集合。List 繼承自 Collection&#xff0c;較之 Collection&#xff0c;List…

Mybatis源碼閱讀(四):核心接口4.1——StatementHandler

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

Shell學習之結合正則表達式與通配符的使用(五)

Shell學習之結合正則表達式與通配符的使用 目錄 通配符 正則表達式與通配符通配符通配符的使用正則表達式 正則表達式正則表達式的使用通配符 正則表達式與通配符 正則表達式用來在文件中匹配符合條件的字符串&#xff0c;正則是包含匹配。grep、awk、sed等命令可以支持正則表達…

Mybatis源碼閱讀(四):核心接口4.2——Executor(上)

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

接收xml參數_SpringBoot實戰(二):接收xml請求

強烈推薦一個大神的人工智能的教程&#xff1a;http://www.captainbed.net/zhanghan【前言】最近在對接一個第三方系統&#xff0c;需要接收第三方系統的回調&#xff0c;而且格式為XML形式&#xff0c;之前自己一般接收的參數是Json形式&#xff0c;于是乎做個實驗驗證一下使用…

報錯 插入更新_window如何解決mysql數據量過大導致的報錯

window如何解決報錯“The total number of locks exceeds the lock table size”第一大步&#xff0c;查看mysql配置信息在CMD中輸入mysql -hlocalhost -uroot -p #如果設置了密碼直接接在p 后面 show variables like %storage_engine%以下為結果可以看到InnoDB是MySQL的默認引…

148. Sort List

Sort a linked list in O(n log n) time using constant space complexity. Example 1: Input: 4->2->1->3 Output: 1->2->3->4 Example 2: Input: -1->5->3->4->0 Output: -1->0->3->4->5難度&#xff1a;medium 題目&#xff1a;排…

Mybatis源碼閱讀(四):核心接口4.2——Executor(下)

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

python解橢圓方程的例題_橢圓標準方程典型例題及練習題

橢圓標準方程典型例題例1已知P 點在以坐標軸為對稱軸的橢圓上&#xff0c;點P 到兩焦點的距離分別為354和352&#xff0c;過P 點作焦點所在軸的垂線&#xff0c;它恰好過橢圓的一個焦點&#xff0c;求橢圓方程&#xff0e; 解&#xff1a;設兩焦點為1F 、2F &#xff0c;且3541…

leetcode393. UTF-8 Validation

題目要求 A character in UTF8 can be from 1 to 4 bytes long, subjected to the following rules:For 1-byte character, the first bit is a 0, followed by its unicode code. For n-bytes character, the first n-bits are all ones, the n1 bit is 0, followed by n-1 by…

Mybatis源碼閱讀(五 ):接口層——SqlSession

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

插入公式_一個小工具,徹底幫你搞定在Markdown中插入公式的問題

在編輯Markdown文檔時&#xff0c;插入公式是一個挺麻煩的活兒。需要掌握LaTex語法。我自己看完語法后&#xff0c;直接放棄&#xff0c;這絕對是反人類的語法。&#xff08;好吧&#xff0c;是我不會用...&#xff09;但是&#xff0c;我相信你看了這篇文章后&#xff0c;絕對…

JavaScript數據結構與算法——字典

1.字典數據結構 在字典中&#xff0c;存儲的是【鍵&#xff0c;值】對&#xff0c;其中鍵名是用來查詢特定元素的。字典和集合很相似&#xff0c;集合以【值&#xff0c;值】的形式存儲&#xff0c;字典則是用【鍵&#xff0c;值】對的形式存儲。字典也稱作映射。 2.創建字典 f…

Mybatis源碼閱讀(一):Mybatis初始化1.2 —— 解析別名、插件、對象工廠、反射工具箱、環境

*************************************優雅的分割線 ********************************** 分享一波:程序員賺外快-必看的巔峰干貨 如果以上內容對你覺得有用,并想獲取更多的賺錢方式和免費的技術教程 請關注微信公眾號:HB荷包 一個能讓你學習技術和賺錢方法的公眾號,持續更…

中西方對時間的差異_中西方時間觀念差異 英文

The concept of time(時間觀念)①Inchina&#xff0c;words and phrases about time are very general. Forexample&#xff0c;ifyoudatewithsomeone,mostofChineseusedtoanswer: in the afternoon /at night/after a while and so on.Butinwestern,peoplehaveaverystrongconc…