一文看懂Mybatis中的延迟加载

 更新时间:2023年10月27日 10:20:15   作者:来日浅谈  
这篇文章主要介绍了一文看懂Mybatis中的延迟加载,延迟加载也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询,MyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的,需要的朋友可以参考下

1. 延迟加载简介

MyBatis中的延迟加载,也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询。例如在进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库压力。MyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的。

好处:

先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。

坏处:

因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

2. 关联对象加载时机

MyBatis根据对关联对象查询的select语句的执行时机,分为三种类型:直接加载、侵入式延迟加载与深度延迟加载。

  • 直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询。
  • 侵入式延迟: 执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。
  • 深度延迟: 执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。

需要注意的是, 延迟加载的应用要求,关联对象的查询与主加载对象的查询必须是分别进行的 select 语句,不能是使用多表连接所进行的select查询。因为多表连接查询,其实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。

MyBatis中对于延迟加载设置,只对于resultMap中的collection和association起作用,可以应用到一对一、一对多、多对一、多对多的所有关联关系查询中。

下面以一对多关联关系查询为例,讲解 MyBatis 中的延迟加载应用。

3. 直接加载

主配置文件SqlMapConfig.xml中设置lazyLoadingEnabled为false,默认为false。(因为默认式false,所以也可以不设置lazyLoadingEnabled)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <!-- 配置properties -->
    <properties resource="jdbcConfig.properties"></properties>
    
    <!-- 配置参数 -->
    <settings>
        <!-- 关闭mybatis支持延迟加载 -->
        <setting name="lazyLoadingEnabled" value="false"/>
    </settings>
    
    <!-- 使用typeAliases配置别名,它只能配置domain中类的别名 -->
    <typeAliases>
        <package name="com.siyi.domain"></package>
    </typeAliases>
    <!-- 配置环境 -->
    <environments default="mysql">
        <!-- 配置mysql的环境 -->
        <environment id="mysql">
            <!-- 配置事务 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置连接池 -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 映射配置文件 -->
    <mappers>
        <mapper resource="com/siyi/dao/IUserDao.xml"/>
        <mapper resource="com/siyi/dao/IAccountDao.xml"/>
    </mappers>
</configuration>

dao映射配置文件

<?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.siyi.dao.IUSerDao">

    <!-- 定义User的resultMap -->
    <resultMap id="userAccountMap" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="address" column="address"/>
        <result property="sex" column="sex"/>
        <result property="birthday" column="birthday"/>
        <collection property="accounts" ofType="account" select="com.siyi.dao.IAccountDao.findAccountByUid" column="id"/>
    </resultMap>

    <!-- 查询所有 -->
    <select id="findAll" resultMap="userAccountMap">
        <!-- select * from user; -->
        select * from user
    </select>
</mapper>

测试

package com.siyi.test;

import com.siyi.dao.IUSerDao;
import com.siyi.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class UserTest {

    private InputStream in;
    private SqlSession session;
    private IUSerDao iuSerDao;

    @Before//用于在测试方法执行之前执行
    public void init() throws IOException {
        //1.读取配置文件生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取SqlSessionFactory对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.获取SqlSession对象
        session = factory.openSession(true);
        //4.获取DAO的代理对象
        iuSerDao = session.getMapper(IUSerDao.class);
    }

    @After//用于在测试方法之后执行
    public void destroy() throws IOException {
        //事务提交
        //session.commit();
        //释放资源
        session.close();
        in.close();
    }

    /**
     * 测试查询所有
     * @throws IOException
     */
    @Test
    public void testFindAll() throws IOException {
        //5.执行查询所有方法
        List<User> users = iuSerDao.findAll();
    }
}

运行过程中就查询了所有的信息:

4. 侵入式延迟加载

主配置文件SqlMapConfig.xml中设置lazyLoadingEnabled为true,默认为false,并且将aggressiveLazyLoading设置为true。(aggressiveLazyLoading默认值为false (在 3.4.1 及之前的版本中默认为 true))

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <!-- 配置properties -->
    <properties resource="jdbcConfig.properties"></properties>
    
    <!-- 配置参数 -->
    <settings>
        <!-- 开启mybatis支持延迟加载 -->
        <setting name="lazyLoadingEnabled" value="false"/>
        <setting name="aggressiveLazyLoading" value="true"/>
    </settings>
    
    <!-- 使用typeAliases配置别名,它只能配置domain中类的别名 -->
    <typeAliases>
        <package name="com.siyi.domain"></package>
    </typeAliases>
    <!-- 配置环境 -->
    <environments default="mysql">
        <!-- 配置mysql的环境 -->
        <environment id="mysql">
            <!-- 配置事务 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置连接池 -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 映射配置文件 -->
    <mappers>
        <mapper resource="com/siyi/dao/IUserDao.xml"/>
        <mapper resource="com/siyi/dao/IAccountDao.xml"/>
    </mappers>
</configuration>

dao映射配置文件不变

测试

package com.siyi.test;

import com.siyi.dao.IUSerDao;
import com.siyi.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class UserTest {

    private InputStream in;
    private SqlSession session;
    private IUSerDao iuSerDao;

    @Before//用于在测试方法执行之前执行
    public void init() throws IOException {
        //1.读取配置文件生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取SqlSessionFactory对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.获取SqlSession对象
        session = factory.openSession(true);
        //4.获取DAO的代理对象
        iuSerDao = session.getMapper(IUSerDao.class);
    }

    @After//用于在测试方法之后执行
    public void destroy() throws IOException {
        //事务提交
        //session.commit();
        //释放资源
        session.close();
        in.close();
    }

    /**
     * 测试查询所有
     * @throws IOException
     */
    @Test
    public void testFindAll() throws IOException {
        //5.执行查询所有方法
        List<User> users = iuSerDao.findAll();
    }
}

没有使用主对象的属性(详情)时,只执行了主对象的查询。

修改测试方法为

    @Test
    public void testFindAll() throws IOException {
        //5.执行查询所有方法
        List<User> users = iuSerDao.findAll();
		System.out.println(users.get(0).getUsername());
    }

在执行主对象属性(详情)的时候,执行关联对象:

5. 深度延迟加载

主配置文件SqlMapConfig.xml中设置lazyLoadingEnabled为true,默认为false,并且将aggressiveLazyLoading设置为false。(aggressiveLazyLoading默认值为false (在 3.4.1 及之前的版本中默认为 true))

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <!-- 配置properties -->
    <properties resource="jdbcConfig.properties"></properties>
    
    <!-- 配置参数 -->
    <settings>
        <!-- 开启mybatis支持延迟加载 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
    
    <!-- 使用typeAliases配置别名,它只能配置domain中类的别名 -->
    <typeAliases>
        <package name="com.siyi.domain"></package>
    </typeAliases>
    <!-- 配置环境 -->
    <environments default="mysql">
        <!-- 配置mysql的环境 -->
        <environment id="mysql">
            <!-- 配置事务 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置连接池 -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 映射配置文件 -->
    <mappers>
        <mapper resource="com/siyi/dao/IUserDao.xml"/>
        <mapper resource="com/siyi/dao/IAccountDao.xml"/>
    </mappers>
</configuration>

dao映射配置文件不变

测试

package com.siyi.test;

import com.siyi.dao.IUSerDao;
import com.siyi.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class UserTest {

    private InputStream in;
    private SqlSession session;
    private IUSerDao iuSerDao;

    @Before//用于在测试方法执行之前执行
    public void init() throws IOException {
        //1.读取配置文件生成字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取SqlSessionFactory对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.获取SqlSession对象
        session = factory.openSession(true);
        //4.获取DAO的代理对象
        iuSerDao = session.getMapper(IUSerDao.class);
    }

    @After//用于在测试方法之后执行
    public void destroy() throws IOException {
        //事务提交
        //session.commit();
        //释放资源
        session.close();
        in.close();
    }

    /**
     * 测试查询所有
     * @throws IOException
     */
    @Test
    public void testFindAll() throws IOException {
        //5.执行查询所有方法
        List<User> users = iuSerDao.findAll();
        System.out.println(users.get(0).getUsername());
    }
}

没有用到关联对象时,只查询主对象一次

修改测试方法

    @Test
    public void testFindAll() throws IOException {
        //5.执行查询所有方法
        List<User> users = iuSerDao.findAll();
        System.out.println(users.get(0).getAccounts());
    }

用到关联对象数据的时候执行关联对象的查询。

6. 延迟加载总结

延迟加载一般就是按需加载,在需要查询的时候再去查询,使用延迟加载可以避免表连接查询,表连接查询比单表查询的效率低,但是它需要多次与数据库进行交互,所以延迟加载使用需谨慎。

关于延迟加载有两个重要的设置:lazyLoadingEnabled表示延迟加载是否开启,如果设置为true表示开启,此时还需要设置aggressiveLazyLoading为false,才能做到按需加载,如果aggressiveLazyLoading设置为true则按需加载关闭,此时只要加载了某个属性就会将所有属性都加载。

lazyLoadingEnabled的默认值为false

aggressiveLazyLoading的默认值为true

注意: 延迟加载的应用要求,关联对象的查询与主加载对象的查询必须是分别进行的 select 语句,不能是使用多表连接所进行的select查询。

到此这篇关于一文看懂Mybatis中的延迟加载的文章就介绍到这了,更多相关Mybatis延迟加载内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 亲手教你SpringBoot中的多数据源集成问题

    亲手教你SpringBoot中的多数据源集成问题

    本文主要是介绍基于springboot的多数据源切换,轻量级的一种集成方案,对于小型的应用可以采用这种方案,我之前在项目中用到是因为简单,便于扩展以及优化,对SpringBoot多数据源集成问题感兴趣的朋友一起看看吧
    2022-03-03
  • Java 完美判断中文字符的方法

    Java 完美判断中文字符的方法

    Java判断一个字符串是否有中文一般情况是利用Unicode编码正则来做判断,但是其实这个区间来判断中文不是非常精确,以下是比较完善的判断方法
    2013-02-02
  • Java-String类最全汇总(下篇)

    Java-String类最全汇总(下篇)

    这篇文章主要介绍了Java-String类最全汇总(下篇),本文章内容详细,本模块分为了两部分,本次为下篇,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2023-01-01
  • java键盘录入的方法举例详解

    java键盘录入的方法举例详解

    这篇文章主要给大家介绍了关于java键盘录入的相关资料,我们在写程序的时候,数据值都是固定的,但是实际开发中,数据值肯定是变化的,所以,把数据改进为键盘录入,提高程序的灵活性,需要的朋友可以参考下
    2023-10-10
  • 从零开始:快速入门SpringBoot注解的精髓

    从零开始:快速入门SpringBoot注解的精髓

    Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架,它通过使用注解来简化配置和开发过程,使开发人员能够更加专注于业务逻辑的实现,Spring Boot提供了许多注解,用于定义和配置应用程序的各个方面,需要的朋友可以参考下
    2023-10-10
  • Kafka单节点伪分布式集群搭建实现过程详解

    Kafka单节点伪分布式集群搭建实现过程详解

    这篇文章主要介绍了Kafka单节点伪分布式集群搭建实现过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • Java中ExecutorService和ThreadPoolExecutor运行原理

    Java中ExecutorService和ThreadPoolExecutor运行原理

    本文主要介绍了Java中ExecutorService和ThreadPoolExecutor运行原理,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • spring初始化源码代码浅析

    spring初始化源码代码浅析

    Spring框架被广泛应用于我们的日常工作中,但是很长时间以来我们都是只会使用,不懂它的作用原理,下面这篇文章主要给大家介绍了关于spring初始化源码的相关资料,需要的朋友可以参考下
    2023-04-04
  • 深入理解Spring Aop的执行顺序

    深入理解Spring Aop的执行顺序

    本文将结合实例代码,介绍Spring Aop的执行顺序,非常具有实用价值,需要的朋友可以参考下
    2021-06-06
  • IDEA创建SpringBoot父子Module项目的实现

    IDEA创建SpringBoot父子Module项目的实现

    本文主要介绍了IDEA创建SpringBoot父子Module项目的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05

最新评论