MyBatisPlus关联查询

目录
  1. 1. 一、前言
  2. 2. 二、准备工作
  3. 3. 三、一对一查询
    1. 3.1. 法一:嵌套 Select 查询
    2. 3.2. 法二:嵌套结果映射
  4. 4. 四、一对多查询
    1. 4.1. 法一:嵌套 Select 查询
    2. 4.2. 法二:嵌套结果映射
  5. 5. 五、总结

一、前言

工作中常常会碰到联表查询,最近刚刚参加工作,在写查询时发现自己很不熟练,特此总结一下一对一和一对多的查询。

在Dao层写sql语句时,我们常用的标签诸如select、update、delete、insert、where、if等等,都需要非常熟悉,我认为这也是代码的基本功吧,关于一对一和一对多,这里使用到的关键标签就是associationcollection

二、准备工作

依赖:

1
2
3
4
5
<dependency>
   <groupId>com.baomidou</groupId>
   <artifactId>mybatis-plus-boot-starter</artifactId>
   <version>3.5.2</version>
</dependency>

application配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
port: 9999
spring:
jackson:
  date-format: yyyy-MM-dd HH:mm:ss
  time-zone: GMT+8
datasource:
  driver-class-name: com.mysql.cj.jdbc.Driver
  url: jdbc:mysql://localhost:3306/class
  username: root
  password: 123

mybatis-plus:
mapper-locations: classpath*:/mappers/*Mapper.xml

数据库:

三张表:班级、老师、学生,数据若干。这里没有使用外键,在工作中其实不常使用外键,外键使用过多会造成数据库维护成本过高!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
-- ----------------------------
-- Table structure for class
-- ----------------------------
DROP TABLE IF EXISTS `class`;
CREATE TABLE `class` (
 `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '班级',
 `head_teacher_id` int(11) DEFAULT NULL COMMENT '班主任',
 PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of class
-- ----------------------------
INSERT INTO `class` VALUES ('1', '1');
INSERT INTO `class` VALUES ('2', '2');
INSERT INTO `class` VALUES ('3', '3');

-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
 `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '学生id',
 `name` varchar(255) DEFAULT NULL COMMENT '学生姓名',
 `class_id` int(11) DEFAULT NULL COMMENT '班级id',
 PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('1', '张三', '1');
INSERT INTO `student` VALUES ('2', '李四', '2');
INSERT INTO `student` VALUES ('3', '王雯雯', '1');
INSERT INTO `student` VALUES ('4', '刘文涛', '3');
INSERT INTO `student` VALUES ('5', '卢毅', '1');
INSERT INTO `student` VALUES ('6', '李晓楠', '2');
INSERT INTO `student` VALUES ('7', '陈果', '2');
INSERT INTO `student` VALUES ('8', '唐浩', '3');
INSERT INTO `student` VALUES ('9', '秦琴', '3');
INSERT INTO `student` VALUES ('10', '王梦娟', '1');

-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
 `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '老师id',
 `name` varchar(255) DEFAULT NULL COMMENT '老师名字',
 PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES ('1', '张江');
INSERT INTO `teacher` VALUES ('2', '王闻彦');
INSERT INTO `teacher` VALUES ('3', '周倩');
INSERT INTO `teacher` VALUES ('4', '李强');

实体类:

班级

1
2
3
4
5
6
7
@Data
public class Classes {
   private Integer id;
   private Integer headTeacherId;
   private Teacher headTeacher;
   private List<Student> students;
}

老师

1
2
3
4
5
@Data
public class Teacher {
   private Integer id;
   private String name;
}

学生

1
2
3
4
5
6
@Data
public class Student {
   private Integer id;
   private String name;
   private Integer classId;
}

这里要说明一下,工作中,班级类的老师和学生这两个属性是不该这样写的,实体类是和数据库中的字段一一对应的,实际工作会按照需求自行创建DTO或者VO类,这里为了省事就这就样直接写了。

三、一对一查询

目标:查出班级的同时要把老师的信息查出来

法一:嵌套 Select 查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<resultMap id="classResultMap" type="com.example.mybatisplus.entity.Classes">
   <id column="id" property="id"/>
   <result column="head_teacher_id" property="headTeacherId"/>
   <association property="headTeacher"
                column="head_teacher_id"
                javaType="com.example.mybatisplus.entity.Teacher"
                select="queryTeacherById"/>
</resultMap>
<select id="queryById" resultMap="classResultMap">
  SELECT * FROM classes WHERE id = #{id}
</select>
<select id="queryTeacherById" resultType="com.example.mybatisplus.entity.Teacher">
  SELECT * FROM teacher WHERE id = #{id}
</select>

说明:

  1. 需要将查询结果结果进行映射到实体类
  2. property属性表示一对一中被包含的对象,例如例子中的:班级中的老师对象
  3. column属性表示数据库的“外键”,例如班主任老师id
  4. javaType属性表示子查询的结果所映射的全限定类名(非必填,MyBatis会自动检测找到相应的类,但是还是建议填上)
  5. select表示子查询语句的id,例如:这里表示调用id为queryTeacherById的查询语句,其中column作为参数传入

法二:嵌套结果映射

1
2
3
4
5
6
7
8
9
10
11
<resultMap id="classResultMap" type="com.example.mybatisplus.entity.Classes">
   <id column="id" property="id"/>
   <result column="head_teacher_id" property="headTeacherId"/>
   <association property="headTeacher" javaType="com.example.mybatisplus.entity.Teacher">
       <id property="id" column="id"/>
       <result property="name" column="name"/>
   </association>
</resultMap>
<select id="queryById" resultMap="classResultMap">
  SELECT * FROM classes c LEFT JOIN teacher t ON c.head_teacher_id = t.id WHERE c.id = #{id}
</select>

说明:

  1. 在结果映射中将结果一一映射,在sql语句中使用联表查询
  2. association中的column属性就可以省略不写了
  3. property要和column对应,如果column取了别名要写别名

结果展示:

四、一对多查询

目标:查出班级的同时要把所有学生的信息查出来

法一:嵌套 Select 查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<resultMap id="classResultMap" type="com.example.mybatisplus.entity.Classes">
   <id column="id" property="id"/>
   <result column="head_teacher_id" property="headTeacherId"/>

   <association property="headTeacher" column="id"
                javaType="com.example.mybatisplus.entity.Teacher"
                select="queryTeacherById"/>

   <collection property="students"
               column="id"
               ofType="com.example.mybatisplus.entity.Student"
               select="queryStudentByClassId"/>
</resultMap>
<select id="queryStudentByClassId" resultType="com.example.mybatisplus.entity.Student">
  SELECT * FROM student WHERE class_id = #{id}
</select>
<select id="queryById" resultMap="classResultMap">
  SELECT * FROM classes c LEFT JOIN teacher t ON c.head_teacher_id = t.id WHERE c.id = #{id}
</select>
<select id="queryTeacherById" resultType="com.example.mybatisplus.entity.Teacher">
  SELECT * FROM teacher WHERE id = #{id}
</select>

说明:

  1. 与一对一的查询几乎一样
  2. 映射的结果是一个集合,javaType则为集合的类型,这里是list
  3. ofType是集合中的对象类型,这里是学生

法二:嵌套结果映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<resultMap id="classResultMap" type="com.example.mybatisplus.entity.Classes">
   <id column="id" property="id"/>
   <result column="head_teacher_id" property="headTeacherId"/>

   <association property="headTeacher" javaType="com.example.mybatisplus.entity.Teacher">
       <id column="t_id" property="id"/>
       <result column="t_name" property="name"/>
   </association>

   <collection property="students" javaType="list" ofType="com.example.mybatisplus.entity.Student">
       <id column="s_id" property="id"/>
       <result column="s_name" property="name"/>
       <result column="s_class_id" property="classId"/>
   </collection>
</resultMap>
<select id="queryById" resultMap="classResultMap">
  SELECT c.*, t.id t_id, t.name t_name, s.id s_id, s.name s_name, s.class_id s_class_id FROM classes c
  LEFT JOIN teacher t ON c.head_teacher_id = t.id
  LEFT JOIN student s ON s.class_id = c.id
  WHERE c.id = #{id}
</select>

说明:

  1. 注意这里要给查出来的字段取别名
  2. 映射结果和查出的别名要一一对应

结果展示:

五、总结

关于MyBatis和MyBatis-Plus的使用本人并不是很熟悉,以上写法只是我在工作中容易碰到的,所以进行一个总结,怕自己容易忘。