MyBatis ResultMap高级映射:多对一与一对多处理实战

沿途有光
沿途有光
发布时间:2026-10-15 | 3人浏览过
原创

在MyBatis开发中,当数据库表之间存在外键关联或业务实体存在包含关系时,简单的resultType无法满足需求。本文将详细讲解如何利用ResultMap的association和collection标签,结合嵌套查询(select)与结果嵌套(join),精准处理多对一与一对多的复杂对象映射问题。

准备条件:实体类设计

为了演示关联映射,我们需要定义主从表的实体类。以下以“学生(Student)”与“教师(Teacher)”为例:

  • 多对一场景:多个学生属于一个老师。学生在Bean中持有Teacher对象。
  • 一对多场景:一个老师教多个学生。老师在Bean中持有List<Student>集合。

第1步:定义基础实体模型

首先定义Teacher实体类:

public class Teacher {
    private int id;
    private String name;
}

再定义Student实体类,根据关联方向不同,其内部结构会有所差异(见后续步骤)。

多对一关系处理(学生关联老师)

场景:Student表中有一个tid字段关联Teacher表。目标是将查询出的Student对象完整包含对应的Teacher信息。

第2步:方法一——按照查询嵌套(Nested Select)

目的:先查主表(学生),再通过子查询查从表(老师)。

入口Mapper接口与XML配置。

动作:

  1. 定义获取老师的单独查询方法:@Select("select name from teacher where id = #{tid}")
  2. 在Student的ResultMap中使用<association>标签。
    参数说明:
    • property="teacher":实体类中的属性名。
    • column="tid":主表查询结果中用于传递的参数列。
    • select="getTeacherById":指向第二步中定义的查询ID。
<resultMap id="studentTeacher" type="student">
  <result column="id" property="id"/>
  <result column="name" property="name"/>
  <!-- 复杂映射:关联对象 -->
  <association property="teacher" column="tid" javaType="teacher" select="getTeacherById"/>
</resultMap>

执行逻辑与现象:
MyBatis会先执行select * from student获取所有学生记录。随后,针对返回的每一条学生记录,根据其tid再次执行查询老师的SQL。日志显示会出现多条Prepare/Execute语句,这是典型的N+1查询模式。

第3步:方法二——按照结果嵌套(Nested Results / Join)

目的:通过一条SQL语句完成联查,在内存中组装对象树。

入口Mapper XML。

动作:
1. 编写Join SQL,注意为列起别名以避免冲突(如s.id sid)。

select s.id sid, s.name sname, t.id tid, t.name tname 
from student s 
join teacher t on s.tid = t.id;

2. 在ResultMap中显式配置每个字段的映射关系,包括嵌套对象的属性:

<resultMap id="studentTeacher2" type="student">
  <result column="sid" property="id"/>
  <result column="sname" property="name"/>
  <association property="teacher" javaType="teacher">
    <result column="tid" property="id"/>
    <result column="tname" property="name"/>
  </association>
</resultMap>

完成判断:执行后仅产生一条SQL日志,返回的Student对象中Teacher属性已正确填充,无额外数据库交互。

一对多关系处理(老师关联学生)

场景:Teacher实体类中包含List<Student> students集合,需查询指定老师及其下属所有学生。

第4步:方法一——按照结果嵌套

目的:单SQL联查,将多行学生数据合并到老师对象的List中。

动作:
1. 修改Teacher实体,添加private List<Student> student;。 2. 使用<collection>标签替代<association>。
关键区别:ofType="student"指定集合泛型类型,而非javaType。

<resultMap id="TeaStu" type="Teacher">
  <result column="tpid" property="id"/>
  <result column="tname" property="name"/>
  <!-- collection处理集合属性 -->
  <collection property="students" ofType="student">
    <result column="sid" property="id"/>
    <result column="sname" property="name"/>
    <result column="tid" property="tid"/>
  </collection>
</resultMap>

第5步:方法二——按照查询嵌套

目的:分步查询,先生成老师信息,再查该老师下的学生列表。

动作:
1. 定义查询学生列表的方法:select id,name,tid from student where tid = #{tid}。

<resultMap id="TeaStu2" type="teacher">
  <result column="id" property="id"/>
  <result column="name" property="name"/>
  <collection property="students" 
              javaType="ArrayList" 
              ofType="student" 
              select="getStuList" 
              column="id"/>
</resultMap>

参数说明:
column="id"表示将当前Teacher对象的id作为参数传递给getStuList查询。

排错与优化建议

常见问题排查:
如果在“按照结果嵌套”模式下,发现对象内的非基本类型属性为null或未初始化,请检查是否漏写了<result>映射。MyBatis不会自动映射嵌套对象内部的字段,必须显式声明每一层属性的映射关系。

性能考量:
“按照查询嵌套”虽然SQL结构简单,但容易产生N+1次查询,适合数据量小或需要分批加载的场景;“按照结果嵌套”依赖JOIN语句,单次IO开销大且代码耦合度高,但在多数复杂报表和全量展示场景中效率更优。若追求高性能,可考虑启用MyBatis的全局lazyLoadingEnabled开启懒加载机制。

简要总结

掌握<association>和<collection>的使用是应对复杂ORM映射的关键。在实际项目中,建议优先评估数据访问频率,选择合适的嵌套策略。以上就是MyBatis ResultMap高级映射解决属性问题的详细内容,更多关于MyBatis高级映射ResultMap内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表