在正常的业务开发中,我们总是免不了会碰到一对一、一对多关联查询的情况,而MyBatis提供了对这两种查询方式的支持。
比如有一个学生实体结构,学生和班级是一对一,学生和课程是一对多:
学生表student
- student_id 标识
- student_name 名字
- class_id 班级标识,关联到class表
班级表class
- class_id 标识
- class_name 名称
课程表course
- course_id 标识
- course_name 名称
- grade 期末成绩
- student_id 关联到学生,一个学生有多个课程
现在我想查询名为小明的学生,我只会查到一个数据,同时我想查到小明所在的班级实体,以及所有的课程实体。
一、为什么mybatis关联查询优于分开查询
我们当然可以分开查询,但是需要考虑如下因素:
- 数据库交互次数:分开查询可能造成多次数据库交互,而使用mybatis查询只需一次交互;
- 数据一致性:如果分开查询,查询过程中,查询玩学生、班级后,课程表发生了变化,可能导致数据不一致,mybatis可以保证数据一致性;
- 代码简洁性:一条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
}
]
}
}