mybatis中一对一、多对多关联查询怎么实现

在正常的业务开发中,我们总是免不了会碰到一对一、一对多关联查询的情况,而MyBatis提供了对这两种查询方式的支持。

比如有一个学生实体结构,学生和班级是一对一,学生和课程是一对多:

学生表student

  • student_id 标识
  • student_name 名字
  • class_id 班级标识,关联到class表

班级表class

  • class_id 标识
  • class_name 名称

课程表course

  • course_id 标识
  • course_name 名称
  • grade 期末成绩
  • student_id 关联到学生,一个学生有多个课程

现在我想查询名为小明的学生,我只会查到一个数据,同时我想查到小明所在的班级实体,以及所有的课程实体。

一、为什么mybatis关联查询优于分开查询

我们当然可以分开查询,但是需要考虑如下因素:

  1. 数据库交互次数:分开查询可能造成多次数据库交互,而使用mybatis查询只需一次交互;
  2. 数据一致性:如果分开查询,查询过程中,查询玩学生、班级后,课程表发生了变化,可能导致数据不一致,mybatis可以保证数据一致性;
  3. 代码简洁性:一条sql即可解决问题,不必为此编写一大堆的sql和业务逻辑。

二、如何实现一对一查询

注意:之后的代码只是示例,意在说明原理,不要直接去运行,未经验证。

根据上述表结构,我们很轻松就可以写出一条sql语句:

vbnet 复制代码
select
    student_id,
    student_name,
    class.class_id,
    class_name
from student
left join class on student.class_id = class.class_id
where student_name = '小明'

然后包在mybatis的xml文件中,成为一个查询语句:

csharp 复制代码
<select id="selectStudent" resultMap="studentMap">
  select
      student_id,
      student_name,
      class.class_id,
      class_name
  from student
  left join class on student.class_id = class.class_id
  where student_name = #{student_name}
</select>    

studentMap用于完成字段映射,其中的association标签完成了一对一的实体映射:

ini 复制代码
<resultMap type="com.hello.school.domain.vo.StudentVO" id="studentMap">
      <id property="studentId" column="student_id" />
      <result property="studentName"    column="student_name"    />
      <result property="classId"    column="class_id"    />
      <association property="class" javaType="com.hello.school.domain.Class">
        <id property="classId" column="class_id" />
        <result property="className"    column="class_name"    />
      </association>
  </resultMap>

resultMap绑定的vo是这样的:

kotlin 复制代码
@Data
public class StudentVO {

    private Long studentId;
    private String studentName;
    private Long classId;
    // 班级实体:一对一关联
    private Class class;

}

我们此时查询到的数据大概是如下形式:

json 复制代码
{
    "code": 200,
    "msg": null,
    "data": {
        "studentId": 1,
        "studentName": "小明",
        "classId": 1,
        "class": {
          "classId": 1,
          "className": "六三班"
        }
    }
}

三、如何实现一对多查询

学生和课程是一对多的关系,sql语句修改为:

vbnet 复制代码
select
    student_id,
    student_name,
    class.class_id,
    class_name,
    course_id,
    course_name,
    grade
from student
left join class on student.class_id = class.class_id
left join course on student.student_id = course.student_id
where student_name = '小明'

同步到xml:

csharp 复制代码
<select id="selectStudent" resultMap="studentMap">
  select
      student_id,
      student_name,
      class.class_id,
      class_name,
      course_id,
      course_name,
      grade
  from student
  left join class on student.class_id = class.class_id
  left join course on student.student_id = course.student_id
  where student_name = #{student_name}
</select>    

studentMap用于完成字段映射,其中的collection标签完成了一对多的实体映射:

注意:主表一定要写id标签指定id,否则一对多查询会因为找不到主表查出多条数据

ini 复制代码
<resultMap type="com.hello.school.domain.vo.StudentVO" id="studentMap">
      <id property="studentId" column="student_id" />
      <result property="studentName"    column="student_name"    />
      <result property="classId"    column="class_id"    />
      <association property="class" javaType="com.hello.school.domain.Class">
        <id property="classId" column="class_id" />
        <result property="className"    column="class_name"    />
      </association>
      <collection property="courses" javaType="com.hello.school.domain.course">
        <id property="courseId" column="course_id" />
        <result property="courseName"    column="course_name"    />
        <result property="grade"    column="grade"    />
        <result property="studentId"    column="student_id"    />
      </collection>
  </resultMap>

resultMap绑定的vo是这样的:

kotlin 复制代码
@Data
public class StudentVO {

    private Long studentId;
    private String studentName;
    private Long classId;
    // 班级实体:一对一关联
    private Class class;
    // 课程列表:一对多关联
    private List<Course> courses;

}

我们此时查询到的数据大概是如下形式:

json 复制代码
{
    "code": 200,
    "msg": null,
    "data": {
        "studentId": 1,
        "studentName": "小明",
        "classId": 1,
        "class": {
          "classId": 1,
          "className": "六三班"
        },
        "courses": [
          {
            "courseId": 1,
            "courseName": "语文",
            "grade": 100,
            "studentId": 1
          },
          {
            "courseId": 2,
            "courseName": "数学",
            "grade": 60,
            "studentId": 1
          },
          {
            "courseId": 3,
            "courseName": "英语",
            "grade": 75,
            "studentId": 1
          }
        ]
    }
}
相关推荐
涡能增压发动积2 小时前
Browser-Use Agent使用初体验
人工智能·后端·python
探索java2 小时前
Spring lookup-method实现原理深度解析
java·后端·spring
lxsy2 小时前
spring-ai-alibaba 之 graph 槽点
java·后端·spring·吐槽·ai-alibaba
码事漫谈2 小时前
深入解析线程同步中WaitForSingleObject的超时问题
后端
码事漫谈2 小时前
C++多线程同步:深入理解互斥量与事件机制
后端
少女孤岛鹿3 小时前
微服务注册中心详解:Eureka vs Nacos,原理与实践 | 一站式掌握服务注册、发现与负载均衡
后端
CodeSaku3 小时前
是设计模式,我们有救了!!!(四、原型模式)
后端
Ray663 小时前
「阅读笔记」零拷贝
后端
二闹3 小时前
什么?你的 SQL 索引可能白加了!?
后端·mysql·性能优化
lichenyang4533 小时前
基于Express+Ejs实现带登录认证的多模块增删改查后台管理系统
后端