Mybatis 的延迟加载
# 180.Mybatis 的延迟加载
使用延迟加载,有时候能加快查询的速度。
# Mybatis 中的加载
在一对多中,当我们有一个用户,它有 100 个账户。此时有几个问题:
- 在查询用户的时候,要不要同时把关联的账户查出来?
- 在查询账户的时候,要不要同时把关联的用户查出来?
在 Mybatis 中,有两种加载方式:
- 延迟加载:在真正使用数据时才发起查询,不用的时候不查询。也叫按需加载、懒加载
- 立即加载:不管用不用,只要一调用方法,马上发起查询。
在查询用户时,用户下的账户信息应该是什么时候使用,什么时候查询出来。举个例子,我们就是想查询一下用户,而该用户关联了 100 个账户,如果都全部查询出来,那么会占用很多的内存,而我们有时候并不会用到这些账户数据;所以应该是用延迟加载。
在查询账户时,账户的所属用户信息应该是随着账户查询时一起查询出来,因为显示账户信息时,通常也需要同时显示所属的用户信息,所以应该立即加载。
那么如何确定使用哪种加载方式呢?表与表之间的关系有 4 种:一对多,多对一,一对一,多对多,通常情况下
一对多,多对多,一般都是采用延迟加载。
多对一,一对一,一般都是采用立即加载。
接下来我们就讲解下,一对一 和 一对多 实现延迟加载的情况
# 一对一实现延迟加载
我们之前实现的查询账户功能时,也会将用户信息立即查询出来,因为 SQL 就是这么写的(IUserDao.xml):
<resultMap id="accountUserMap" type="account">
<id property="id" column="id"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
<!-- 建立一对一的关系映射,配置封装user的内容 -->
<association property="user" column="uid" javaType="user">
<id property="userId" column="id"/>
<result property="userName" column="username"/>
<result property="userAddress" column="address"/>
</association>
</resultMap>
<select id="findAll" resultMap="accountUserMap">
SELECT a.*, u.username, u.address
FROM account a, user u
WHERE u.id = a.UID
</select>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
我们是通过配置 association 标签来实现延迟加载,新增一个属性 select,并修改 findAll 方法的 SQL,修改后的内容如下:
<resultMap id="accountUserMap" type="account">
<id property="id" column="id"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
<!-- 建立一对一的关系映射,配置封装user的内容 -->
<association property="user" column="uid" javaType="user" select="com.peterjxl.dao.IUserDao.findById"/>
</resultMap>
<select id="findAll" resultMap="accountUserMap">
SELECT * from account
</select>
2
3
4
5
6
7
8
9
10
11
此时我们再试着运行 findAll 方法:
发现查询了 3 次 SQL,但是并没有实现延迟加载的效果,这是因为 Mybatis 需要手动开启延迟加载,我们可以看看官网文档:mybatis – MyBatis 3 | 配置 (opens new window)
在 Mybatis 中,默认是关闭延迟加载的,我们得手工开启。我们在 Mybatis 主配置文件中,添加如下标签(注意 settings 标签要在 properties 和 typeAliases 标签之间配置):
<settings>
<!-- 开启Mybatis支持延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
2
3
4
5
我们再次运行,可以看到在遍历用户集合的时候,才会去查询了,也就是实现了延迟加载。
# 一对多实现延迟加载
一个用户可以有多个账户,本次我们就实现延迟查询账户信息。
我们修改 IUserDao.xml:
<resultMap id="userAccountMap" type="user">
<id property="userId" column="id"/>
<result property="userName" column="userName"/>
<result property="userAddress" column="address"/>
<result property="userSex" column="sex"/>
<result property="userBirthday" column="birthday"/>
<!-- 配置user对象中account集合的映射 -->
<collection property="accounts" ofType="account" select="com.peterjxl.dao.IAccountDao.findAccountByUID" column="id"/>
</resultMap>
<!-- 配置查询所有用户,id要写方法名称-->
<select id="findAll" resultMap="userAccountMap">
SELECT * FROM user u
</select>
2
3
4
5
6
7
8
9
10
11
12
13
14
在 IAccountDao.java
接口中新增方法:
package com.peterjxl.dao;
import com.peterjxl.domain.Account;
import com.peterjxl.domain.AccountUser;
import java.util.List;
public interface IAccountDao {
List<Account> findAll();
List<AccountUser> findAccountUser();
List<Account> findAccountByUID(Integer uid);
}
2
3
4
5
6
7
8
9
修改 IAccountDao.xml,增加 findAccountByUID 方法:
<!--根据用户ID查询账户列表 -->
<select id="findAccountByUID" resultType="account">
SELECT * from account
where uid = #{uid}
</select>
2
3
4
5
测试:
@Test
public void helloMybatis() throws Exception{
List<User> users = userDao.findAll();
}
2
3
4
运行结果:只查询了用户的信息,并没有查询账户的信息;
而当我们遍历 User 对象的时候,就会去查询账户信息了:
@Test
public void helloMybatis() throws Exception{
List<User> users = userDao.findAll();
for(User user : users){
System.out.print(user);
System.out.println(user.getAccounts());
}
}
2
3
4
5
6
7
8
9
# 源码
本文所有代码已上传到了 GitHub (opens new window) 和 Gitee (opens new window) 上,并且创建了分支 demo17,读者可以通过切换分支来查看本文的示例代码。