当前位置: 首页 > news >正文

【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 为例)

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

http://www.jsqmd.com/news/711845/

相关文章:

  • 轻松掌握Windows和Office激活:新手也能上手的完整指南
  • 毕设选题避坑:这 5 类题目千万不要选,谁选谁挂
  • 终极指南:GHelper手动风扇控制如何让你的ROG笔记本实现静音与性能完美平衡
  • 告别漏报!Log4j2Scan插件v0.13的延迟检测与缓存机制详解
  • 嵌入式C实时采集系统崩溃日志解密:解析HardFault_Handler中隐藏的栈溢出+浮点异常+未对齐访问三重叠加故障(含GDB脚本)
  • codedb:专为AI智能体设计的亚毫秒级代码智能索引服务器
  • ARM GICv3虚拟中断控制器优先级分组机制详解
  • 自动驾驶视频生成模型评估框架DrivingGen解析
  • 任务栏图标显示异常
  • 2026AI大模型API加速平台真实测评:深度剖析5大靠谱平台,为开发者精准避坑
  • ARMv8内存管理:TCR_EL1寄存器详解与优化实践
  • LLM在网页设计中的智能应用与优化实践
  • 2025届学术党必备的十大降AI率工具推荐榜单
  • 告别网盘限速:八大平台直链解析工具完全指南
  • 实时光线追踪技术解析与实践指南
  • 从U盘到CAN:汽车ECU升级的“幕后英雄”与安全门道(以AUTOSAR为例)
  • 提升开发效率:Xcode 必备技巧与实用教程
  • 番茄小说下载器:离线阅读的完美解决方案
  • DROID-SLAM:动态环境中的实时RGB SLAM技术解析
  • (一区top顶级trans期刊,TIE复现)面向执行器饱和和故障情况的航天器姿态机动的主动容错控制系统,基于状态观测器故障检测、反步控制+自适应滑模主动容错控制(Matlab代码实现)
  • Blender3MF插件:3分钟学会在Blender中处理3D打印3MF格式的完整指南
  • 终极指南:在Linux系统中高效配置foo2zjs打印机驱动
  • Kotlin 2.4.0-Beta2 发布,语法与多平台能力全线革新
  • 【2026 Gartner认证沙箱架构】:为什么头部AI公司已弃用Kubernetes原生Pod隔离,全面转向轻量Docker Sandbox+eBPF Policy Engine?
  • 位点特异性抗体定制如何实现精准偶联?
  • 2026岳阳到长沙商务车公司选型推荐:核心维度拆解 - 优质品牌商家
  • 从零构建大语言模型:手把手实现Transformer核心组件与训练流程
  • 眼科医生和工程师都该懂点:SS-OCT如何从眼底扫描中‘看’到视网膜分层?
  • ThinkPad黑苹果终极实战指南:让T480变身为macOS工作站的完整解决方案
  • AMD Ryzen处理器终极调试指南:SMUDebugTool让你的硬件性能飞起来