那一天,我爱上你,才知道,一切都变得那么不重要。 有幸遇见,Hello Java。

上一章简单介绍了MyBatis的一级缓存和二级缓存(十三),如果没有看过,​​请观看上一章​​。

一.MyBatis 延迟加载

MyBatis 延迟加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。 如在查询 User 的信息时,如果没有用到Dept或者不想查询Dept,就不查询Dept 的信息,哪怕关联了Dept。
为了详细一些,老蝴蝶把以前的表信息拿出来。

User表:

MyBatis的延迟加载(十四)_sql


Dept表:

MyBatis的延迟加载(十四)_延迟加载_02


IdCard 表:

MyBatis的延迟加载(十四)_延迟加载_03


对应的实体 类.

User.java

package com.yjl.pojo;

/**
@author:yuejl
@date: 2019年6月15日 上午11:11:02
@Description Mybatis 使用的基本类 User
*/
public class User {
/**
* @param id id编号,自增
* @param name 姓名
* @param age 年龄
* @param sex 性别
* @param description 描述
*/
private Integer id;
private String name;
private Integer age;
private String sex;
private String description;
//引入部门的对象。
private IdCard idCard;
private Dept dept;
public User(){

}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public IdCard getIdCard() {
return idCard;
}
public void setIdCard(IdCard idCard) {
this.idCard = idCard;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", age=" + age + ", sex=" + sex + ", description=" + description
+ "]";
}
}

Dept.java

package com.yjl.pojo;

import java.util.List;

/**
@author: yuejl
@date: 2019年7月8日 上午10:15:08
@Description 数据库中一的一方 部门实体
*/
public class Dept {
/**
* @param id 部门的编号
* @param name 部门的名称
* @param description 部门的描述
*/
private Integer id;
private String name;
private String description;
// 集合,用的是 List, 而不是Set
private List<User> allUser;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<User> getAllUser() {
return allUser;
}
public void setAllUser(List<User> allUser) {
this.allUser = allUser;
}
@Override
public String toString() {
return "Dept [id=" + id + ", name=" + name + ", description=" + description + "]";
}
}

IdCard.java

package com.yjl.pojo;
/**
@author: yuejl
@date: 2019年7月5日 下午12:41:47
@Description 类的相关描述
*/
public class IdCard {
/**
* @param uid 身份证唯一标识符
* @param idNum 身份证标识符
*/
private Integer id;
private String idNum;

//引入员工的属性
private User userId;

public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getIdNum() {
return idNum;
}
public void setIdNum(String idNum) {
this.idNum = idNum;
}
public User getUserId() {
return userId;
}
public void setUserId(User userId) {
this.userId = userId;
}
@Override
public String toString() {
return "IdCard [id=" + id + ", idNum=" + idNum + ", userId=" + userId + "]";
}
}

二. 由User 去关联查询部门和身份证

方法与前面讲关联关系时一样,故老蝴蝶在这里就不继续讲了。

UserMapper.java 接口:

public interface UserMapper {
public User getByIdWithSelect(int id);
public List<User> findUserByDeptId(@Param(value="deptId") int deptId);
}

DeptMapper.java 接口:

public interface DeptMapper {
public Dept getById(int id);
public Dept getAllInfoByIdWithSelect(int id);
}

IdCardMapper.java 接口:

public interface IdCardMapper {
public IdCard findById(int id);
}

UserMapper.xml 语句:

<resultMap type="user" id="userResultMapWithSelect">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sex" column="sex"/>
<result property="age" column="age"/>
<result property="description" column="description"/>
<association property="idCard" javaType="idCard"
column="id" select="com.yjl.mapper.IdCardMapper.findByUserId">
</association>
<association property="dept" javaType="dept"
column="deptId" select="com.yjl.mapper.DeptMapper.getById" ></association>
</resultMap>

<select id="getByIdWithSelect" parameterType="int" resultMap="userResultMapWithSelect">
select * from user u where u.id=#{id}
</select>

DeptMapper.xml 语句

<resultMap type="dept" id="deptResultMap">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="description" column="description"/>
</resultMap>
<!-- 嵌套结果 -->
<select id="getById" parameterType="int" resultMap="deptResultMap">
select * from dept where id=#{id}
</select>

IdCardMapper.xml 语句:

<resultMap type="idCard" id="idCardResultMap">
<id property="id" column="id"/>
<result property="idNum" column="idNum"/>
</resultMap>
<!-- 嵌套查询的sql, 没有对应的接口 -->
<select id="findByUserId" parameterType="int" resultMap="idCardResultMap">
select * from idCard where uid=#{id}
</select>

二.一 全部查询时是否延迟加载

@Test
public void findByIdWithSelectF2Test(){
SqlSession sqlSession=SqlSessionFactoryUtils.getSession();
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
User user=userMapper.getByIdWithSelect(1);
System.out.println("user:"+user);
//没有用其他的,也查询了。
IdCard idCard=user.getIdCard();
System.out.println("idCard:"+idCard);
Dept dept=user.getDept();
System.out.println("dept:"+dept);
}

MyBatis的延迟加载(十四)_MyBatis中fetchType的使用_04


进行了三次查询。

debug 一下,看查询的点在哪,是在getByIdWithSelect() 方法时, 还是在 user.getDept() ,user.getIdCard() 时。

MyBatis的延迟加载(十四)_sql_05

执行了方法:

MyBatis的延迟加载(十四)_java_06


方法调用之后,就会去查询了。

这是全部查询的。

二.二 只查询User 表信息。

@Test
public void findByIdWithSelectF1Test(){
SqlSession sqlSession=SqlSessionFactoryUtils.getSession();
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
User user=userMapper.getByIdWithSelect(1);
System.out.println(user);
//不查询部门和身份证信息。
}

MyBatis的延迟加载(十四)_MyBatis的延迟加载_07

也是把 部门和身份证查询了出来。 当然,肯定也是在调用getByIdWithSelect() 方法时查询的。

三. 延迟加载 lazy

三.一 setting 中设置 lazyLoadingEnabled

设置延迟加载为 true, 默认为false. 这是延迟加载的总开关,必须开启。

将其设置为true.

<setting name="lazyLoadingEnabled" value="true"/>

三.二 setting中设置 aggressiveLazyLoading 为false.

这个值,在3.4.1 版本之前,默认为true, 在3.4.1之后,就默认为false.
老蝴蝶的mybatis 版本为3.4.5 ,默认为false.
其中,设置时有些不同。 延迟加载时设置为false.

现设置为false

<setting name="aggressiveLazyLoading" value="true"/>

三.三 测试 findByIdWithSelectF1Test() 方法

发现依旧查询了

MyBatis的延迟加载(十四)_MyBatis中fetchType的使用_08


延迟加载并没有好使。 这并没有错。 查询资料,

将打印的 语句去除掉:

User user=userMapper.getByIdWithSelect(1);
//System.out.println(user);

测试运行

MyBatis的延迟加载(十四)_MyBatis的延迟加载_09


这个时候,就只查询一条了,延迟加载好用了。

但并不能去除掉 打印语句啊,不然怎么看呢。 原因还是在setting 设置时。

在setting 中有这么一条设置:

<setting name="lazyLoadTriggerMethods" value="equals,toString,clone,hashCode"/>

即如果运行 equals,toString,clone,hasCode 方法时,将不延迟加载,而是立即加载。

将其中的toString 去除掉,再进行操作。

<setting name="lazyLoadTriggerMethods" value="equals,toString,clone,hashCode"/>

继续测试 findByIdWithSelectF1Test() 方法, 加上那条打印语句。

MyBatis的延迟加载(十四)_java_10


也只查询了一个。 延迟加载成功。

三.四 测试 findByIdWithSelectF2Test() 方法

这个方法,肯定查询三次,但要debug 测试一下,查询的点在哪,是一次性查出的,还是分别查询出的。

MyBatis的延迟加载(十四)_延迟加载_11

MyBatis的延迟加载(十四)_延迟加载_12


MyBatis的延迟加载(十四)_延迟加载_13

MyBatis的延迟加载(十四)_MyBatis中fetchType的使用_14

MyBatis的延迟加载(十四)_延迟加载_15

MyBatis的延迟加载(十四)_MyBatis的延迟加载_16


是用到的时候,才进行查询。

四. 将 aggressiveLazyLoading 设置为true 时

<!-- 控制懒加载的 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="true"/>

这个时候,测试运行 findByIdWithSelectF1Test()

MyBatis的延迟加载(十四)_sql_17


发现,并没有延迟加载。 故需要将其字段设置为false 才可以。

五. 在关联处 设置是否延迟加载

有的时候,需要对同一个类 中的不同的属性分别进行设置 是否进行延迟加载, 如 查询员工时,把身份证信息查询出来,把部门延迟加载出来。 故在setting 的 lazyLoadingEnabled 设置总开头时,又设置了一个分开关。 fetchType , 默认为lazy, 延迟加载。 有两个值, lazy 延迟加载, eager 急切加载,即不延迟加载。

如分别设置 身份证为延迟加载, 部门为不延迟加载。

<association property="idCard" javaType="idCard" 
column="id" select="com.yjl.mapper.IdCardMapper.findByUserId"
fetchType="lazy">
<association property="dept" javaType="dept"
column="deptId" select="com.yjl.mapper.DeptMapper.getById"
fetchType="eager"></association>

这个时候,执行 findByIdWithSelectF1Test() 方法。(aggressiveLazyLoading 已经设置为false 了。)

MyBatis的延迟加载(十四)_延迟加载_18


查询了部门的信息,没有查询身份证信息, 身份证信息被延迟加载了。

六. 部门查询员工的一对多加载

接口上面写过了。

DeptMapper.xml sql语句:

<resultMap type="dept" id="deptCollectionResultMapWithSelect">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="description" column="description"/>
<!-- 用的是ofType的类型。 -->
<collection property="allUser" ofType="user" column="id"
select="com.yjl.mapper.UserMapper.findUserByDeptId"></collection>
</resultMap>
<select id="getAllInfoByIdWithSelect" parameterType="int" resultMap="deptCollectionResultMapWithSelect">
select * from dept where id=#{id}
</select>

UserMapper.xml 语句:

<resultMap type="user" id="userResultMap">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sex" column="sex"/>
<result property="age" column="age"/>
<result property="description" column="description"/>
</resultMap>
<select id="findUserByDeptId" parameterType="int" resultMap="userResultMap">
select * from user u where u.deptId=#{deptId}
</select>

测试方法:

@Test
public void getAllInfoByIdWithSelectTest(){
SqlSession sqlSession=SqlSessionFactoryUtils.getSession();
DeptMapper deptMapper=sqlSession.getMapper(DeptMapper.class);
Dept dept=deptMapper.getAllInfoByIdWithSelect(1);
System.out.println(dept);
}

测试运行,发现只查询出了 部门的信息,并没有查询出员工的信息。

MyBatis的延迟加载(十四)_sql_19


当使用员工集合时,才会查询。

@Test
public void getAllInfoByIdWithSelectTest(){
SqlSession sqlSession=SqlSessionFactoryUtils.getSession();
DeptMapper deptMapper=sqlSession.getMapper(DeptMapper.class);
Dept dept=deptMapper.getAllInfoByIdWithSelect(1);
System.out.println(dept);
List<User> allUser=dept.getAllUser();
allUser.forEach(n ->System.out.println(n));
}

在调用 getAllUser() 方法时才会查询。

MyBatis的延迟加载(十四)_MyBatis的延迟加载_20

debug 运行的话:

MyBatis的延迟加载(十四)_延迟加载_21

getAllUser() 时进行查询:

MyBatis的延迟加载(十四)_sql_22

输出:

MyBatis的延迟加载(十四)_延迟加载_23


当然,如果想将其改成 不延迟加载,而是立即查询,只需要 fetchType 改成eager 即可。

<!-- 用的是ofType的类型。 -->
<collection property="allUser" ofType="user" column="id"
select="com.yjl.mapper.UserMapper.findUserByDeptId"
fetchType="eager"></collection>

MyBatis的延迟加载(十四)_MyBatis中fetchType的使用_24

谢谢!!!