注:代码已托管在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查询。

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

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

举个例子:我们使用上一个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: 2
1

深度延迟加载

需要将延迟加载开关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年我写了什么?

开源编程笔记

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

Mybatis【20】-- Mybatis延迟加载怎么处理?的更多相关文章

  1. Mybatis 一对多延迟加载,并且子查询中与主表字段不对应 (19)

    Mybatis  一对多延迟加载,并且子查询中与主表字段不对应应用说明. 实现一对多关联(懒加载),一个教研组对应多个教师,既:教师的教研编号与教研组的教研编号关联,并且教师关联教研组外键与教研组编号 ...

  2. Mybatis中的延迟加载的使用方法

    Mybatis中的延迟加载的使用方法 在Mybatis中查询订单,然后带出商品信息和快递信息的配置方法 orderMapper.xml配置如下 <?xml version="1.0&q ...

  3. mybatis探究之延迟加载和缓存

    mybatis探究之延迟加载和缓存 一.什么是延迟加载 1.延迟加载的概念 在mybatis进行多表查询时,并非所有的查询都需要立即进行.例如在查询带有账户信息的用户信息时,我们们并不需要总是在加载用 ...

  4. MyBatis(2)——MyBatis 深入学习

    编写日志输出环境配置文件 在开发过程中,最重要的就是在控制台查看程序输出的日志信息,在这里我们选择使用 log4j 工具来输出: 准备工作: 将[MyBatis]文件夹下[lib]中的 log4j 开 ...

  5. 【Mybatis】MyBatis之Sql配置文件的使用(四)

    上一章[Mybatis]MyBatis对表执行CRUD操作(三),已经讲了基本操作,本章介绍Sql配置文件中常用功能 1.插入返回主键 2.参数值的获取方式 3.resultMap使用 插入返回主键 ...

  6. 【Mybatis】MyBatis配置文件的使用(二)

    本例在[Mybatis]MyBatis快速入门(一)基础上继续学习XML映射配置文件 MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置(settings)和属性(properti ...

  7. Hello Mybatis 02 mybatis generator

    接着上一篇文章通过Mybatis完成了一个User的CRUD的功能之后,这篇开始还需要建立一个Blog类,这样就可以模拟一个简单的微博平台的数据库了. 数据库准备 首先我们,还是需要在数据库中新建一个 ...

  8. 【Mybatis】MyBatis之动态SQL(六)

    MyBatis 的强大特性之一便是它的动态 SQL,本章介绍动态 SQL 查看本章,请先阅读[Mybatis]MyBatis对表执行CRUD操作(三). 本例表结构 CREATE TABLE `emp ...

  9. MyBatis笔记----MyBatis 入门经典的两个例子: XML 定义与注解定义

    ----致敬MyBatis官方开放文档让大家翻译,不用看书直接看文档就行了,mybatis的中文文档还需要完备的地方 简介 什么是 MyBatis ? MyBatis 是支持定制化 SQL.存储过程以 ...

  10. 【Mybatis】MyBatis对表执行CRUD操作(三)

    本例在[Mybatis]MyBatis配置文件的使用(二)基础上继续学习对表执行CRUD操作 使用MyBatis对表执行CRUD操作 1.定义sql映射xml文件(EmployeeMapper.xml ...

随机推荐

  1. SQL(replace)替换字段中指定的字符

    语法:update 表名 set 字段名=REPLACE(字段名,'修改前的字符','修改后的字符') 例 Product商品表中Name 名字字段中描述中将'AAA' 修改成 'BBB' SQL语句 ...

  2. Codeforces Round #594 (Div. 2) D1 - The World Is Just a Programming Task

    思路:枚举换的位置i,j 然后我们要先判断改序列能否完全匹配 如果可以 那我们就需要把差值最大的位置换过来 然后直接判断就行

  3. hdu4217 Data Structure?

    Problem Description Data structure is one of the basic skills for Computer Science students, which i ...

  4. [视频] 使用 JavaCV 来显示和保存来自摄像头的视频

    目录 JavaCV 预览本机摄像头视频图像的简单实现 预览的同时,保存为FLV文件 JavaCV javacv开发包是用于支持java多媒体开发的一套开发包,可以适用于本地多媒体(音视频)调用以及音视 ...

  5. 超详细 DNS 协议解析

    尽人事,听天命.博主东南大学研究生在读,热爱健身和篮球,正在为两年后的秋招准备中,乐于分享技术相关的所见所得,关注公众号 @ 飞天小牛肉,第一时间获取文章更新,成长的路上我们一起进步 本文已收录于 C ...

  6. Dapr是如何简化微服务的开发和部署

    基于微服务设计模式的现代应用程序面临着一系列挑战.微服务需要有一个强大的服务发现机制来实现动态连接.它们需要松散耦合,实现自主性和独立缩放.微服务需要支持多种语言,其中每个服务都是以最合适的语言.框架 ...

  7. 【非原创】LightOJ - 1284 Lights inside 3D Grid【概率期望】

    学习博客: 戳这里 戳这里 戳这里 戳这里 题意: 在一个三维的空间,每个点都有一盏灯,开始全是关的, 现在每次随机选两个点,把两个点之间的全部点,开关都按一遍:问k次过后开着的灯的期望数量: 题解: ...

  8. 美团CodeM资格赛第二题

    锦标赛 时间限制:1秒 空间限制:32768K 组委会正在为美团点评CodeM大赛的决赛设计新赛制.比赛有 n 个人参加(其中 n 为2的幂),每个参赛者根据资格赛和预赛.复赛的成绩,会有不同的积分. ...

  9. docker-compose -----单机多容器管理

    Compose是用于定义和运行多容器Docker应用程序的工具.通过Compose,您可以使用YAML文件来配置应用程序的服务.然后,使用一个命令,就可以从配置中创建并启动所有服务. Docker-C ...

  10. sdutoj2887

    #include <stdio.h> #include <math.h> int main(){ int px,tx;double alpha; int T;scanf(&qu ...