MyBatis 中多对一与一对多的数据处理详解

巷口听风
巷口听风
发布时间:2026-10-15 | 2人浏览过
原创

在开发基于 MyBatis 的项目时,经常需要处理表之间的关联关系。本文将详细介绍如何通过 resultMap 标签中的 association 和 collection 元素,实现多对一(学生与老师)和一对多(老师与学生)数据的正确处理,并提供具体的 SQL 定义、Java 实体类示例及测试结果。

多对一的映射处理

在多对一关系中,多个实体指向同一个父实体。以“多个学生对应一个老师”为例,从学生的角度来看,这是一个典型的多对一关系。

第1步:数据库设计与搭建环境

首先需要建立对应的数据表。教师表用于存储老师信息,学生表包含一个外键 tid 关联到教师表。

数据库脚本如下:

CREATE TABLE `teacher` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

INSERT INTO teacher(`id`, `name`) VALUES (1, '吴老师');

CREATE TABLE `student` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  `tid` INT(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fktid` (`tid`),
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');

为了简化代码,推荐使用 Lombok 插件自动生成 getter/setter。引入 Maven 依赖并在实体类上添加 @Data 注解:

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

@Data
public class Student {
    private int id;
    private String name;
    // 多个学生可以是同一个老师,即多对一
    private Teacher teacher;
}

接下来创建对应的 Mapper 接口及 XML 配置文件(初始可为空),并注册到 MyBatis 配置文件中。

第2步:按查询嵌套处理(子查询方式)

这种方法类似于 SQL 中的子查询,先查学生,再通过外键查老师。

  1. 接口方法: 在 StudentMapper 中添加获取所有学生的方法。
// 获取所有学生及对应老师的信息
public List getStudents();
  1. MXML 配置: 使用 <resultMap> 配合 <association> 标签,利用 select 属性指定关联的查询 ID。
<select id="getStudents" resultMap="studentTeacher">
    select * from mybatis.student;
</select>

<resultMap id="studentTeacher" type="student">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <!-- association: 关联属性 -->
    <!-- javaType: 目标对象类型 -->
    <!-- column: 在多的一方的表中的列名 (这里是学生表的tid) -->
    <association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
</resultMap>

<select id="getTeacher" resultType="teacher">
    select * from mybatis.teacher where id = #{id};
</select>
  1. 测试验证:
@Test
public void getStudents(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    List<Student> students = mapper.getStudents();
    for (Student student : students) {
        System.out.println(
            "学生名:" + student.getName() +
            "\t老师:" + student.getTeacher().getName());
    }
    sqlSession.close();
}

第3步:按结果嵌套处理(联表查询方式)

这种处理方式类似于 SQL 中的联表查询(Join),一次性查出所有需要的数据,然后在内存中进行封装。

  1. 接口方法:
public List<Student> getStudents2();
  1. MXML 配置: 直接编写联表 SQL,通过不同的 column 别名区分字段,避免冲突。
<select id="getStudents2" resultMap="studentTeacher2">
    SELECT s.id sid, s.name sname, t.id tid, t.name tname
    FROM mybatis.student s, mybatis.teacher t
    WHERE t.id = s.tid
</select>

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

小结: 按照查询进行嵌套处理类似 SQL 的子查询;而按照结果进行嵌套处理类似 SQL 的联表查询。

一对多的映射处理

一对多关系通常指一个实体关联一组子实体。以“一个老师拥有多个学生”为例,对于老师来说,这就是一个一对多现象。

第1步:调整实体类结构

在学生表和老师的逻辑中翻转视角。学生类不再持有老师引用,而是老师类持有学生的集合。

@Data
public class Student {
    private int id;
    private String name;
    private int tid;
}

@Data
public class Teacher {
    private int id;
    private String name;
    // 一个老师多个学生
    private List<Student> students;
}

第2步:按结果嵌套处理

这是最直观的实现方式,通过单条 SQL 查出老师和其下的所有学生。

  1. 接口定义:
// 获取指定老师,及老师下的所有学生
public Teacher getTeacher(int id);
  1. MXML 配置: 使用 <collection> 标签来映射集合属性,注意指定 ofType 为集合内的泛型类型。
<select id="getTeacher" resultMap="TeacherStudent">
    SELECT s.id sid, s.name sname, t.id tid, t.name tname
    FROM mybatis.teacher t, mybatis.student s
    WHERE t.id = s.tid AND t.id = #{tid}
</select>

<resultMap id="TeacherStudent" type="teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <!-- collection: 映射集合 -->
    <!-- ofType: 指定映射到 list 集合属性中 pojo 的类型 -->
    <collection property="students" ofType="student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="tid" column="tid"/>
    </collection>
</resultMap>

完成映射文件的注册后,进行测试:

@Test
public void testGetTeacher(){
    SqlSession session = MybatisUtils.getSession();
    TeacherMapper mapper = session.getMapper(TeacherMapper.class);
    Teacher teacher = mapper.getTeacher(1);
    System.out.println(teacher.getName());
    System.out.println(teacher.getStudents());
}

第3步:按查询嵌套处理

同样可以使用子查询的方式来实现一对多。

<select id="getTeacher2" resultMap="TeacherStudent2">
    SELECT * FROM teacher WHERE id = #{id}
</select>

<resultMap id="TeacherStudent2" type="Teacher">
    <!-- column: 主键列名作为参数传递给子查询 -->
    <collection property="students" javaType="ArrayList" ofType="Student" 
                column="id" select="getStudentByTeacherId"/>
</resultMap>

<select id="getStudentByTeacherId" resultType="Student">
    SELECT * FROM student WHERE tid = #{id}
</select>

总结与注意事项

  • <association> 用于一对一和多对一关系;<collection> 用于一对多关系。
  • javaType 用于指定 POJO 中属性的具体类型(如单个对象);ofType 用于指定映射到集合(List/Collection)属性中 POJO 的泛型类型。
  • 编写 SQL 时应保证可读性,并尽量考虑性能(如联表查询 vs 多次子查询)。
  • 注意字段名与属性名不一致的问题,必要时在 SQL 中使用别名或在 resultMap 中手动映射。
  • 建议开启日志功能(如 Log4j),以便排查 SQL 错误或执行流程问题。

以上就是 MyBatis 中多对一和一对多数据处理的详细内容,更多关于 MyBatis 多对一一对多处理技巧的资料请关注本站其它相关文章!

本站声明

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

Python 相关产品和应用

全部下载

专题列表