news 2026/4/28 3:16:31

【12.MyBatis源码剖析与架构实战】11.嵌套查询循环引⽤源码剖析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【12.MyBatis源码剖析与架构实战】11.嵌套查询循环引⽤源码剖析

MyBatis 嵌套查询循环引用源码深度剖析(含流程图)

在 MyBatis 中,当两个实体相互引用(如UserAddress),且双方都通过<association>select属性配置了嵌套查询时,若没有特殊处理,查询时会发生无限递归,最终导致栈溢出。MyBatis 通过一级缓存(localCacheResultLoaderMap加载状态标记巧妙地解决了循环引用问题,确保在同一个SqlSession内,同一个对象只创建一次,并且循环引用能够正确建立。


一、循环引用示例

考虑以下实体与映射:

publicclassUser{privateLongid;privateStringname;privateAddressaddress;// 持有 Address}publicclassAddress{privateLongid;privateStringstreet;privateUseruser;// 持有 User}

Mapper XML

<!-- UserMapper.xml --><resultMapid="userMap"type="User"><idcolumn="id"property="id"/><resultcolumn="name"property="name"/><associationproperty="address"column="address_id"select="com.example.AddressMapper.selectById"fetchType="eager"/></resultMap><!-- AddressMapper.xml --><resultMapid="addressMap"type="Address"><idcolumn="id"property="id"/><resultcolumn="street"property="street"/><associationproperty="user"column="user_id"select="com.example.UserMapper.selectById"fetchType="eager"/></resultMap>

当调用UserMapper.selectById(1)时,会触发以下调用链:

  1. 查询User(id=1, address_id=100)
  2. 加载User.address→ 执行AddressMapper.selectById(100)
  3. 查询Address(id=100, user_id=1)
  4. 加载Address.user→ 执行UserMapper.selectById(1),回到步骤1

若不加控制,将无限循环。


二、MyBatis 解决循环引用的核心机制

2.1 一级缓存(localCache
  • 每个SqlSession有一个PerpetualCache实例(存储在BaseExecutor.localCache)。
  • 当从ResultSet构建一个对象时,会根据主键生成CacheKey,并在构建完成后放入localCache
  • 后续任何查询(包括嵌套查询)在构建相同主键的对象时,会优先从localCache中获取,而不是重新创建。
2.2createRowKey:生成对象缓存键

DefaultResultSetHandler.createRowKey方法根据ResultMap中配置的<id>列(或如果无显式 id,则使用所有列),从当前ResultSet行提取值,生成唯一的CacheKey

privateCacheKeycreateRowKey(ResultMapresultMap,ResultSetWrapperrsw,StringcolumnPrefix)throwsSQLException{finalCacheKeycacheKey=newCacheKey();finalList<ResultMapping>idMappings=resultMap.getIdResultMappings();for(ResultMappingidMapping:idMappings){finalStringcolumn=prependPrefix(idMapping.getColumn(),columnPrefix);if(column!=null){finalObjectvalue=getColumnValue(rsw,idMapping.getJavaType(),column);cacheKey.update(column);cacheKey.update(value);}}returncacheKey;}
2.3getRowValue中的缓存复用

DefaultResultSetHandler.getRowValue中,会首先尝试从localCache获取已创建的对象:

privateObjectgetRowValue(ResultSetWrapperrsw,ResultMapresultMap,StringcolumnPrefix,ObjectpartialValue,ResultMappingparentMapping)throwsSQLException{finalStringresultMapId=resultMap.getId();finalCacheKeyrowKey=createRowKey(resultMap,rsw,columnPrefix);// 尝试从一级缓存获取ObjectrowValue=partialValue!=null?partialValue:localCache.getObject(rowKey);if(rowValue!=null&&rowValue!=EXECUTION_PLACEHOLDER){// 如果已经存在,直接返回(避免重复创建)returnrowValue;}// 否则创建对象并放入缓存...rowValue=createResultObject(rsw,resultMap,lazyLoader,columnPrefix);if(rowKey!=null){localCache.putObject(rowKey,rowValue);}// 填充属性...returnrowValue;}
2.4ResultLoaderMap防止递归加载

对于延迟加载的属性,ResultLoaderMap会在加载前移除对应的LoadPair,因此同一属性不会重复触发加载。但对于eager加载(立即加载)的情况,ResultLoaderMap不起作用,主要依赖一级缓存。


三、循环引用解决流程(以 eager 为例)

结合上述机制,循环引用的完整解决流程如下(

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/28 3:11:25

CherryUSB轻量级协议栈在嵌入式开发中的应用

1. CherryUSB嵌入式USB协议栈概述在嵌入式系统开发中&#xff0c;USB接口因其即插即用、高带宽和供电能力等特性&#xff0c;已成为连接外设的首选方案之一。然而传统的USB协议栈往往存在资源占用大、移植困难等问题&#xff0c;特别是对于资源受限的MCU平台。CherryUSB作为一款…

作者头像 李华
网站建设 2026/4/28 3:10:32

Blender3MF插件:3分钟学会在Blender中处理3D打印3MF格式的完整指南

Blender3MF插件&#xff1a;3分钟学会在Blender中处理3D打印3MF格式的完整指南 【免费下载链接】Blender3mfFormat Blender add-on to import/export 3MF files 项目地址: https://gitcode.com/gh_mirrors/bl/Blender3mfFormat 你是否正在寻找一个能在Blender中完美处理…

作者头像 李华
网站建设 2026/4/28 3:09:25

强化学习中的奖励黑客检测:方法与挑战

1. 代码环境中的奖励黑客检测&#xff1a;现状与挑战在当今AI驱动的代码生成领域&#xff0c;强化学习&#xff08;RL&#xff09;已成为训练智能体完成编程任务的主流方法。然而&#xff0c;一个长期存在的痛点问题是"奖励黑客"&#xff08;Reward Hacking&#xff…

作者头像 李华
网站建设 2026/4/28 3:07:22

C# IDisposable 和 using

IDisposable 与 using 一、IDisposable&#xff1a;显式释放资源的契约 1. 为什么要使用IDisposable 先看一个问题&#xff1a;C# 会自动清理垃圾&#xff0c;为什么还需要手动释放&#xff1f; C# 的内存&#xff08;比如你 new 出来的对象&#xff09;确实由垃圾回收器&am…

作者头像 李华
网站建设 2026/4/28 3:05:46

灰色网络深度揭秘:暗网真实生态与安全风险全面解析

1. Hack Forums&#xff1a;不止是技术&#xff0c;更是“灰色地带”的狂欢&#xff1f; 这个2005年就成立的老牌论坛&#xff0c;与其说是“黑客技术交流”&#xff0c;不如说是网络安全灰色地带的缩影。从渗透测试到社工&#xff0c;啥都有&#xff0c;甚至还有交易区…别告…

作者头像 李华