MyBatis的级联查询(一对一、一对多、多对多)

ops/2025/3/12 8:35:00/

MyBatis的级联查询

级联的优点是获取关联数据十分便捷。但是级联过多会增加系统的复杂度,同时降低系统的性能,此增彼减。所以记录超过 3 层时,就不要考虑使用级联了,因为这样会造成多个对象的关联,导致系统的耦合、负载和难以维护。
标签说明:

  • property:指定映射到实体类的对象属性。
  • column:指定表中对应的字段(即查询返回的列名)。
  • javaType:指定映射到实体对象属性的类型。
  • select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。
    MyBatis的级联查询(一对一、一对多、多对多)主要通过ResultMap<association><collection>标签实现。

1.一对一关系

场景:如订单(Order)与用户(User)、身份证与公民,一个订单仅对应一个用户。

1. 建表原则

  • 主键对应:两表主键一致,直接通过主键关联。
  • 外键唯一约束:在任意一方添加外键并设置唯一约束(如user_id字段)

2. 实现方式

  • 分步查询:分两次查询,先查主表,再通过外键查询关联表。

    <resultMap id="OrderResult" type="Order"><id property="id" column="id"/><association property="user" column="user_id" select="findUserById"/>
    </resultMap>
    

    需定义findUserById的查询语句

  • 嵌套结果映射:单次联表查询,通过<association>映射关联对象。

    <resultMap id="OrderWithUserResult" type="Order"><id property="id" column="id"/><association property="user" javaType="User"><id property="id" column="user_id"/><result property="username" column="username"/></association>
    </resultMap>
    

    SQL需使用JOIN联表查询


2.一对多关系

场景:如班级(Clazz)与学生(Student),一个班级对应多个学生。

1. 建表原则

  • 在多的一方(学生表)添加外键指向一的一方(班级表主键)

2. 实现方式

  • 嵌套查询:分两次查询,先查主表,再通过外键查关联表集合。

    <resultMap id="ClazzResult" type="Clazz"><collection property="students" column="id" select="findStudentsByClazzId"/>
    </resultMap>
    
  • 嵌套结果映射:单次联表查询,通过<collection>映射集合。

    <resultMap id="ClazzWithStudentsResult" type="Clazz"><id property="id" column="id"/><collection property="students" ofType="Student"><id property="id" column="student_id"/><result property="name" column="student_name"/></collection>
    </resultMap>
    

    SQL需使用LEFT JOIN并关联外键

3.多对多关系

场景:如用户(User)与角色(Role),一个用户可拥有多个角色,一个角色可分配给多个用户。

1. 建表原则

  • 通过中间表关联两表主键(如user_role表)

2. 实现方式

  • 嵌套结果映射:通过两次JOIN联表查询,将中间表与主表关联。

    <resultMap id="RoleWithUsersResult" type="Role"><id property="roleId" column="rid"/><collection property="users" ofType="User"><id property="id" column="user_id"/><result property="username" column="username"/></collection>
    </resultMap>
    

    SQL示例:

    SQLSELECT r.*, u.* FROM role r 
    LEFT JOIN user_role ur ON r.id = ur.rid 
    LEFT JOIN user u ON ur.uid = u.id
    

4.核心配置标签对比

关系类型标签用途关联对象类型
一对一<association>映射单个关联对象javaType
一对多<collection>映射对象集合ofType
多对多<collection>通过中间表映射双向一对多关系ofType + 联表查询

5.优化建议

  1. 延迟加载:在嵌套查询中启用lazyLoadingEnabled,避免一次性加载所有关联数据。
  2. 动态SQL:通过<if><foreach>等标签处理复杂条件
  3. 联表查询 vs 嵌套查询:联表查询减少数据库交互次数,但可能返回冗余数据;嵌套查询更灵活但可能引发N+1问题

6.一对多和多对一的使用异同总结

特性一对多多对一
数据库关系主表(一)的一条记录对应从表(多)的多条记录从表(多)的外键指向主表(一)的主键
映射配置在“一”端使用 <collection> 标签映射集合属性在“多”端使用 <many-to-one> 标签映射单对象属性
外键位置外键位于“多”的表,指向“一”的主键与一对多相同,但视角相反(从“多”向“一”看)
查询方向通常从“一”端查询关联的“多”端数据(如班级查询学生)通常从“多”端查询关联的“一”端数据(如学生查询班级)
数据维护插入数据时需先维护“一”端,删除时需先删除“多”端操作顺序与一对多一致,但逻辑上从“多”端操作
应用场景适合主表与从表的层级关系(如部门与员工)适合从表需要频繁引用主表的场景(如订单与客户)

关键区别

  • 视角差异:一对多是从“一”端指向“多”端,而多对一是从“多”端指向“一”端,本质是同一关系的双向表达
  • 映射标签:在ORM框架(如Hibernate)中,一对多使用<collection>多对一使用<many-to-one>
  • 数据操作逻辑:一对多的查询可能涉及集合遍历(如班级所有学生),而多对一更关注单对象关联(如学生所属班级)

7.使用多对多时的核心要点

  1. 中间表设计

    • 必须通过中间表(如Student_Courses)维护多对多关系,存储两表主键作为外键
    • 若需记录额外属性(如选课时间),需扩展中间表为独立实体(如Enrollment)
  2. 查询优化

    • 使用JOIN连接三张表,避免多次单表查询导致的性能问题
    • 分页处理大数据量时,优先在中间表或主表过滤以减少笛卡尔积爆炸
  3. 结果映射

    • 在ORM框架中通过<collection>标签嵌套映射关联数据,如学生对象包含课程集合
    • 双向关联时需避免循环引用(如学生引用课程,课程又引用学生),可通过DTO解耦或注解忽略
  4. 数据一致性

    • 插入时需先操作主表(学生、课程),再操作中间表;删除时需先解除中间表关联

    • 使用事务保证关联操作的原子性,避免部分失败导致脏数据

  5. 性能与冗余

    • 添加索引优化中间表的外键字段,加速查询
    • 避免在中间表中存储非关联数据,保持表的轻量化

http://www.ppmy.cn/ops/165154.html

相关文章

numpy常用函数详解

在深度神经网络代码中经常用到numpy库的一些函数&#xff0c;很多看过之后很容易忘记&#xff0c;本文对经常使用的函数进行归纳总结。 np.arange arange是numpy一个常用的函数&#xff0c;该函数主要用于创建等差数列。它的使用方法如下所示&#xff1a; numpy.arange([star…

llama.cpp编译

llam.cpp编译 1. 下载&编译 git clone https://github.com/ggml-org/llama.cpp cmake -S . -B build2. 下载模型验证 # 下载地址 https://huggingface.co/filipealmeida/open-llama-7b-v2-open-instruct-GGUF/blob/main/ggml-model-Q4_0.gguf# 验证 ./llama-cli.exe -m …

Python第十六课:深度学习入门 | 神经网络解密

🎯 本节目标 理解生物神经元与人工神经网络的映射关系掌握激活函数与损失函数的核心作用使用Keras构建手写数字识别模型可视化神经网络的训练过程掌握防止过拟合的基础策略一、神经网络基础(大脑的数字化仿生) 1. 神经元对比 生物神经元人工神经元树突接收信号输入层接收特…

使用DeepSeek+蓝耘快速设计网页简易版《我的世界》小游戏

前言&#xff1a;如今&#xff0c;借助先进的人工智能模型与便捷的云平台&#xff0c;即便是新手开发者&#xff0c;也能开启创意游戏的设计之旅。DeepSeek 作为前沿的人工智能模型&#xff0c;具备强大的功能与潜力&#xff0c;而蓝耘智算云平台则为其提供了稳定高效的运行环境…

python学习第三天

条件判断 条件判断使用if、elif和else关键字。它们用于根据条件执行不同的代码块。 # 条件判断 age 18 if age < 18:print("你还是个孩子&#xff01;") elif age 18:print("永远十八岁&#xff01;") else:print("你还年轻&#xff01;")…

【2025】基于springboot+uniapp的乡村旅游小程序系统统(源码、万字文档、图文修改、调试答疑)农家乐预约

乡村旅游小程序系统通过 Spring Boot 与 uniapp 技术栈的深度整合&#xff0c;为乡村旅游产业打造了一个功能全面、交互流畅、性能稳定的综合服务平台。系统根据不同角色&#xff08;管理员、商家、用户&#xff09;的业务需求&#xff0c;提供了针对性的功能模块&#xff0c;实…

requests中post中data=None, json=None两个参数区别

在 requests.post() 方法中&#xff0c;data 和 json 主要用于发送请求体&#xff0c;但它们的作用和格式有所不同。 1. data 参数 用途&#xff1a;用于发送表单数据或原始二进制数据。格式&#xff1a; 可以是 字典&#xff08;dict&#xff09;&#xff08;默认会编码为 a…

python-leetcode-反转字符串中的元音字母

345. 反转字符串中的元音字母 - 力扣&#xff08;LeetCode&#xff09; 使用双指针的方法高效地反转字符串中的元音字母。以下是 Python 代码实现&#xff1a; def reverseVowels(s: str) -> str:vowels set("aeiouAEIOU")s list(s) # 将字符串转换为列表&…