Mybatis | 07 Mybatis延迟加载
Mybatis延迟加载
1. 多表查询的问题
对于多表查询中用户和账户的示例
-
在查询用户时,如果把所有关联账户的信息都查询出来会占用较大的内存
-
在查询账户时,还要查询所属用户名便于了解账户的详情,单单只有一个用户ID不便于理解
2. 加载方式
2.1 两种加载方式
-
延迟加载:在真正使用数据时才进行查询,不用的时候不查询(查询用户)
-
立即加载:不管是否使用,只要调用查询方法就立刻一起进行查询(查询账户)
2.2 两种加载方式的选择
-
一对多、多对多关系(对多关系):关联的是一个集合会占用较大的内存,通常采用延迟加载
-
多对一、一对一关系(对一关系):关联的是一个基本对象且包含重要识别信息,通常采用立即加载
2.3 延迟加载的实现
2.3.1 对一关系配置
2.3.1.1 用户DAO接口
public interface IUserDao{
//根据ID查询用户
User findUserById(Integer userId);
}
2.3.1.2 账户DAO接口
public interface IAccountDao{
//查询所有
List<Account> findAll();
}
2.3.1.3 账户映射文件
使用 association标签 封装实体类对象
标签属性:
- select属性:指定延迟加载时查询信息使用的方法,值就是使用方法的select标签ID
- column属性:指定延迟加载时查询方法传入的参数
<!--头文件省略-->
<mapper namespace="org.example.dao.IAccountDao">
<resultMap id="accountMap" type="account">
<id property="id" column="id"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<association property="user" column="uid" javaType="user"
select="org.example.dao.IUserDao.findUserById">
</association>
</resultMap>
<select id="findAll" resultMap="accountMap">
select * from account;
</select>
</mapper>
小心别掉坑:注意column属性的值(column属性深入TO Mybatis多表查询篇)
如果ID属性和多表查询中一样使用aid属性列赋值
<resultMap id="accountMap" type="account">
<!--使用aid赋值-->
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<association property="user" column="uid" javaType="user"
select="org.example.dao.IUserDao.findUserById">
</association>
</resultMap>
由于没有配置别名,ID属性就无法封装,结果就为NULL
出错…
2.3.1.4 用户映射文件
<!--头文件省略-->
<mapper namespace="org.example.dao.IUserDao">
<select id="findUserById" parameterType="INT" resultType="user">
select * from user where id=#{id};
</select>
</mapper>
2.3.2 对多关系配置
2.3.2.1 用户DAO接口
public interface IUserDao{
//查询所有
List<User> findAll();
}
2.3.2.2 账户DAO接口
public interface IAccountDao{
//根据用户的ID查询账户
Account findAccountByUid(Integer userId);
}
2.3.2.3 用户映射文件
使用 collection标签 封装实体类集合
标签属性:
与association标签的属性意义相同
- select属性:指定延迟加载时查询信息使用的方法,值就是使用方法的select标签ID
- column属性:指定延迟加载时查询方法传入的参数
<!--头文件省略-->
<mapper namespace="org.example.dao.IUserDao">
<resultMap id="userMap" type="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
<collection property="accounts" column="id" ofType="account"
select="org.example.dao.IAccountDao.findAccountByUid">
</collection>
</resultaMap>
<select id="findAll" resultMap="userMap">
select * from user;
</select>
</mapper>
2.3.2.4 账户映射文件
<!--头文件省略-->
<mapper namespace="org.example.dao.IAccountDao">
<select id="findAccountByUid" parameterType="INT" resultType="account">
select * from account where uid=#{uid};
</select>
</mapper>
2.3.3 开启延迟加载支持
两个与延迟加载有关的全局参数
lazyLoadingEnabled
aggressiveLazyLoading
在主配置文件中开启延迟加载功能
<!--头文件省略-->
<configuration>
<!--properties标签省略-->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<!--typeAlises标签配置省略-->
<!--environments配置省略-->
<!--mappers配置省略-->
</configuration>
小心别掉坑:注意配置的顺序 (TO Mybatis基本使用篇)
2.3.4 测试代码
以账户查询为例
- 测试方法
//省略获取session的过程和释放资源过程
@Test
public void findAllAccountTest(){
List<Account> accounts = accountDao.findAllAccount();
for(Account account : accounts){
System.out.println(account);
System.out.println(account.getUser());
}
}
- 测试结果
- 未开启延迟加载(立即加载)
- 开启延迟加载仅查询不打印用户信息(不使用用户信息)
- 开启延迟加载打印用户信息(使用用户信息)
Tips 2.1:实现特点
在实现的过程中,都调用了对方的一个方法,实现延迟加载时进行查询
- 用户延迟查询账户信息时使用了账户接口中根据用户ID查询账户的方法
- 账户延迟查询用户信息时使用了用户接口中根据用户ID查询用户的方法
以用户查询为例图解
Tips 2.2:多表查询关联对象的封装
以账户查询为例
- 自己定义封装
- 交给关联对象内部封装
- 使用多表查询语句,同时使用ID和RESULT标签定义封装方式
<mapper namespace="org.example.dao.IAccount">
<resultMap id="accountMap" type="account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<association property="user" column="uid" javaType="user">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="address" column="address"></result>
<result property="sex" column="sex"></result>
<result property="birthday" column="birthday"></result>
</association>
</resultMap>
<select id="findAllAccount" resultMap="accountMap">
select u.*,a.id as aid,a.uid,a.money from account a,user u
where a.uid=u.id;
</select>
</mapper>
- 使用单表查询语句,同时使用SELECT属性调用关联对象的查询方法实现封装
- 账户映射文件
<mapper namespace="org.example.dao.IAccount">
<resultMap id="accountMap" type="account">
<id property="id" column="aid"></id>
<result property="uid" column="uid"></result>
<result property="money" column="money"></result>
<association property="user" column="uid" javaType="user"
select="org.example.dao.IUseDao.findUserById">
</association>
</resultMap>
<select id="findAllAccount" resultMap="accountMap">
select * from account;
</select>
</mapper>
- 用户映射文件
<mapper namespace="org.example.dao.IUserDao">
<select id="findUserById" parameterType="INT" resultType="user">
select * from user where id=#{id};
</select>
</mapper>