MyBatis的延迟加载
问题:在一对多的关系中,例:有一个班级,其中有100个学生,在查询班级的时候要不要把关联的学生查询出来,查询学生的时候要不要把关联的班级查询出来?
解答:在查询班级的时候,学生的信息应该是什么时候使用什么时候去查询,在查询学生时,班级的信息应该是随着学生查询时一起查询出来。
一、延迟加载
-
概念:MyBatis中的延迟加载也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。在真正使用数据的时候才发起查询,不用的时候不查询关联的数据,延迟加载又叫做按需查询。例如,在进行一对多查询的时候,只查询出一方,当程序中需要多方数据时,MyBatis在发出sql语句进行查询,这样子延迟加载就可以减少数据库压力,MyBatis的延迟加载只是对关联对象的查询有延迟设置,对于主加载对象都是直接执行查询语句的。
-
MyBatis中延迟加载的条件:resultMap可以实现高级映射,例:使用association、collection实现一对一及一对多映射,association、collection具备延迟加载功能。
-
延迟加载的好处:先从单表查询,需要时再从关联表查询,大大提高数据库性能,因为查询单表比关联查询多张表速度要快。
-
使用场景:在对应的四种关系表中,一对多、多对多通常情况下采用延迟加载,多对一、一对一、通常采用立即加载。
-
延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询,因为多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询,会一次性将多张表的所有信息查询出来。
二、开启延迟加载功能
MyBatis中实现查询方法的延迟加载,在MyBatis
的配置文件中通过设置settings的lazyLoadingEnabled属性为true进行开启全局的延迟加载,通过aggressiveLazyLoading属性开启立即加载。
| 设置名 | 描述 | 有效值 | 默认值 |
|---|---|---|---|
| lazyLoadingEnabled | 延迟加载的全局开关,当开启时所有关联对象都会延迟加载,特定关联关系可以通过设置fetchType属性来覆盖该项的开关状态。 | true|false | false |
| aggressiveLazyLoading | 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。 | true|false | false(在3.4.1及之前的版本默认值为true) |
| lazyLoadTriggerMethods | 指定对象的哪些方法触发一次延迟加载。 | 用逗号分隔的方法列表。 | equals,clone,hashCode,toString |
<settings>
<!-- 开启延迟加载主要是前两个配置-->
<!-- 延迟加载,默认是false-->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 当为true时,所有延迟属性都会立即加载,为false时,调用这些延迟属性时才会查询和加载-->
<setting name="aggressiveLazyLoading" value="false" />
<!-- 最好配置为空,这样可以最大限度延迟加载-->
<!-- 当前配置的意思为当调用getId时延迟加载-->
<!-- 可以不配置-->
<setting name="lazyLoadTriggerMethods" value="getId"/>
</settings>
三、延迟加载的实现
-
在resultMap中使用association或者collection,即可使用延迟加载。
-
延迟加载需要多个select语句,不能是一条多表连接的select语句。
实现班级学生案例:
- 创建实体类 班级类以及学生类
班级类:
public class Clazz {
private int id;
private String name;
private List<Student> students;
@Override
public String toString() {
return "Clazz{" +
"id=" + id +
", name='" + name + '\'' +
", students=" + students +
'}';
}
get和set方法省略...
学生类:
public class Student {
private int id;
private String name;
private String sex;
private int clazz;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", clazz=" + clazz +
'}';
}
get和set方法省略...
- 创建StudentMapper和ClazzMapper接口
ClazzMapper接口:
public interface ClazzMapper {
/**
* 根据班级编号查询班级
* @param id
* @return
*/
Clazz selectById(int id);
}
StudentMapper接口:
public interface StudentMapper {
/**
* 根据学生编号查找学生
* @param id
* @return
*/
Student selectById(int id);
/**
* 根据学生的班级号查找学生
* @param id
* @return
*/
List<Student> selectByClazz(int id);
}
- 创建ClazzMapper.xml和StudentMapper.xml
ClazzMapper.xml:
<!-- 根据班级编号查找班级-->
<select id="selectById" resultMap="rm">
select * from clazz where id=#{id}
</select>
<!-- 配置映射,根据学生班级号查找学生放进班级里-->
<resultMap id="rm" type="clazz" autoMapping="true">
<id column="id" property="id"></id>
<collection property="students" column="id" select="dao.StudentMapper.selectByClazz"></collection>
</resultMap>
StudentMapper.xml:
<!-- 根据学生id查找学生-->
<select id="selectById" resultType="student">
select * from student where id=#{id}
</select>
<!-- 根据学生班级号查找学生-->
<select id="selectByClazz" resultType="student">
select * from student where clazz=#{id}
</select>
- 测试:
public class Test {
public static void main(String[] args) {
Clazz clazz = new ClazzService().selectById(1); // 1
System.out.println(clazz.getId()); // 2
System.out.println(clazz.getName()); // 3
System.out.println(clazz.getStudents()); // 4
}
}
当注释掉第4句时,因为没有要求查找学生,按照延迟加载的特性,程序只会查询班级的信息,而不会查询学生的信息,所以只有一条sql语句,就是根据班级编号查找班级的select,并没有查找学生的select:

当放开第四句时,因为clazz.getStudents()需要student数据,所以会执行第二条sql语句将student的信息查询到:



浙公网安备 33010602011771号