写点什么

Mybatis【20】-- Mybatis 延迟加载怎么处理?

发布于: 2021 年 02 月 23 日

注:代码已托管在GitHub上,地址是:https://github.com/Damaer/Mybatis-Learning ,项目是mybatis-16-lazyload,需要自取,需要配置 maven 环境以及mysql环境(sql语句在resource下的test.sql中),觉得有用可以点个小星星。


docsify文档地址在:https://damaer.github.io/Mybatis-Learning/#/


mybatis的懒加载,也称为延迟加载,是指在进行关联查询的时候,按照设置延迟规则推迟对关联对象的select查询,延迟加载可以有效的减少数据库压力。延迟加载只对关联对象有延迟设置,主加载对象都是直接执行查询语句的

关联对象加载类型

mybatis 的关联对象的查询 select 语句的执行时机,可以分为 3 类,直接加载,侵入式加载与深度延迟加载。


1.直接加载

执行完主加载对象的 select 语句,马上就会执行关联对象的 select 语句。

2.侵入式延迟加载

执行对主加载对象的查询时,不会执行关联对象的查询,但是当访问主加载对象的详情时,就会马上执行关联对象的 select 查询,也就是说关联对象的查询执行,侵入到了主加载对象的详情访问中,可以理解为,将关联对象的详情侵入到主加载对象的详情中,作为它的一部分出现了。

3.深度延迟加载

执行对主加载对象的查询的时候,不会执行对关联对象的查询,访问主加载对象的详情的时候,也不会执行关联对象的 select 查询,只有当真正的访问关联对象的详情的时候,才会执行对关联对象的 select 查询。<br>


注意:延迟加载的最基本要求,关联对象的查询与主加载对象的查询必须是分别放在两个语句中的,不能使用多表连接查询,因为多表连接查询相当于把多张表连接成一张表的查询,无法做到分开查询,会一次性将表的内容查询出来。

延迟加载,可以应用到一对多,一对一,多对一,多对多的关联查询中。


举个例子:我们使用上一个demo,查询ministercountry之间的关系,数据库如下:


#创建数据库CREATE DATABASE `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;#创建数据表CREATE TABLE `test`.`student` ( `sid` INT(10) NOT NULL AUTO_INCREMENT ,`sname` VARCHAR(20) NOT NULL ,PRIMARY KEY(`sid`)) ENGINE = MyISAM;CREATE TABLE `test`.`course` ( `cid` INT(10) NOT NULL AUTO_INCREMENT ,`cname` VARCHAR(20) NOT NULL ,PRIMARY KEY(`cid`)) ENGINE = MyISAM;CREATE TABLE `test`.`middle` (`id` INT(10) NOT NULL AUTO_INCREMENT ,`studentId` INT(10) NOT NULL ,`courseId` INT(10) NOT NULL ,PRIMARY KEY(`id`)) ENGINE = MyISAM;#初始化数据表INSERT INTO `course` (`cid`, `cname`) VALUES ('1', 'JAVA') ;INSERT INTO `course` (`cid`, `cname`) VALUES ('2', 'C++') ;INSERT INTO `course` (`cid`, `cname`) VALUES ('3', 'JS') ;
INSERT INTO `student` (`sid`, `sname`) VALUES ('1', 'Jam') ;INSERT INTO `student` (`sid`, `sname`) VALUES ('2', 'Lina') ;
INSERT INTO `middle` (`id`, `studentId`, `courseId`) VALUES ('1', '1', '1');INSERT INTO `middle` (`id`, `studentId`, `courseId`) VALUES ('2', '1', '2');INSERT INTO `middle` (`id`, `studentId`, `courseId`) VALUES ('3', '2', '1');INSERT INTO `middle` (`id`, `studentId`, `courseId`) VALUES ('4', '2', '3');
复制代码

与之对应的实体类:Country.class

public class Country {	private Integer cid;	private String cname;
private Set<Minister> ministers; public Integer getCid() { return cid; } public void setCid(Integer cid) { this.cid = cid; } public String getName() { return cname; } public void setName(String cname) { this.cname = cname; } public Set<Minister> getMinisters() { return ministers; } public void setMinisters(Set<Minister> ministers) { this.ministers = ministers; } @Override public String toString() { return "Country [cid=" + cid + ", cname=" + cname + ", ministers=" + ministers + "]"; }}
复制代码

Minister.class:领导人实体类

public class Minister {	private Integer mid;	private String mname;	@Override	public String toString() {		return "Minister [mid=" + mid + ", mname=" + mname + "]";	}	public Integer getMid() {		return mid;	}	public void setMid(Integer mid) {		this.mid = mid;	}	public String getMname() {		return mname;	}	public void setMname(String mname) {		this.mname = mname;	}	}
复制代码

主配置文件mybatis.xml

<?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 resource="jdbc_mysql.properties">
</properties> <settings> <setting name="lazyLoadingEnabled" value="false"/> <!--<setting name="aggressiveLazyLoading" value="false"/>--> </settings> <!-- 别名,对数据对象操作全名太长,需要使用别名 --> <typeAliases> <!--<typeAlias type="bean.Student" alias="Student"/>--> <!--直接使用类名即可,对于整个包的路径配置(别名),简单快捷 --> <package name="beans"/> </typeAliases> <!-- 配置运行环境 --> <!-- default 表示默认使用哪一个环境,可以配置多个,比如开发时的测试环境,上线后的正式环境等 --> <environments default="mysqlEM"> <environment id="mysqlEM"> <transactionManager type="JDBC"> </transactionManager> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.user}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <!-- 注册映射文件 --> <mappers> <mapper resource="mapper/mapper.xml"/> </mappers></configuration>
复制代码

mapper.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="dao.ICountryDao">    <!-- 	resultMap 能解决字段和属性不一样的问题 -->    <!-- 以后用得比较多 ,是因为可以使用延迟加载-->    <!-- 嵌套查询 -->    <select id="selectMinisterByCountry" resultType="Minister">	select mid,mname from minister where countryId=#{ooo}	</select>    <resultMap type="Country" id="countryMapper">        <id column="cid" property="cid"/>        <result column="cname" property="cname"/>        <!-- country中有一个成员变量是ministers,它的泛型是Minister -->        <collection property="ministers"                    ofType="Minister"                    select="selectMinisterByCountry"                    column="cid">        </collection>    </resultMap>    <select id="selectCountryById" resultMap="countryMapper">		select cid,cname		from country		where		cid=#{cid}	</select></mapper>
复制代码

与之对应的sql接口:

public interface ICountryDao {	Country selectCountryById(int cid);}
复制代码

使用到的工具类:

public class MyBatisUtils {  private static SqlSessionFactory sqlSessionFactory;
public static SqlSession getSqlSession() { InputStream is; try { is = Resources.getResourceAsStream("mybatis.xml"); if (sqlSessionFactory == null) { sqlSessionFactory = new SqlSessionFactoryBuilder().build(is); } return sqlSessionFactory.openSession(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; }}
复制代码

直接加载查询

关于懒加载的配置,我们只需要在mybatis.xml文件里面使用<settings></settings>就可以了,懒加载有一个总开关,lazyloadingEnabled,只要置为false就可以将延迟加载关掉,那就是直接加载查询了。配置在<properties></properties><typeAliases></typeAliases>之间。

    <properties resource="jdbc_mysql.properties">    </properties>    <settings>        <setting name="lazyLoadingEnabled" value="flase"/>    </settings>    <!-- 别名,对数据对象操作全名太长,需要使用别名 -->    <typeAliases>        <!--<typeAlias type="bean.Student" alias="Student"/>-->        <!--直接使用类名即可,对于整个包的路径配置(别名),简单快捷 -->        <package name="bean"/>    </typeAliases>
复制代码

当单元测试是直接査country对象的时候:

	@Test	public void TestselectCountryById(){		Country country=dao.selectCountryById(1);	}
复制代码

结果是,我们可以看到两条sql,除了查询country之外,连同minister关联对象也一起查询了,这就是直接加载,简单粗暴,不管是否使用,都会先将关联查询加载:

[service] 2018-07-17 09:59:00,796 - dao.ICountryDao.selectCountryById -491  [main] DEBUG dao.ICountryDao.selectCountryById  - ==>  Preparing: select cid,cname from country where cid=? [service] 2018-07-17 09:59:00,823 - dao.ICountryDao.selectCountryById -518  [main] DEBUG dao.ICountryDao.selectCountryById  - ==> Parameters: 1(Integer)[service] 2018-07-17 09:59:00,838 - dao.ICountryDao.selectMinisterByCountry -533  [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - ====>  Preparing: select mid,mname from minister where countryId=? [service] 2018-07-17 09:59:00,838 - dao.ICountryDao.selectMinisterByCountry -533  [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - ====> Parameters: 1(Integer)[service] 2018-07-17 09:59:00,849 - dao.ICountryDao.selectMinisterByCountry -544  [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - <====      Total: 2[service] 2018-07-17 09:59:00,850 - dao.ICountryDao.selectCountryById -545  [main] DEBUG dao.ICountryDao.selectCountryById  - <==      Total: 1
复制代码

侵入式延迟加载

需要将延迟加载开关开启(true),同时也需要将侵入式加载开关开启(true

    <settings>        <setting name="lazyLoadingEnabled" value="true"/>        <setting name="aggressivelazyLoading" value="true"/>    </settings>
复制代码

1.当我们只查询country的时候,只会执行country的查询,不会执行关联查询minister


	@Test	public void TestselectCountryById(){		Country country=dao.selectCountryById(1);	}
复制代码

结果如下,只有一条 sql:

[service] 2018-07-17 14:30:55,471 - dao.ICountryDao.selectCountryById -902  [main] DEBUG dao.ICountryDao.selectCountryById  - ==>  Preparing: select cid,cname from country where cid=? [service] 2018-07-17 14:30:55,494 - dao.ICountryDao.selectCountryById -925  [main] DEBUG dao.ICountryDao.selectCountryById  - ==> Parameters: 1(Integer)[service] 2018-07-17 14:30:55,590 - dao.ICountryDao.selectCountryById -1021 [main] DEBUG dao.ICountryDao.selectCountryById  - <==      Total: 1
复制代码

当我们查询country的属性,但是不查询minister属性的时候:

	@Test	public void TestselectCountryById(){		Country country=dao.selectCountryById(1);		System.out.println(country.getCid());	}
复制代码

结果如下,会加载到关联对象minister,这就是侵入式延迟加载:

[service] 2018-07-17 14:32:37,959 - dao.ICountryDao.selectCountryById -724  [main] DEBUG dao.ICountryDao.selectCountryById  - ==>  Preparing: select cid,cname from country where cid=? [service] 2018-07-17 14:32:37,979 - dao.ICountryDao.selectCountryById -744  [main] DEBUG dao.ICountryDao.selectCountryById  - ==> Parameters: 1(Integer)[service] 2018-07-17 14:32:38,170 - dao.ICountryDao.selectCountryById -935  [main] DEBUG dao.ICountryDao.selectCountryById  - <==      Total: 1[service] 2018-07-17 14:32:38,171 - dao.ICountryDao.selectMinisterByCountry -936  [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - ==>  Preparing: select mid,mname from minister where countryId=? [service] 2018-07-17 14:32:38,171 - dao.ICountryDao.selectMinisterByCountry -936  [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - ==> Parameters: 1(Integer)[service] 2018-07-17 14:32:38,173 - dao.ICountryDao.selectMinisterByCountry -938  [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - <==      Total: 21
复制代码

深度延迟加载

需要将延迟加载开关lazyLoadingEnabled开启(true),同时需要将侵入式加载开关aggressivelazyLoading关闭(false

    <settings>        <setting name="lazyLoadingEnabled" value="true"/>        <setting name="aggressivelazyLoading" value="false"/>    </settings>
复制代码

1.当我们只查询出country的时候,只会查询country,而不会查询minister

单元测试代码:

	@Test	public void TestselectCountryById(){		Country country=dao.selectCountryById(1);	}
复制代码


[service] 2018-07-17 14:20:38,608 - dao.ICountryDao.selectCountryById -1271 [main] DEBUG dao.ICountryDao.selectCountryById  - ==>  Preparing: select cid,cname from country where cid=? [service] 2018-07-17 14:20:38,631 - dao.ICountryDao.selectCountryById -1294 [main] DEBUG dao.ICountryDao.selectCountryById  - ==> Parameters: 1(Integer)[service] 2018-07-17 14:20:38,980 - dao.ICountryDao.selectCountryById -1643 [main] DEBUG dao.ICountryDao.selectCountryById  - <==      Total: 1
复制代码

2.当我们访问country的属性的时候,也不会加载关联查询的minister

	@Test	public void TestselectCountryById(){		Country country=dao.selectCountryById(1);		System.out.println(country.getCid());	}
复制代码

结果同样是:

[service] 2018-07-17 14:24:03,004 - org.apache.ibatis.transaction.jdbc.JdbcTransaction -686  [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction  - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@cb51256][service] 2018-07-17 14:24:03,030 - dao.ICountryDao.selectCountryById -712  [main] DEBUG dao.ICountryDao.selectCountryById  - ==>  Preparing: select cid,cname from country where cid=? [service] 2018-07-17 14:24:03,078 - dao.ICountryDao.selectCountryById -760  [main] DEBUG dao.ICountryDao.selectCountryById  - ==> Parameters: 1(Integer)[service] 2018-07-17 14:24:03,160 - dao.ICountryDao.selectCountryById -842  [main] DEBUG dao.ICountryDao.selectCountryById  - <==      Total: 1
复制代码

3.当我们查询country属性 minister 的时候:

	@Test	public void TestselectCountryById(){		Country country=dao.selectCountryById(1);		System.out.println(country.getMinisters());	}
复制代码

我们可以看到结果,执行了minister的查询,这个时候才是真正的加载minister查询

[service] 2018-07-17 14:26:55,913 - dao.ICountryDao.selectCountryById -1540 [main] DEBUG dao.ICountryDao.selectCountryById  - ==>  Preparing: select cid,cname from country where cid=? [service] 2018-07-17 14:26:55,943 - dao.ICountryDao.selectCountryById -1570 [main] DEBUG dao.ICountryDao.selectCountryById  - ==> Parameters: 1(Integer)[service] 2018-07-17 14:26:56,161 - dao.ICountryDao.selectCountryById -1788 [main] DEBUG dao.ICountryDao.selectCountryById  - <==      Total: 1[service] 2018-07-17 14:26:56,162 - dao.ICountryDao.selectMinisterByCountry -1789 [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - ==>  Preparing: select mid,mname from minister where countryId=? [service] 2018-07-17 14:26:56,163 - dao.ICountryDao.selectMinisterByCountry -1790 [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - ==> Parameters: 1(Integer)[service] 2018-07-17 14:26:56,168 - dao.ICountryDao.selectMinisterByCountry -1795 [main] DEBUG dao.ICountryDao.selectMinisterByCountry  - <==      Total: 2[Minister [mid=2, mname=bbb], Minister [mid=1, mname=aaa]]
复制代码


加载方式 | lazyLoadingEnabled | aggressiveLazyLoading

---|---|---

直接加载 | 必须是 false,默认是 false | 不管是什么,只要 lazyLoadingEnabled 是 false 就是直接加载

侵入式延迟加载 | 必须是 true | 必须是 true

深度延迟加载 | 必须是 true | 必须是 false,默认是 false


【作者简介】

秦怀,公众号【秦怀杂货店】作者,技术之路不在一时,山高水长,纵使缓慢,驰而不息。个人写作方向:Java 源码解析,JDBC,Mybatis,Spring,redis,分布式,剑指 Offer,LeetCode 等,认真写好每一篇文章,不喜欢标题党,不喜欢花里胡哨,大多写系列文章,不能保证我写的都完全正确,但是我保证所写的均经过实践或者查找资料。遗漏或者错误之处,还望指正。


2020年我写了什么?


开源编程笔记


平日时间宝贵,只能使用晚上以及周末时间学习写作,关注我,我们一起成长吧~


发布于: 2021 年 02 月 23 日阅读数: 12
用户头像

纵使缓慢,驰而不息。 2018.05.17 加入

慢慢走,比较快。公众号:秦怀杂货店

评论

发布
暂无评论
Mybatis【20】-- Mybatis延迟加载怎么处理?