Mybatis 延迟加载策略
通过前面的学习,我们已经掌握了 Mybatis 中一对一,一对多,多对多关系的配置及实现,可以实现对象的关联查询。实际开发过程中很多时候我们并不需要总是在加载用户信息时就一定要加载他的账户信息。 此时就是我们所说的延迟加载。
1.1 何为延迟加载?
延迟加载:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
好处: 先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。 。
坏处: 因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。
前面实现多表操作时,我们使用了 resultMap 来实现一对一,一对多,多对多关系的操作。主要是通过 association、 collection 实现一对一及一对多映射。 association、 collection 具备延迟加载功能。
1.2 实现需求
需求:
查询账户(Account)信息并且关联查询用户(User)信息。如果先查询账户(Account)信息即可满足要求,当我们需要查询用户(User)信息时再查询用户(User)信息。把对用户(User)信息的按需去查询就是延迟加载。
工程目录结构如下:
1.2.1 使用 Assocation 实现延迟加载
需求: 查询账户信息同时查询用户信息。
Account 实体类中加入一个 User 类的对象
public class Account { private Integer id; private Integer uid; private Double money; //加上一个用户对象,has a的关系 private User user; //get...set...toStrirng... }
第一步:只查询账户信息 的 DAO 接口
SQL:select * from account;
AccountMapper 类中添加查询账户信息的方法:
public interface AccountMapper { /*** * 查询账户信息 * @return */ List<Account> findAccounts(); }
第二步: AccountMapper.xml 映射文件
其中上面resultMap属性的值accountlayLoadUser,它是我们自定义的resultMap,具体如下:
- select: 填写我们要调用的 select 映射的 id
- column: 填写我们要传递给 select 映射的参数
<?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.itheima.mapper.AccountMapper"> <!--映射配置--> <resultMap id="accountLazyLoadUser" type="Account"> <id column="id" property="id"/> <result column="money" property="money"/> <result column="uid" property="uid"/> <!-- select:调用指定查询结点 column:调用指定查询时,将当前查询出的某列的结果作为参数传递过去 --> <association property="user" select="com.itheima.mapper.UserMapper.getUserByUserId" column="uid"></association> </resultMap> <!--查询账户信息--> <select id="findAccounts" resultMap="accountLazyLoadUser"> SELECT * FROM account </select> </mapper>
第三步:在UserMapper.xml映射文件中添加映射。
<select id="getUserByUserId" parameterType="int" resultType="User">
SELECT * from USER WHERE id=#{id} </select>
第四步:开启Mybatis的延迟加载策略
进入Mybaits的官方文档,找到settings的说明信息:
我们需要在Mybatis的配置文件SqlMapConfig.xml文件中添加延迟加载的配置。
<settings> <!--开启延迟加载--> <setting name="lazyLoadingEnabled" value="true"/> <!--当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载--> <setting name="aggressiveLazyLoading" value="false" /> </settings>
第四步:编写测试只查账户信息不查用户信息。
/**** * 测试懒加载 * 一对一 */ @Test public void testFindAccounts(){ List<Account> accounts = accountMapper.findAccounts(); }
测试结果如下:
我们发现,因为本次只是将Account对象查询出来放入List集合中,并没有涉及到User对象,所以就没有发出SQL语句查询账户所关联的User对象的查询。
第五步:测试加载账户信息同时加载用户信息
重新修改测试方法:
/**** * 测试懒加载 * 一对一 */ @Test public void testFindAccounts(){ List<Account> accounts = accountMapper.findAccounts(); for (Account account : accounts) { System.out.println(account.getUser()); } }
测试效果如下:
小结:
通过本示例,我们可以发现Mybatis的延迟加载还要有很明显效果,对于提升软件性能这是一个不错的手段。
实现的关键:association的配置
<association property="user" select="com.itheima.mapper.UserMapper.getUserByUserId" column="uid"></association>
1.2.2 使用Collection实现延迟加载
同样我们也可以在一对多关系配置的 结点中配置延迟加载策略。
结点中也有select属性,column属性。
需求:完成加载用户对象时,查询该用户所拥有的账户信息。
第一步:在User实体类中加入List 属性
public class User implements Serializable { private Integer id; //主键ID private String username; //用户名 private Date birthday; //用户生日 private String sex; //用户性别 private String address; //用户住址 private List<Account> accList; //get...set...toString... }
第二步:UserMapper接口
在UserMapper接口中添加查询所有用户信息的方法
public interface UserMapper { /*** * 查询用户列表 * @return */ List<User> findUserList(); }
第三步: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.itheima.mapper.UserMapper"> <!--userLazyLoadAccountListResultMap--> <resultMap id="userLazyLoadAccountListResultMap" type="User"> <id column="id" property="id" /> <result column="username" property="username" /> <result column="sex" property="sex" /> <result column="birthday" property="birthday" /> <result column="address" property="address" /> <!--一对多映射--> <collection property="accList" ofType="Account" column="id" select="com.itheima.mapper.AccountMapper.findAccountsByUid"></collection> </resultMap> <select id="getUserByUserId" parameterType="int" resultType="User"> SELECT * from USER WHERE id=#{id} </select> <!--findUserList--> <select id="findUserList" resultMap="userLazyLoadAccountListResultMap"> SELECT * FROM user </select> </mapper>
- 标签主要用于加载关联的集合对象
- select属性用于指定查询account列表的sql语句,所以填写的是该sql映射的id
- column属性用于指定select属性的sql语句的参数来源,上面的参数来自于user的id列,所以就写成id这一个字段名了
第四步:AccountMapper.xml映射文件加入如下结点
UserMapper.xml映射文件中的 标签的select属性的值就是来自这个文件的< select>的id的值.
<!--findAccountsByUid,根据用户ID查询用户账户信息--> <select id="findAccountsByUid" parameterType="int" resultType="Account"> SELECT * FROM account WHERE uid=#{uid} </select>
第五步:开启Mybatis的延迟加载
在Mybatis的配置文件SqlMapConfig.xml中添加延迟加载的配置。
<settings> <!--开启延迟加载--> <setting name="lazyLoadingEnabled" value="true"/> <!--当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载--> <setting name="aggressiveLazyLoading" value="false" /> </settings>
第六步:测试只加载用户信息
在测试类中加入测试方法,如下:
/*** * 测试一对多懒加载 */ @Test public void testFindUserList(){ //查询用户列表 List<User> users = userMapper.findUserList(); }
测试结果如下:
我们发现并没有加载Account账户信息。
第七步:测试加载用户信息同时还加载账户列表
/*** * 测试一对多懒加载 */ @Test public void testFindUserList(){ //查询用户列表 List<User> users = userMapper.findUserList(); for (User user : users) { System.out.println(user.getUsername()); System.out.println(user.getAccList()); } }
测试结果如下: