MyBatis 延迟加载入门

引言

前面一篇文章,介绍了多表查询,在实际使用中,我们会经常性的涉及到多表联合查询,但是有时候,并不会立即用到所有的查询结果,我来举两个例子:

  • 例如,查询一批笔记本电脑的进货明细,而不直接展示每列明细对应电脑配置或者价格等的详细信息,等到用户需要取出某笔记本相关的详细信息的时候,再进行单表查询
  • 再例如 ,银行中,某个用户拥有50个账户(打比方),再我们查询这个而用户的信息,这个用户下所有账户的详细信息很显然,在使用的时候再查询才是比较合理的

针对这样一种情况,延迟加载这一种机制就出现了,延迟加载(懒加载)顾名思义,就是对某种信息推迟加载,这样的技术也就帮助我们实现了 “按需查询” 的机制,在一对多,或者多对多的情况下

既然提到了延迟加载,当然顺便提一句立即加载,它的含义就是不管是否用户需要,一调用,则马上查询,这种方式,适合与多对一,或者一对一的情况下

(一) 必要准备

首先,配置基本的环境,然后我们首先在数据库准备两张表

User表

CREATE TABLE USER (
 `id`			INT(11)NOT NULL AUTO_INCREMENT,
 `username` 	VARCHAR(32) NOT NULL COMMENT '用户名',
 `telephone`    VARCHAR(11) NOT NULL COMMENT '手机',
 `birthday`		DATETIME DEFAULT NULL COMMENT '生日',
 `gender`  		CHAR(1) DEFAULT NULL COMMENT '性别',
 `address` 		VARCHAR(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY  (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

Account表

CREATE TABLE `account` (
  `ID` int(11) NOT NULL COMMENT '编号',
  `UID` int(11) default NULL COMMENT '用户编号',
  `MONEY` double default NULL COMMENT '金额',
  PRIMARY KEY  (`ID`),
  KEY `FK_Reference_8` (`UID`),
  CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

然后分别创建出其对应的实体类

User类

public class User implements Serializable {
    private Integer id;
    private String username;
    private String telephone;
    private Date birthday;
    private String gender;
    private String address;
    //一对多关系映射,主表实体应该包含从表实体的集合引用
    private List<Account> accounts;
	...... 请补充 get set 和 toString 方法
}

Account类

public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private Double money;
    //从表实体应该包含一个主表实体的对象引用
    private User user;
    ...... 请补充 get set 和 toString 方法
}

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="cn.ideal.mapper.UserMapper">

    <!-- 定义User的resultMap-->
    <resultMap >
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="telephone" column="telephone"></result>
        <result property="birthday" column="birthday"></result>
        <result property="gender" column="gender"></result>
        <result property="address" column="address"></result>
        <collection property="accounts" ofType="account">
            <id property="id" column="aid"></id>
            <result property="uid" column="uid"></result>
            <result property="money" column="money"></result>
        </collection>
    </resultMap>
    
    <!-- 查询所有用户 并且显示对应账户信息 -->
    <select >
       SELECT u.*,a.id as aid,a.uid,a.money FROM user u LEFT OUTER JOIN account a on u.id = a.uid;
    </select>

    <!-- 根据id查询用户 -->
    <select >
        select * from user where id = #{uid}
    </select>
    
</mapper>

两个接口中创建对应方法

public interface AccountMapper {
    /**
     * 查询所有账户
     * @return
     */
    List<Account> findAll();
}
public interface UserMapper {
    /**
     * 查询所有用户信息,同时显示出该用户下的所有账户
     *
     * @return
     */
    List<User> findAll();

    /**
     * 根据id查询用户信息
     * @param userId
     * @return
     */
    User findById(Integer userId);
}

(一) 延迟加载代码实现

首先,给大家演示一下,我们之前一对一查询用户的方式,同时会将用户对应所有的账户信息,也查询出来

/**
* 测试查询所有
*/
@Test
public void testFindAll() {
	List<User> users= userMapper.findAll();
    for (User user : users) {
        System.out.println("---------------------");
        System.out.println(user);
        System.out.println(user.getAccounts());
    }
}

效果:

MyBatis 延迟加载入门

这种方式是通过 SQL 语句,以及resultMap将 用户和账户的信息同时查询出来

那么如何实现我们上面所说的延迟加载呢?

这次我们选择 查询账户,然后延迟加载用户的信息

(1) 修改AccountMapper.xml

首先需要修改的就是账户的映射配置文件,可以看到我们在查询时,依旧定义了一个 resultMap 先封装了 Account ,然后通过association 进行关联 User,其中使用的就是 select 和 column 实现了延迟加载用户信息

  • select 用来指定延迟加载所需要执行的 SQL 语句,也就是指定 某个SQL映射文件中的某个select标签对的 id,在这里我们指定了用户中通过id查询信息的方法
  • column 是指关联的用户信息查询的列,在这里也就是关联的用户的主键即,id
<mapper namespace="cn.ideal.mapper.AccountMapper">
	<!-- 定义封装 Account和User 的resultMap -->
    <resultMap >
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <!-- 配置封装 User 的内容
            select:查询用户的唯一标识
            column:用户根据id查询的时候,需要的参数值
        -->
        <association property="user" column="uid" javaType="User" select="cn.ideal.mapper.UserMapper.findById"></association>
    </resultMap>

    <!-- 根据查询所有账户 -->
    <select >
        SELECT * FROM account
    </select>
</mapper>

(2) 第一次测试代码

我们只执行一下账户的查询所有方法,看一下,是否能够实现我们的效果

@Test
public void testFindAll(){
    List<Account> accounts = accountMapper.findAll();
}

(3) 执行效果

MyBatis 延迟加载入门

可以看到,三条 SQL 语句都执行了,这是为什么呢?

这是因为,我们在测试方法之前,需要开启延迟加载功能

(4) 延迟加载功能

我们可以去官网,如何配置开启这样一个功能

MyBatis 延迟加载入门

经过查阅文档,我们知道了,如果想要开始延迟加载功能,就需要在总配置文件 SqlMapConfig.xml 中配置 setting 属性,也就是将延迟加载 lazyLoadingEnable 的开关设置成 teue ,由于是按需加载,所以还需要将积极加载修改为消极加载,也就是将 aggressiveLazyLoading 改为 false

当然,由于我这里导入的 MyBatis 版本为 3.4.5 所以这个值默认就是 false 实际上不用设置也可以,不过我们还是写出来

<settings>
	<setting name="lazyLoadingEnabled" value="true"/>
	 <setting name="aggressiveLazyLoading" value="false"></setting>
</settings>

注意:如果有使用typeAliases配置别名的话一定要将 typeAliases 标签放在后面

(5) 再次测试

仍然只执行查询方法

@Test
public void testFindAll(){
    List<Account> accounts = accountMapper.findAll();
}

执行效果

MyBatis 延迟加载入门

这一次果然只执行了一条查询 account 的命令

那么当用户想要查看到,每个账户对应下的用户的时候呢?这也就是按需查询,只需要在测试时,加入对应获取方法就可以了

@Test
public void testFindAll(){
    List<Account> accounts = accountMapper.findAll();
    for (Account account : accounts){
        System.out.println("----------------------------");
        System.out.println(account);
        System.out.println(account.getUser());
    }
}

执行一下

MyBatis 延迟加载入门

可以看到,我们延迟加载的目的达到了

总结

上面的测试,我们已经实现了延迟加载,简单的总结一下步骤:

  • ①:执行对应的 mapper 方法,也就是上例中执行 Mapper 中 id 值为 findAll 的对应 SQL配置,只查询到账户的信息

  • ②:在程序中,遍历查询到的 accounts ,调用 getUser() 方法时,开始进行延迟加载

    • List<Account> accounts = accountMapper.findAll();
  • ③:进行延迟加载,调用映射文件中 id 值为 findById 的对应 SQL配置,获取到对应用户的信息

可以看到,我们之前通过使用 左外连接等的 SQL书写方式,直接就可以查询到多张表

SELECT u.*,a.id as aid,a.uid,a.money FROM user u LEFT OUTER JOIN account a on u.id = a.uid;

但是我们可以通过延迟加载,实现我们按需查询的需求,综上所述,在使用的时候,先执行简单的 SQL,然后再按照需求加载查询其他信息

结尾

如果文章中有什么不足,欢迎大家留言交流,感谢朋友们的支持!

如果能帮到你的话,那就来关注我吧!如果您更喜欢微信文章的阅读方式,可以关注我的公众号

在这里的我们素不相识,却都在为了自己的梦而努力 ❤

一个坚持推送原创开发技术文章的公众号:理想二旬不止

MyBatis 延迟加载入门

相关文章: