文章目录

1.什么是延迟加载?

前面我们讲解Mybatis中的一对一,一对多,多对多关系的配置及实现,可以实现对象的关联查询。在实际使用中的大多数时间里查询用户信息时是不需要查询它的账户信息的。此时就可以使用延迟加载策略。就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。

2.搭建一对多测试环境

一对多关联查询是指在查询一方对象的同时把与它关联的多方对象也查询出来,这里以用户(​​User​​​)和账户(​​Account​​)为例,一个用户可以有多个账户,一个账户只能属于一个用户,用户和账户是一对多关系。

建议在开始之前,新建一个​​maven​​​项目,将上一个工程的内容复制到新工程内,这样便于修改使用。
新建account数据库表,语句如下:

注意: 这里的uid是外键,关联user表,表中的数据大家可以根据自己的user表中的数据自行插入。

CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`birthday` date DEFAULT NULL,
`sex` varchar(255) COLLATE utf8_bin DEFAULT NULL,
`address` varchar(255) COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'zhansgan', '2021-08-07', '1', '北京');


DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`ID` int NOT NULL AUTO_INCREMENT COMMENT '编号',
`UID` int DEFAULT NULL COMMENT '用户编号',
`MONEY` double DEFAULT NULL COMMENT '金额',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3;

-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES ('1', '1', '2000');
INSERT INTO `account` VALUES ('2', '1', '4000');

UserMapper接口:

import java.util.List;

public interface UserMapper {

User findUserById(int id);
}

UserMapper.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bruce.mapper.UserMapper">


<!--定义User的resultMap-->
<resultMap id="userAccountMap" type="com.bruce.pojo.User">
<id property="id" column="id"/>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<!--配置User对象中accounts集合的映射
ofType指定集合中元素的类型,这里使用别名-->
<collection property="accounts" ofType="com.bruce.pojo.Account">
<id property="id" column="aid"/>
<result property="uid" column="uid"/>
<result property="money" column="money"/>
</collection>
</resultMap>

<!--配置查询所有User, id为namespace限定的接口中的方法名-->
<select id="findUserById" resultMap="userAccountMap">
select u.*, a.id as aid, a.uid, a.money from user u left outer join account a on a.uid=u.id where u.id=#{id};
</select>

</mapper>

测试类:

package com.bruce.test;

import com.bruce.mapper.EmployeeMapper;
import com.bruce.mapper.UserMapper;
import com.bruce.pojo.Employee;
import com.bruce.pojo.User;
import com.bruce.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

/**
* 测试类
*/
public class TestUser {

SqlSession sqlSession = null;
UserMapper um = null;

@Before
public void init() {
sqlSession = MyBatisUtils.getSession();
um = sqlSession.getMapper(UserMapper.class);
}

@Test
public void test1(){
User u=um.findUserById(1);
}

@After
public void destory(){
MyBatisUtils.closeSqlsession(sqlSession);
}

}

运行结果:

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 366873404.
==> Preparing: select u.*, a.id as aid, a.uid, a.money from user u left outer join account a on a.uid=u.id;
==> Parameters:
<== Columns: id, username, birthday, sex, address, aid, uid, money
<== Row: 1, zhansgan, 2021-08-07, 1, 北京, 2, 1, 4000.0
<== Row: 1, zhansgan, 2021-08-07, 1, 北京, 1, 1, 2000.0
<== Total: 2
User(id=1, username=zhansgan, birthday=2021-08-07, sex=1, address=北京, accounts=[Account(id=2, uid=1, money=4000.0), Account(id=1, uid=1, money=2000.0)])
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@15de0b3c]
Returned connection 366873404 to pool.

3.使用collection实现的延迟加载

这里我们使用前面讲到的账户和用户的一对对关系,修改AccountDao.xml映射文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bruce.mapper.UserMapper">


<!--定义User的resultMap-->
<resultMap id="userAccountMap" type="com.bruce.pojo.User">
<id property="id" column="id"/>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<!--配置User对象中accounts集合的映射
ofType指定集合中元素的类型,这里使用别名-->
<collection property="accounts" ofType="com.bruce.pojo.Account" column="id" select="com.bruce.mapper.AccountMapper.findAccountByUid"></collection>
</resultMap>

<!--配置查询所有User, id为namespace限定的接口中的方法名-->
<!--配置查询所有User, id为namespace限定的接口中的方法名-->
<select id="findUserById" resultMap="userAccountMap">
select * from user where id=#{id}
</select>

</mapper>

AccountMapper接口:

import com.bruce.pojo.Account;

import java.util.List;

public interface AccountMapper {

List<Account> findAccountByUid(int uid);
}

AccountMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bruce.mapper.AccountMapper">

<select id="findAccountByUid" resultType="Account">
select * from account where uid=#{uid}
</select>

</mapper>

运行结果:

C:\JDK8\bin\java.exe -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:G:\softDevelopment\ideaIU-2019.3.5\ideaIU-2019.3.5.win\lib\idea_rt.jar=11518:G:\softDevelopment\ideaIU-2019.3.5\ideaIU-2019.3.5.win\bin -Dfile.encoding=UTF-8 -classpath G:\softDevelopment\ideaIU-2019.3.5\ideaIU-2019.3.5.win\lib\idea_rt.jar;G:\softDevelopment\ideaIU-2019.3.5\ideaIU-2019.3.5.win\plugins\junit\lib\junit5-rt.jar;G:\softDevelopment\ideaIU-2019.3.5\ideaIU-2019.3.5.win\plugins\junit\lib\junit-rt.jar;C:\JDK8\jre\lib\charsets.jar;C:\JDK8\jre\lib\deploy.jar;C:\JDK8\jre\lib\ext\access-bridge-64.jar;C:\JDK8\jre\lib\ext\cldrdata.jar;C:\JDK8\jre\lib\ext\dnsns.jar;C:\JDK8\jre\lib\ext\jaccess.jar;C:\JDK8\jre\lib\ext\jfxrt.jar;C:\JDK8\jre\lib\ext\localedata.jar;C:\JDK8\jre\lib\ext\nashorn.jar;C:\JDK8\jre\lib\ext\sunec.jar;C:\JDK8\jre\lib\ext\sunjce_provider.jar;C:\JDK8\jre\lib\ext\sunmscapi.jar;C:\JDK8\jre\lib\ext\sunpkcs11.jar;C:\JDK8\jre\lib\ext\zipfs.jar;C:\JDK8\jre\lib\javaws.jar;C:\JDK8\jre\lib\jce.jar;C:\JDK8\jre\lib\jfr.jar;C:\JDK8\jre\lib\jfxswt.jar;C:\JDK8\jre\lib\jsse.jar;C:\JDK8\jre\lib\management-agent.jar;C:\JDK8\jre\lib\plugin.jar;C:\JDK8\jre\lib\resources.jar;C:\JDK8\jre\lib\rt.jar;E:\Server\MyBatis202108\MyBatis20210805\target\test-classes;E:\Server\MyBatis202108\MyBatis20210805\target\classes;C:\Users\bruceliu\.m2\repository\org\mybatis\mybatis\3.4.5\mybatis-3.4.5.jar;C:\Users\bruceliu\.m2\repository\mysql\mysql-connector-java\8.0.15\mysql-connector-java-8.0.15.jar;C:\Users\bruceliu\.m2\repository\com\google\protobuf\protobuf-java\3.6.1\protobuf-java-3.6.1.jar;C:\Users\bruceliu\.m2\repository\junit\junit\4.12\junit-4.12.jar;C:\Users\bruceliu\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;C:\Users\bruceliu\.m2\repository\org\projectlombok\lombok\1.16.18\lombok-1.16.18.jar com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.bruce.test.TestUser,test1
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 645482568.
==> Preparing: select * from user where id=?
==> Parameters: 1
<== Columns: id, username, birthday, sex, address
<== Row: 1, zhansgan, 2021-08-07, 1, 北京
====> Preparing: select * from account where uid=?
====> Parameters: 1(Integer)
<==== Columns: ID, UID, MONEY
<==== Row: 1, 1, 2000.0
<==== Row: 2, 1, 4000.0
<==== Total: 2
<== Total: 1
User(id=1, username=zhansgan, birthday=2021-08-07, sex=1, address=北京, accounts=[Account(id=1, uid=1, money=2000.0), Account(id=2, uid=1, money=4000.0)])
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@26794848]
Returned connection 645482568 to pool.

Process finished with exit code 0

4.开启Mybatis的延迟加载策略

只写了上面的内容还不够,必须在mybatis的主配置文件中,开启延迟加载:

<settings>
<!--延迟加载总开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--是否启用侵入式延迟加载-->
<!--3.4.1版本之前默认是true,之后默认是false-->
<setting name="aggressiveLazyLoading" value="true"/>
</settings>
直接加载:执行完对主对象的查询后,立刻执行对关联对象的查询。
侵入式延迟(aggressiveLazyLoading) 执行完对主对象的查询后,不会执行对关联对象的查询。当要访问主对象的属性时,就会立刻执行对关联对象的查询。
深度延迟: 执行完对主对象的查询后,不会执行对关联对象的查询。访问主对象的属性时也不会执行对关联对象的查询。只有当真正访问关联对象的详情时,才会执行对关联对象的查询。

注意: settings标签在主配置文件中的位置一定要按照如下的顺序放置。

没有开启延迟加载:

测试程序:

@Test
public void test1(){
User u=um.findUserById(1);
}

运行结果:

PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 807752428.
==> Preparing: select * from user where id=?
==> Parameters: 1
<== Columns: id, username, birthday, sex, address
<== Row: 1, zhansgan, 2021-08-07, 1, 北京
====> Preparing: select * from account where uid=?
====> Parameters: 1(Integer)
<==== Columns: ID, UID, MONEY
<==== Row: 1, 1, 2000.0
<==== Row: 2, 1, 4000.0
<==== Total: 2
<== Total: 1
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@302552ec]
Returned connection 807752428 to pool.

开启延迟加载:
测试程序:

@Test
public void test1(){
User u=um.findUserById(1);
}

运行结果:

C:\JDK8\bin\java.exe -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:G:\softDevelopment\ideaIU-2019.3.5\ideaIU-2019.3.5.win\lib\idea_rt.jar=11617:G:\softDevelopment\ideaIU-2019.3.5\ideaIU-2019.3.5.win\bin -Dfile.encoding=UTF-8 -classpath G:\softDevelopment\ideaIU-2019.3.5\ideaIU-2019.3.5.win\lib\idea_rt.jar;G:\softDevelopment\ideaIU-2019.3.5\ideaIU-2019.3.5.win\plugins\junit\lib\junit5-rt.jar;G:\softDevelopment\ideaIU-2019.3.5\ideaIU-2019.3.5.win\plugins\junit\lib\junit-rt.jar;C:\JDK8\jre\lib\charsets.jar;C:\JDK8\jre\lib\deploy.jar;C:\JDK8\jre\lib\ext\access-bridge-64.jar;C:\JDK8\jre\lib\ext\cldrdata.jar;C:\JDK8\jre\lib\ext\dnsns.jar;C:\JDK8\jre\lib\ext\jaccess.jar;C:\JDK8\jre\lib\ext\jfxrt.jar;C:\JDK8\jre\lib\ext\localedata.jar;C:\JDK8\jre\lib\ext\nashorn.jar;C:\JDK8\jre\lib\ext\sunec.jar;C:\JDK8\jre\lib\ext\sunjce_provider.jar;C:\JDK8\jre\lib\ext\sunmscapi.jar;C:\JDK8\jre\lib\ext\sunpkcs11.jar;C:\JDK8\jre\lib\ext\zipfs.jar;C:\JDK8\jre\lib\javaws.jar;C:\JDK8\jre\lib\jce.jar;C:\JDK8\jre\lib\jfr.jar;C:\JDK8\jre\lib\jfxswt.jar;C:\JDK8\jre\lib\jsse.jar;C:\JDK8\jre\lib\management-agent.jar;C:\JDK8\jre\lib\plugin.jar;C:\JDK8\jre\lib\resources.jar;C:\JDK8\jre\lib\rt.jar;E:\Server\MyBatis202108\MyBatis20210805\target\test-classes;E:\Server\MyBatis202108\MyBatis20210805\target\classes;C:\Users\bruceliu\.m2\repository\org\mybatis\mybatis\3.4.5\mybatis-3.4.5.jar;C:\Users\bruceliu\.m2\repository\mysql\mysql-connector-java\8.0.15\mysql-connector-java-8.0.15.jar;C:\Users\bruceliu\.m2\repository\com\google\protobuf\protobuf-java\3.6.1\protobuf-java-3.6.1.jar;C:\Users\bruceliu\.m2\repository\junit\junit\4.12\junit-4.12.jar;C:\Users\bruceliu\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;C:\Users\bruceliu\.m2\repository\org\projectlombok\lombok\1.16.18\lombok-1.16.18.jar com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.bruce.test.TestUser,test1
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 645482568.
==> Preparing: select * from user
==> Parameters:
<== Columns: id, username, birthday, sex, address
<== Row: 1, zhansgan, 2021-08-07, 1, 北京
<== Total: 1
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@26794848]
Returned connection 645482568 to pool.

开启侵入式延迟

<settings>
<!--延迟加载总开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--是否启用侵入式延迟加载-->
<!--3.4.1版本之前默认是true,之后默认是false-->
<setting name="aggressiveLazyLoading" value="true"/>
</settings>

测试方法

@Test
public void test1(){
User u=um.findUserById(1);
System.out.println(u.getUsername());
//List<Account> accounts = u.getAccounts();
}
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 1026055550.
==> Preparing: select * from user where id=?
==> Parameters: 1(Integer)
<== Columns: id, username, birthday, sex, address
<== Row: 1, zhansgan, 2021-08-07, 1, 北京
<== Total: 1
==> Preparing: select * from account where uid=?
==> Parameters: 1(Integer)
<== Columns: ID, UID, MONEY
<== Row: 1, 1, 2000.0
<== Row: 2, 1, 4000.0
<== Total: 2
zhansgan
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3d285d7e]
Returned connection 1026055550 to pool.

开启深度延迟加载

<settings>
<!--延迟加载总开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--是否启用侵入式延迟加载-->
<!--3.4.1版本之前默认是true,之后默认是false-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
@Test
public void test1(){
User u=um.findUserById(1);
System.out.println(u.getUsername());
//List<Account> accounts = u.getAccounts();
}
C:\JDK8\bin\java.exe -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:G:\softDevelopment\ideaIU-2019.3.5\ideaIU-2019.3.5.win\lib\idea_rt.jar=11939:G:\softDevelopment\ideaIU-2019.3.5\ideaIU-2019.3.5.win\bin -Dfile.encoding=UTF-8 -classpath G:\softDevelopment\ideaIU-2019.3.5\ideaIU-2019.3.5.win\lib\idea_rt.jar;G:\softDevelopment\ideaIU-2019.3.5\ideaIU-2019.3.5.win\plugins\junit\lib\junit5-rt.jar;G:\softDevelopment\ideaIU-2019.3.5\ideaIU-2019.3.5.win\plugins\junit\lib\junit-rt.jar;C:\JDK8\jre\lib\charsets.jar;C:\JDK8\jre\lib\deploy.jar;C:\JDK8\jre\lib\ext\access-bridge-64.jar;C:\JDK8\jre\lib\ext\cldrdata.jar;C:\JDK8\jre\lib\ext\dnsns.jar;C:\JDK8\jre\lib\ext\jaccess.jar;C:\JDK8\jre\lib\ext\jfxrt.jar;C:\JDK8\jre\lib\ext\localedata.jar;C:\JDK8\jre\lib\ext\nashorn.jar;C:\JDK8\jre\lib\ext\sunec.jar;C:\JDK8\jre\lib\ext\sunjce_provider.jar;C:\JDK8\jre\lib\ext\sunmscapi.jar;C:\JDK8\jre\lib\ext\sunpkcs11.jar;C:\JDK8\jre\lib\ext\zipfs.jar;C:\JDK8\jre\lib\javaws.jar;C:\JDK8\jre\lib\jce.jar;C:\JDK8\jre\lib\jfr.jar;C:\JDK8\jre\lib\jfxswt.jar;C:\JDK8\jre\lib\jsse.jar;C:\JDK8\jre\lib\management-agent.jar;C:\JDK8\jre\lib\plugin.jar;C:\JDK8\jre\lib\resources.jar;C:\JDK8\jre\lib\rt.jar;E:\Server\MyBatis202108\MyBatis20210805\target\test-classes;E:\Server\MyBatis202108\MyBatis20210805\target\classes;C:\Users\bruceliu\.m2\repository\org\mybatis\mybatis\3.4.5\mybatis-3.4.5.jar;C:\Users\bruceliu\.m2\repository\mysql\mysql-connector-java\8.0.15\mysql-connector-java-8.0.15.jar;C:\Users\bruceliu\.m2\repository\com\google\protobuf\protobuf-java\3.6.1\protobuf-java-3.6.1.jar;C:\Users\bruceliu\.m2\repository\junit\junit\4.12\junit-4.12.jar;C:\Users\bruceliu\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;C:\Users\bruceliu\.m2\repository\org\projectlombok\lombok\1.16.18\lombok-1.16.18.jar com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.bruce.test.TestUser,test1
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 1026055550.
==> Preparing: select * from user where id=?
==> Parameters: 1(Integer)
<== Columns: id, username, birthday, sex, address
<== Row: 1, zhansgan, 2021-08-07, 1, 北京
<== Total: 1
zhansgan
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3d285d7e]
Returned connection 1026055550 to pool.