- 高级映射:将多表查询结果映射为 Java 类对象。
- 多对一高级映射
- 一对多高级映射
十三、★MyBatis的高级映射及延迟加载
高级映射:将多表查询结果映射为 Java 类对象。
准备工作:
- 模块名:
mybatis-010-advanced-mapping
- 打包方式:
jar
- 引入依赖:
mysql
驱动依赖、mybatis
依赖、logback
依赖、junit
依赖。 - 引入配置文件:
jdbc.properties
、mybatis-config.xml
、logback.xml
- 创建
pojo
类:Student
、Clazz
- 创建
Mapper
接口:com.f.mybatis.mapper.StudentMapper
、com.f.mybatis.mapper.ClazzMapper
- 创建
Mapper
接口对应的映射文件:com/f/mybatis/mapper/StudentMapper.xml
、com/f/mybatis/mapper/ClazzMapper.xml
- 创建单元测试:
StudentMapperTest
、ClazzMapperTest
- 创建工具类:
SqlSessionUtil
- 模块名:
13.1 多对一
多对一:多个学生对应一个班级。
- 多的一方:学生表(主表)
- 一的一方:班级表(副表)
既然学生表是主表,那么 JVM 中的主对象就是
Student
对象、Clazz
对象是副对象。- 因此在
Student
类中,添加成员变量:private Clazz clazz;
。

怎么分主表和副表?
- 原则:谁在前谁是主表。
- 多对一:多在前,那么多就是主表。
- 一对多:一在前,那么一就是主表。
- 原则:谁在前谁是主表。
如何将 SQL 语句多表查询的结果映射为
Student
类对象?Student
类中含有Clazz
类成员变量。
可以有多种方式,常见的包括三种:
- 第一种方式:一条SQL语句,级联属性映射。
- 第二种方式:一条SQL语句,使用
association
标签。 - 第三种方式:两条 SQL 语句,分步查询(这种方式常用:优点一是可复用,优点二是支持懒加载,提高了性能)。
- 延迟加载的核心原理是:用的时候再执行查询语句,不用的时候不查询。
13.1.1 级联属性映射
1 | package com.f.mybatis.mapper; |
1 |
|
1 |
|
13.1.2 association标签
1 | package com.f.mybatis.mapper; |
1 |
|
1 |
|
- 第二种方式和第一种方式差别不大,就是在
StudentMapper.xml
文件中增加了association
标签。
13.1.3 ★分步查询(常用)
分步查询是指,分两步对两个表进行查询。因为学生表是主表,所以肯定是先查学生表。
第一步:先查学生表,根据学生
id
查学生信息。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package com.f.mybatis.mapper;
import com.f.mybatis.pojo.Student;
/**
* @author fzy
* @date 2024/1/11 16:28
*/
public interface StudentMapper {
/**
* 分步查询第一步,根据学生id查学生信息
*
* @param id
* @return
*/
Student selectByIdStep1(Integer id);
}- 注意上面这是
StudentMapper
接口。
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
<mapper namespace="com.f.mybatis.mapper.StudentMapper">
<!--两条SQL语句,完成多对一的分布查询-->
<!--这里是第一步,根据学生的id查询学生的所有信息,这些信息中含有班级的id(cid)-->
<resultMap id="studentResultMapByStep" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<!--
select里面指定另外第二步SQL语句的sqlId(namespace+id)
column里面指定传给第二步SQL语句的参数(是第一步SQL语句查询结果里面的)
-->
<association property="clazz"
select="com.f.mybatis.mapper.ClazzMapper.selectByIdStep2"
column="cid"/>
</resultMap>
<select id="selectByIdStep1" resultMap="studentResultMapByStep">
SELECT sid, sname, cid
FROM t_stu
<where>
sid = #{sid}
</where>
</select>
</mapper>注意上面
resultMap
中的配置。1
2
3
4
5
6
7<!--
select里面指定另外第二步SQL语句的sqlId(namespace+id)
column里面指定传给第二步SQL语句的参数(是第一步SQL语句查询结果里面的)
-->
<association property="clazz"
select="com.f.mybatis.mapper.ClazzMapper.selectByIdStep2"
column="cid"/>
- 注意上面这是
第二步:再查询班级表,根据班级
id
查班级信息(班级id
是第一步查询传过来的)。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package com.f.mybatis.mapper;
import com.f.mybatis.pojo.Clazz;
/**
* @author fzy
* @date 2024/1/11 16:40
*/
public interface ClazzMapper {
/**
* 分步查询第二步,根据班级id查班级信息
*
* @param id
* @return
*/
Clazz selectByIdStep2(Integer id);
}- 注意上面这是
ClazzMapper
接口。
1
2
3
4
5
6
7
8
9
10
11
12
13
<mapper namespace="com.f.mybatis.mapper.ClazzMapper">
<select id="selectByIdStep2" resultType="Clazz">
SELECT cid, cname
FROM t_clazz
<where>
cid = #{cid}
</where>
</select>
</mapper>- 注意上面这是
第三步,进行测试。
1
2
3
4
5
6
7
8
public void testSelectByIdStep1() {
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.selectByIdStep1(5);
System.out.println(student);
SqlSessionUtil.close(sqlSession);
}发现执行结果确实是执行了两条 SQL 语句:

分步查询的优点:
- 第一个优点:代码复用性增强。
- 第二个优点:支持延迟加载(懒加载)【暂时访问不到的数据可以先不查询,提高程序的执行效率】。
13.2 多对一延迟加载
要想使用延迟加载,非常简单,只需要在分布查询的
association
标签中添加fetchType="lazy"
即可。即修改13.1.3
小节的StudentMapper.xml
文件:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15...
<resultMap id="studentResultMapByStep" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<!--
select里面指定另外第二步SQL语句的sqlId(namespace+id)
column里面指定传给第二步SQL语句的参数(是第一步SQL语句查询结果里面的)
fetchType="lazy"表示开启延迟加载(懒加载)
-->
<association property="clazz"
select="com.f.mybatis.mapper.ClazzMapper.selectByIdStep2"
column="cid"
fetchType="lazy"/>
</resultMap>
...注意:默认情况下是不开启延迟加载的。
上面的这种
fetchType="lazy"
配置,是局部的设置,只对当前的association
关联的 sql 语句起作用。如果想开启全局的延迟加载,需要在
mybatis-config.xml
中,进行setting
配置:<setting name="lazyLoadingEnabled" value="true"/>
1
2
3
4
5
6
7
8
9
10
11
12
<configuration>
...
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
...
</configuration>- 在实际的开发中,大部分都是需要使用延迟加载的,所以建议开启全局的延迟加载机制。
实际开发中的模式:
- 把全局的延迟加载打开:
<setting name="lazyLoadingEnabled" value="true"/>
。 - 如果某一步不需要使用延迟加载,就通过
fetchType="eager"
进行设置。
- 把全局的延迟加载打开:
13.3 一对多
一对多:一个班级对应多个学生。
- 一的一方:班级表(主表)
- 多的一方:学生表(副表)
既然班级表是主表,那么 JVM 中的主对象就是
Clazz
对象、Student
对象是副对象。- 因此在
Clazz
类中,添加成员变量:private List<Student> students;
。

如何将 SQL 语句多表查询的结果映射为
Clazz
类对象?Clazz
类中含有List<Student>
成员变量。
可以有多种方式,常见的包括两种:
- 第一种方式:
collection
。 - 第二种方式:分步查询。
13.3.1 collection标签
1 | package com.f.mybatis.mapper; |
1 |
|
1 |
|
13.3.2 ★分步查询(常用)
一对多的分步查询类似于多对一的分布查询,因为此时班级表是主表,所以肯定是先查班级表。
第一步:先查班级表,根据班级
id
查班级信息。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package com.f.mybatis.mapper;
import com.f.mybatis.pojo.Clazz;
/**
* @author fzy
* @date 2024/1/11 16:40
*/
public interface ClazzMapper {
/**
* 一对多分步查询第一步,根据班级id查班级信息
*
* @param id
* @return
*/
Clazz selectByIdStep1(Integer id);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<mapper namespace="com.f.mybatis.mapper.ClazzMapper">
<resultMap id="clazzResultMapByStep" type="Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
<collection property="students"
select="com.f.mybatis.mapper.StudentMapper.selectByIdStep2"
column="cid"/>
</resultMap>
<select id="selectByIdStep1" resultMap="clazzResultMapByStep">
SELECT cid, cname
FROM t_clazz
<where>
cid = #{cid}
</where>
</select>
</mapper>第二步:再查学生表,根据班级
id
查学生信息(班级id
是第一步查询传过来的)。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package com.f.mybatis.mapper;
import com.f.mybatis.pojo.Student;
/**
* @author fzy
* @date 2024/1/11 16:28
*/
public interface StudentMapper {
/**
* 一对多分步查询第二步,根据班级id查学生信息
*
* @param cid
* @return
*/
Student selectByIdStep2(Integer cid);
}1
2
3
4
5
6
7
8
9
10
11
12
13
<mapper namespace="com.f.mybatis.mapper.StudentMapper">
<select id="selectByIdStep2" resultType="Student">
SELECT sid, sname
FROM t_stu
<where>
cid = #{cid}
</where>
</select>
</mapper>第三步,进行测试。
1
2
3
4
5
6
7
8
public void testSelectByIdStep1() {
SqlSession sqlSession = SqlSessionUtil.openSession();
ClazzMapper mapper = sqlSession.getMapper(ClazzMapper.class);
Clazz clazz = mapper.selectByIdStep1(1000);
System.out.println(clazz);
SqlSessionUtil.close(sqlSession);
}
一对多分步查询 和 多对一分步查询非常类似,区别在于:
- 一对多分步查询使用的是
collection
标签。 - 多对一分步查询使用的是
association
标签。
- 一对多分步查询使用的是
13.4 一对多延迟加载
- 和多对一延迟加载一样,具体看
13.2
小节笔记。