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
          }
        ]
    }
}
相关推荐
热河暖男3 小时前
【实战解决方案】Spring Boot+Redisson构建高并发Excel导出服务,彻底解决系统阻塞难题
spring boot·后端·excel
noravinsc7 小时前
redis是内存级缓存吗
后端·python·django
noravinsc9 小时前
django中用 InforSuite RDS 替代memcache
后端·python·django
喝醉的小喵9 小时前
【mysql】并发 Insert 的死锁问题 第二弹
数据库·后端·mysql·死锁
kaixin_learn_qt_ing10 小时前
Golang
开发语言·后端·golang
炒空心菜菜11 小时前
MapReduce 实现 WordCount
java·开发语言·ide·后端·spark·eclipse·mapreduce
芯眼11 小时前
STM32启动文件详解(重点)
java·开发语言·c++·stm32·单片机·mybatis
wowocpp13 小时前
spring boot Controller 和 RestController 的区别
java·spring boot·后端
后青春期的诗go13 小时前
基于Rust语言的Rocket框架和Sqlx库开发WebAPI项目记录(二)
开发语言·后端·rust·rocket框架
freellf13 小时前
go语言学习进阶
后端·学习·golang