zoukankan      html  css  js  c++  java
  • 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="userAccountMap" type="User">
            <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 id="findAll" resultMap="userAccountMap">
           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 id="findById" parameterType="INT" resultType="User">
            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());
        }
    }
    

    效果:

    这种方式是通过 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="userAccountMap" type="Account">
            <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 id="findAll" resultMap="userAccountMap">
            SELECT * FROM account
        </select>
    </mapper>
    

    (2) 第一次测试代码

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

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

    (3) 执行效果

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

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

    (4) 延迟加载功能

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

    经过查阅文档,我们知道了,如果想要开始延迟加载功能,就需要在总配置文件 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();
    }
    

    执行效果

    这一次果然只执行了一条查询 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());
        }
    }
    

    执行一下

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

    总结

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

    • ①:执行对应的 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,然后再按照需求加载查询其他信息

    结尾

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

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

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

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

  • 相关阅读:
    从一个线上服务器警告谈谈backlog
    聊聊服务器的负载
    Apache 配置说明
    Apache 服务器性能评估
    php之apc浅探
    Linux 服务器 监控命令
    小白学习mysql 之 innodb locks
    小白学习mysql之存储过程的优劣分析以及接入控制
    小白学习mysql之索引初步
    小白学习mysql之优化基础(EXPLAIN的连接类型)
  • 原文地址:https://www.cnblogs.com/ideal-20/p/12292060.html
Copyright © 2011-2022 走看看