java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Mybatis延迟加载

一文看懂Mybatis中的延迟加载

作者:来日浅谈

这篇文章主要介绍了一文看懂Mybatis中的延迟加载,延迟加载也称为懒加载,是指在进行表的关联查询时,按照设置延迟规则推迟对关联对象的select查询,MyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的,需要的朋友可以参考下

1. 延迟加载简介

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

好处:

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

坏处:

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

2. 关联对象加载时机

MyBatis根据对关联对象查询的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延迟加载内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文