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
          }
        ]
    }
}
相关推荐
2351620 小时前
【MySQL】慢查寻的发现和解决优化(思维导图版)
java·后端·sql·mysql·职场和发展·数据库开发·数据库架构
纳就这样吧20 小时前
达梦数据库保留字冲突问题总结
后端
追逐时光者20 小时前
全面的 C#/.NET 图表构建解决方案,助力快速实现图表开发需求!
后端·.net
初级程序员Kyle20 小时前
开始改变第一天 JVM的原理到调优(4)
java·后端
我是天龙_绍21 小时前
java 类 静态和非静态说明
java·后端
Pa2sw0rd丶21 小时前
Python 循环导入详解:为什么会导致生产环境崩溃及企业级解决方案
后端·python
十一点四十就是十一点半21 小时前
一起来探索TinyPro的最新Springboot的上手指南吧
后端
lang201509281 天前
Spring Boot健康检查全解析
java·spring boot·后端
我是华为OD~HR~栗栗呀1 天前
华为OD-Java面经-21届考研
java·c++·后端·python·华为od·华为·面试
考虑考虑1 天前
流收集器
java·后端·java ee