myabtis 一对多实现原理(自连接)

news/2025/1/5 7:22:00/文章来源:https://www.cnblogs.com/zyb-luckey/p/18648851

在 MyBatis 中进行 一对多映射 时,确实需要通过 resultMap 来映射父对象和子对象的关系。你提到的“为了避免父对象重复插入”,实际上是指 如何避免在查询结果中父对象重复创建。为了实现这一点,MyBatis 提供了 collection 标签,可以在映射父对象时,把多个子对象聚合到一个父对象中,从而避免重复创建父对象实例。

1. 场景和数据模型:

假设有如下数据模型:

admin_menu 表:

midmenunamepid
1 菜单管理 0
2 菜单管理 1
3 菜单管理 1
4 用户管理 0
  • mid 是菜单的唯一标识。
  • pid 是父菜单的标识,pid = 0 表示该菜单没有父菜单。

2. 目标:

我们希望通过 MyBatis 映射以下一对多关系:

  • 每个父菜单可以有多个子菜单。
  • 子菜单的 pid 列指向父菜单的 mid 列。
  • 查询时将父菜单和其所有子菜单一起返回。

3. SQL 查询:

首先,我们需要编写一个 SQL 查询,通过联表查询获取父菜单和子菜单。

sql
SELECT am1.mid AS parent_mid, am1.menuname AS parent_menuname, am2.mid AS sub_mid, am2.menuname AS sub_menuname FROM admin_menu am1 LEFT JOIN admin_menu am2 ON am1.mid = am2.pid WHERE am1.menuname = '菜单管理';

4. MyBatis 映射:

我们可以使用 resultMap 来定义如何将查询结果映射到 Java 对象。首先,我们定义父对象和子对象的 resultMap

4.1 定义 Menu

假设我们有一个 Menu 类表示菜单。

java
public class Menu { private Integer mid; private String menuname; private List<Menu> subMenus; // 子菜单列表 // Getters and Setters public Integer getMid() { return mid; } public void setMid(Integer mid) { this.mid = mid; } public String getMenuname() { return menuname; } public void setMenuname(String menuname) { this.menuname = menuname; } public List<Menu> getSubMenus() { return subMenus; } public void setSubMenus(List<Menu> subMenus) { this.subMenus = subMenus; } }

4.2 定义 resultMap

接下来,在 MyBatis 的 XML 映射文件中,我们定义 resultMap 来处理父对象和子对象的映射。

xml
<resultMap id="MenuMap" type="com.example.Menu"> <!-- 映射父对象字段 --> <id property="mid" column="parent_mid"/> <result property="menuname" column="parent_menuname"/> <!-- 映射子菜单集合 --> <collection property="subMenus" ofType="com.example.Menu"> <id property="mid" column="sub_mid"/> <result property="menuname" column="sub_menuname"/> </collection> </resultMap> <select id="selectMenuWithSubMenu" resultMap="MenuMap"> SELECT am1.mid AS parent_mid, am1.menuname AS parent_menuname, am2.mid AS sub_mid, am2.menuname AS sub_menuname FROM admin_menu am1 LEFT JOIN admin_menu am2 ON am1.mid = am2.pid WHERE am1.menuname = '菜单管理'; </select>

4.3 解释

  • id:将查询结果中的 parent_mid 映射到父对象的 mid 属性,parent_menuname 映射到 menuname
  • collection:表示一对多的关系,subMenus 属性是一个 List<Menu>,将查询结果中的 sub_midsub_menuname 映射到子菜单对象的属性。
  • ofType="com.example.Menu":指定子菜单的类型。

5. 查询结果

假设执行查询后,我们得到如下的 SQL 查询结果:

parent_midparent_menunamesub_midsub_menuname
1 菜单管理 2 菜单管理
1 菜单管理 3 菜单管理

6. MyBatis 如何处理结果

MyBatis 会将查询结果映射为 Java 对象,结果结构如下:

java
Menu parentMenu = new Menu(); parentMenu.setMid(1); parentMenu.setMenuname("菜单管理"); List<Menu> subMenus = new ArrayList<>(); Menu subMenu1 = new Menu(); subMenu1.setMid(2); subMenu1.setMenuname("菜单管理"); subMenus.add(subMenu1); Menu subMenu2 = new Menu(); subMenu2.setMid(3); subMenu2.setMenuname("菜单管理"); subMenus.add(subMenu2); parentMenu.setSubMenus(subMenus);

7. 避免父对象重复

  • MyBatis 会合并重复的父对象,即使 SQL 查询的结果集有重复的父对象,MyBatis 会根据父对象的 mid 字段,自动将多个相同的父对象合并成一个父对象,并将所有的子菜单放到父对象的 subMenus 集合中。
  • 这意味着,即使 SQL 查询返回了两行包含相同父对象的记录,MyBatis 也不会重复创建父对象实例,而是将所有子菜单添加到同一个父菜单对象的 subMenus 集合中。

8. 总结:

在 MyBatis 中,一对多映射是通过 collection 标签实现的,collection 会将 SQL 查询结果中的子对象聚合到父对象的集合属性中。MyBatis 会根据查询结果中的父对象 mid 来判断是否已经创建过父对象实例,从而避免重复创建。这样,多个子对象会共享一个父对象实例,避免了父对象的重复插入。

通过这种方式,MyBatis 可以有效地处理一对多关系,并且确保不会因为 SQL 查询的重复记录而重复创建父对象。

 
 
您说:
MyBatis 会合并重复的父对象,即使 SQL 查询的结果集有重复的父对象,MyBatis 会根据父对象的 mid 字段,自动将多个相同的父对象合并成一个父对象,并将所有的子菜单放到父对象的 subMenus 集合中。 这意味着,即使 SQL 查询返回了两行包含相同父对象的记录,MyBatis 也不会重复创建父对象实例,而是将所有子菜单添加到同一个父菜单对象的 subMenus 集合中。 这个怎么具体实现的mybatis机制
ChatGPT 说:
ChatGPT

MyBatis 实现 一对多关系映射 时,能够避免父对象的重复实例化并将子对象合并到父对象的集合属性中的机制,主要依赖于 MyBatis 的 ResultMap 以及 collection 标签的特性。具体来说,MyBatis 会利用 一级缓存对象的标识 来判断父对象是否已创建,从而避免重复创建父对象实例。

1. MyBatis 的一级缓存与合并机制

MyBatis 的一级缓存默认是开启的,这意味着在一个 SqlSession 中,同一条父对象会缓存到一级缓存中。当查询返回多个包含相同父对象的记录时,MyBatis 会检查一级缓存中是否已经存在这个父对象实例,如果存在,则直接复用该实例,而不是创建新的父对象。

1.1 缓存原理

  • 一级缓存:MyBatis 的一级缓存是基于 SqlSession 的,即在一个 SqlSession 的生命周期内,如果多次查询相同的对象,MyBatis 会使用缓存的对象,而不会每次都查询数据库。

    • 查找父对象时,MyBatis 会先检查缓存中是否已有这个父对象。如果有,直接返回缓存中的父对象实例。
    • 如果父对象没有被缓存(即第一次查询时),MyBatis 会从数据库查询该对象,并将其加入缓存。
  • 缓存与 collection 标签结合

    • 当查询结果返回多个父对象时,MyBatis 会利用 mid(或其他唯一标识)来判断该父对象是否已经存在于缓存中。
    • 如果已经存在,MyBatis 会直接将新的子对象添加到父对象的 subMenus 集合中,而不会创建新的父对象实例。

1.2 Example

考虑下面的查询:

sql
SELECT am1.mid AS parent_mid, am1.menuname AS parent_menuname, am2.mid AS sub_mid, am2.menuname AS sub_menuname FROM admin_menu am1 LEFT JOIN admin_menu am2 ON am1.mid = am2.pid WHERE am1.menuname = '菜单管理';

假设查询结果如下:

parent_midparent_menunamesub_midsub_menuname
1 菜单管理 2 菜单管理
1 菜单管理 3 菜单管理

在这种情况下,即使 SQL 查询返回了两行包含相同父对象 parent_mid = 1 的记录,MyBatis 会执行以下步骤:

2. 步骤解析:

  1. 第一次查询:
    • MyBatis 查询到第 1 行记录,parent_mid = 1parent_menuname = 菜单管理
    • MyBatis 创建一个新的父对象 Menu,并将其 mid 设置为 1,menuname 设置为 "菜单管理"。
    • 然后,MyBatis 查询到第 1 行的子对象 sub_mid = 2sub_menuname = 菜单管理,并将子对象添加到父对象的 subMenus 集合中。
  2. 第二次查询(重复父对象):
    • MyBatis 查询到第 2 行记录,parent_mid = 1parent_menuname = 菜单管理
    • MyBatis 会检测到父对象 mid = 1 已经存在于缓存中(一级缓存),因此不会再次创建新的父对象。
    • MyBatis 会将新的子对象 sub_mid = 3sub_menuname = 菜单管理 添加到已经存在的父对象的 subMenus 集合中。
  3. 最终结果:
    • 最终,MyBatis 会返回一个 Menu 对象,mid = 1menuname = 菜单管理subMenus 集合中包含两个子菜单对象,sub_mid = 2sub_mid = 3

3. ResultMapcollection 的实现原理:

MyBatis 会通过 resultMapcollection 来处理这些对象的映射。collection 标签会告诉 MyBatis 把查询结果中的多个子对象聚合成父对象的一个集合属性。具体实现原理如下:

3.1 父对象映射

xml
<resultMap id="MenuMap" type="com.example.Menu"> <id property="mid" column="parent_mid"/> <result property="menuname" column="parent_menuname"/> </resultMap>
  • idresult 标签会分别映射父对象的标识符和其他属性。

3.2 子对象映射

xml
<collection property="subMenus" ofType="com.example.Menu"> <id property="mid" column="sub_mid"/> <result property="menuname" column="sub_menuname"/> </collection>
  • collection 标签会把多个子对象通过 subMenus 属性聚合到父对象中,并且通过 ofType 指定子对象的类型(如 Menu)。

3.3 合并逻辑:

  • 在执行查询时,MyBatis 会逐行读取 SQL 查询的结果,并根据 resultMap 来映射到对象。如果查询结果中的父对象字段相同,MyBatis 会通过 mid 判断是否是同一个父对象。如果是同一个父对象,就不会创建新的父对象实例,而是直接将当前行的子对象添加到父对象的 subMenus 集合中。

4. 避免父对象重复实例化的关键:

  • 通过唯一标识符(mid)来合并父对象:每次查询时,MyBatis 都会检查当前父对象的唯一标识符(如 mid)。如果当前父对象已在缓存中,MyBatis 会直接复用缓存中的对象实例。

  • 一级缓存的作用:一级缓存确保了在一次 SqlSession 的生命周期内,重复查询同一个父对象时只会创建一次父对象实例。

5. MyBatis 处理一对多关系的总结:

  1. MyBatis 会根据 resultMapcollection 标签将查询结果映射为 Java 对象。
  2. 当查询结果中有重复的父对象时,MyBatis 会通过父对象的唯一标识符(如 mid)来避免重复创建父对象实例。
  3. 子对象会被添加到父对象的集合属性(如 subMenus)中,从而实现一对多关系的映射。
  4. 一级缓存 是避免重复创建父对象的关键机制,它在同一个 SqlSession 中缓存了查询到的父对象,避免了重复创建。

通过这种机制,MyBatis 能够高效地处理一对多的映射关系,并且在多次查询同一个父对象时避免重复创建实例。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/863115.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《docker基础篇:8.Docker常规安装简介》包括:docker常规安装总体步骤、安装tomcat、安装mysql、安装redis

《docker基础篇:8.Docker常规安装简介》包括:docker常规安装总体步骤、安装tomcat、安装mysql、安装redis@目录8.Docker常规安装简介8.1 docker常规安装总体步骤8.2安装tomcat8.3 安装mysql8.3.1 docker hub上面查找mysql镜像8.3.2 从docker hub上(阿里云加速器)拉取mysql镜像…

Elasticsearch VS Easysearch 性能测试

压测环境 虚拟机配置 使用阿里云上规格:ecs.u1-c1m4.4xlarge,PL2: 单盘 IOPS 性能上限 10 万 (适用的云盘容量范围:461GiB - 64TiB)vCPU 内存 (GiB) 磁盘(GB) 带宽(Gbit/s) 数量16 64 500 5000 24Easysearch 配置 7 节点集群,版本:1.9.0实例名 内网 IP 软件 vCPU JVM 磁…

win10/win11 用 ncpa.cpl 命令快速打开网络连接

前言:Win11系统配置网络适配器好费劲的,每次都要在设置找半天 得,直接来,快捷键安排1、开始 -> 运行 Win + R 弹出 运行 窗口2、输入命令 ncpa.pcl并回车 3、见证奇迹QQ:1061767621 Q群:215481318

基于爬山法MPPT最大功率跟踪算法的光伏发电系统simulink建模与仿真

1.课题概述 基于爬山法MPPT最大功率跟踪算法的光伏发电系统simulink建模与仿真。2.系统仿真结果3.核心程序与模型 版本:MATLAB2022a 4.系统原理简介最大功率点跟踪(Maximum Power Point Tracking, MPPT)是光伏发电系统中至关重要的技术,用于确保光伏电池在其工作条件下输出最…

网络_网络分层模型和应用协议

本文主要介绍了网络的分层模型和应用层的协议,分层模型有四层、七层、五层这几种模型,应用层协议主要涉及 URL 和 HTTP,并且介绍了请求和响应以及他们的行、头、体网络分层模型和应用协议 分层模型 为了解决复杂问题往往分层 经过不断的演化,网络最终形成了五层模型:MAC像…

1.2 可压缩流:激波和膨胀扇

1.2 可压缩流:激波和膨胀扇 前言 欢迎观看《Bang Dream! Ave mujica》,一部超好看的少女乐队动漫,从2025.1.2开始,每周四晚10点更新。 哇嘎利马斯 大量玩梗注意 AA笔记主要参考刘永学主编《空气动力学》,讲的物理概念很清晰易懂,推荐给大家。 扰动的传播 接下来我们讨论扰…

使用ClosedXML实现Excel导入导出

使用ClosedXML实现Excel导入导出 写在开头 游览Dotnet 基金会中的项目时,发现了这个库,Github 链接,它的性能非常好,详细见下图话不多说,直接上代码吧! 导出 后端: public static byte[] Output<T>(List<T> data, string sheetName) {using var workbook = …

离线环境一步部署OCR文字识别程序

前言 百度、阿里等的OCR接口需要联网环境,并且超过免费/试用次数后需要付费。一般政务项目因为信息安全要求都部署在独立内网,有没有离线免费的OCR实现方案?下文基于EasyOCR实现一步部署,可下载直接使用。 EasyOCR EasyOCR支持离线部署,可免费使用(支持Apache-2.0 licens…

昆明理工大学25届MBA复试资料

昆明理工大学25届MBA复试资料介绍 昆明理工大学MBA工商管理考研复试群:679724235 作者:唐维康 QQ1352517362 包含内容 全部内容如下:01、往年面试真题分为了专业知识问答、英语口语、社会类问题、综合面试问题四类,为去年上岸的面试题。02、专业英语词汇05、MBA复试1V1辅导课…

Alexander ——2024年报

Alexander ——2024年报Alexander ——2024年报 小总结 知己知彼,百战不殆。2024下半年找到了自己学习的方向,也认识很多的师傅,深入领域学习,无时不刻不在CTF的路上,逐渐建立一个完整的知识库体系。 今年的成长变化与进步 所学习的知识 Misc大部分编码体制皮亚诺夫曲线,希伯…

2025 多校冲刺省选模拟赛 1

第一次!输输输!2025 多校冲刺省选模拟赛 1 切割蛋糕(cake) 签到题 本质上是求 \(a\) 序列最小满足所有前缀平均值均大于全局平均值的循环位移,考虑 Raney 引理,找到斜率 \(\dfrac{s}{n}\) 所经过截距最小的点,易知没有无解情况。 时间复杂度 \(O(n)\)。 游乐园(park) …

IntelliJ IDEA 2024 安装激活详细使用教程(激活至2026,实测是永久,亲测!)

开发工具推荐:IntelliJ IDEA 2024 安装激活详细使用教程(激活至2026,实际上永久,亲测!)申明:本教程 IntelliJ IDEA补丁、激活码均收集于网络,请勿商用,仅供个人学习使用,如有侵权,请联系作者删除。若条件允许,希望大家购买正版 !卸载老版本 IDEA 首先,如果小伙伴的…