9. MyBatis加载策略

Posted 海洋的渔夫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了9. MyBatis加载策略相关的知识,希望对你有一定的参考价值。

9. MyBatis加载策略

前言

在上一篇中我们已经熟悉了 MyBatis 的嵌套查询,而嵌套查询是通过多个单表查询多次执行来实现的。

不过在使用的过程中,有些时候我们希望嵌套查询只执行前面的一些SQL,不那么着急去执行后面所有的SQL,因为有时候不一定需要立即查询所有的结果出来。

那么要实现这个目标,我们就要来认识一下 MyBatis 的 加载策略

什么是加载策略

  • 当多个模型(表)之间存在关联关系时, 加载一个模型(表)的同时, 是否要立即加载其关联的模型, 我们把这种决策称为加载策略

  • 如果加载一个模型(表)的时候, 需要立即加载出其关联的所有模型(表), 这种策略称为立即加载

  • 如果加载一个模型的时候, 不需要立即加载出其关联的所有模型, 等到真正需要的时候再加载, 这种策略称为延迟加载(懒加载)

Mybatis中的加载策略有两种: 立即加载延迟加载, 默认立即加载

注意:延迟加载是在嵌套查询基础上实现的

加载的分类

  • 前提: 在嵌套查询基础上才有懒加载

  • 懒加载(lazy)需要了才去加载

  • 立即加载(eager)不论你是否需要,都是直接加载

下面我们先来看看该如何配置,最后再写一个案例来熟悉熟悉。

配置延迟加载

全局

官网文档  https://mybatis.org/mybatis-3/

SqlMapConfig.xml,设置开启全局延迟加载

 <!--全局配置: 写在properties标签之后,typeAliases标签之前 -->
    <settings>
        <!--开启延迟(懒)加载  true 开始  false(默认值) 关闭-->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>

局部

mapper映射文件,指定某一个select标签配置

<association></association> 标签
<collection></collection> 标签
 fetchType=""属性
  eager 立即加载
  lazy  延迟加载

注意:局部优先级高于全局的...

触发(立即)加载

有这样一个全局配置lazyLoadTriggerMethods, 定义的类方法会触发立即加载

也就说当你调用类定义的方法时, 会执行数据加载, 它的默认值是equals,clone,hashCode,toString,也就是说当调用某个类的方法(例如:user.equals user.toString)就会触发数据加载。

<!--全局配置-->
<settings>
    <!--开启延迟(懒)加载  true 开始  false(默认值) 关闭-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--触发立即加载的配置
            默认值:equals,clone,hashCode,toString
            value="" 覆盖了默认值,表示在执行上述四个方法时,不会触发立即加载...
   只有在执行orders.get方法获取时,触发数据加载...
        -->

    <setting name="lazyLoadTriggerMethods" value=""/>
</settings>

应用场景

  • 需求: 查询某个订单以及对应的用户 a. 联合查询: 直接查询两张表的结果 (表数据小) b. 嵌套查询: 分别查询订单 和 用户的数据 (表数据大)

  • 什么样的场景使用立即加载 查询订单的时候,需要立即知道订单所属的用户

  • 什么样的场景使用延迟加载(什么时候用,什么时候查询,提高数据库性能) 查询订单的时候,不许立即知道订单所属的用户

默认加载(立即加载)演示

  • 1.回到我们上一篇嵌套查询的 订单 Orders 与 用户 Users 的一对一嵌套查询,测试方法执行如下:
image-20210324141222980
@Test
public void test02(){
    SqlSession session = MyBatisUtil.getSqlSession();

    OrdersMapper mapper = session.getMapper(OrdersMapper.class);
    // 订单编号 = 1, 查询该订单的信息以及对应的用户信息
    Orders orders = mapper.findOrderByIdWithUser(1);

    MyBatisUtil.commitAndClose(session);
}

从上面的结果来看,我们只需要输出 订单 orders 的信息,但是嵌套查询也把 user 的信息执行查询出来了。

疑问:那么如果我们只想查询 orders 的信息,并不想立即把 user 的信息进行查询,只有当需要使用 user 信息的时候,才进行查询。该怎么做呢?

下面我们来配置一下 全局延迟加载。

全局延迟加载

  • 1.在 SqlMapConfig.xml,设置开启全局延迟加载。
9. MyBatis加载策略
image-20210324140533981
<!--全局配置: 写在properties标签之后,typeAliases标签之前 -->
<settings>
    <!--开启延迟(懒)加载  true 开始  false(默认值) 关闭-->
    <setting name="lazyLoadingEnabled" value="true"/>
</settings>
  • 2.配置完毕全局延迟加载之后,我们再次执行嵌套查询,看看执行的 SQL 情况:
9. MyBatis加载策略
image-20210324141359980
  • 3.打印 orders 信息,执行 orders 属性中的 user.toString() ,触发执行 user 用户信息的 查询

在上面我们可以看到并没有立即触发执行 user 用户信息的查询,那么下面我们来触发一下。如下:

9. MyBatis加载策略
image-20210324141759423

局部立即加载

在上面我们通过打印 user 信息来进行加载,那么我们又回到希望就算不打印 user 信息,却需要立即加载的需求呢?

我们可以配置 局部立即加载,如下:

9. MyBatis加载策略
image-20210324142345043

OrdersMapper.xml

<!--
    设置查询结果集 resultMap, 结果映射为 Orders 类
-->

<resultMap id="myorder" type="Orders" autoMapping="true">
    <!--  设置 id 字段映射 Orders 类的属性 id -->
    <id property="id" column="id"/>
    <!--  设置普通字段 ordertime 映射 Orders 类的属性 ordertime   -->
    <result property="ordertime" column="ordertime"/>
    <!--
        # 嵌套查询重点:
            0. 目的
                select * from user where id = ?
                    映射到 orders.user属性中
            1. 编写查询user表的语句:
                UserMapper.findUserById -> UserMapper.xml
            2. 嵌套到这里
                association标签的两个属性
                     a. column : 条件 (执行查询方法的参数)
                     b. select : 调用第二句sql执行
                        接口的权限定名.方法名
                        UserMapper.findUserById(用户id)
    -->

    <association property="user" javaType="user" autoMapping="true"
                 fetchType="eager"
                 column="uid"
                 select="com.lijw.dao.UserMapper.findUserById">

        <id property="id" column="uid"/>
        <result property="username" column="username"/>
    </association>
</resultMap>

<select id="findOrderByIdWithUser" resultMap="myorder">
    select * from orders where id = #{id}
</select>

触发(立即)加载

  • 1.在上面我们已经实现了局部立即加载,让我们回退一下。去除一下这个局部立即加载。
9. MyBatis加载策略
image-20210324193144819
  • 2.此时如果要加载查询 user 信息,只要打印 orders 信息,从而触发 orders 的属性 user.toString() 的方法
9. MyBatis加载策略
image-20210324193324029

可以看到此时已经触发了 user 的查询加载了。

那么为什么会触发呢?这是因为立即加载的方法默认值是equals,clone,hashCode,toString ,下面我们来修改一下这些方法,让其不立即加载来试试。

  • 3.修改立即加载的 类方法:修改默认方法,去除 toString() 方法
image-20210324193949217

sqlMapConfig.xml

<!--全局配置: 写在properties标签之后,typeAliases标签之前 -->
<settings>
    <!--开启延迟(懒)加载  true 开始  false(默认值) 关闭-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--触发立即加载的配置
        默认值:equals,clone,hashCode,toString
        value="" 覆盖了默认值,表示在执行上述四个方法时,不会触发立即加载...
        只有在执行orders.get方法获取时,触发数据加载...
    -->

    <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode"/>
</settings>
  • 4.使用 order.getUser() 方法触发 user 数据加载
image-20210324194218357


以上是关于9. MyBatis加载策略的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis08:MyBatis加载策略及缓存

Mybatis的延迟加载

超级有用的9个PHP代码片段

Mybatis从入门到精通系列 11——Mybatis 延迟加载与立即加载

课时9::MyBatis整合Log4j延迟加载

MyBatis专题-延迟加载