一:引言

通过前面几篇的文章介绍了Mybatis的一对一、一对多、多对多关系的配置及实现,可是大家发现了吗?在执行关联查询的时候,直接会把当前查询的主表里包含的副表也查询后封装到对象里,其实在实际开发中,很多时候我们并不需要总是在查询主表数据时就一定要加载它们的副表数据,此时我们就可以使用延迟加载。还有就是在介绍完延迟加载策略后会说明一下Mybatis的缓存机制。

准备工作【搭建一个快速的小框架

二:Mybatis延迟加载策略

1:什么是延迟加载

在用到副表数据的时候才进行数据加载,没使用到副表数据(就是Teacher对象里面包含List<Student> studnets属性为副表)时不会加载数据。所以说延迟加载也称懒加载

好处:先查询主表数据,后期用到副表数据时再从关联表中去查询数据,减少了一次性查询大量数据,大大提高了数据库的性能,因为查询单表要比关联查询多表速度快

坏处:因为只有在用到副表数据时才会进行数据库查询,如果有大批量数据查询时,因为查询工作也要消耗时间,所以可能会造成用户等待时间变成,体验下降

数据库中有四种表关系:一对多,多对一,一对一,多对多

  一对多,多对多:通常情况下我们采用延迟加载

  一对多,多对一:通常情况下我们采用立即加载(Mybatis没用多对一的概念,当一对一看)

2:问题阐述

假设在一对多表中。当我们有一个辅导员,他管理1000个学生,这就是典型的一对多,那我们在查询辅导员信息的时候要不要把关联的学生数据查出来呢?考虑性能就不需要查询,但是如果后期有需求,要打印辅导员对应的学生信息,这时候怎么办?在没设置延迟加载的查询时,只能再手动调用查询学生信息。

3:一对多的延迟加载

#####改造TeacherDao里面的teacher映射关系
<!--辅导员关系映射-->
<resultMap id="teacherMapper" type="teacher">
<id column="tid" property="id"></id>
<result column="tname" property="name"></result>
<result column="tsex" property="sex"></result>
<result column="tage" property="age"></result>
<result column="tsalary" property="salary"></result>
<result column="taddress" property="address"></result>
<collection property="students" ofType="student" column="tid" select="cn.xw.dao.StudentDao.findByTid"></collection>
</resultMap>
<!--
collection:集合查询(一对多查询、多对多查询)标签
property:Teacher里的属性字段 private List<Student> students 封装到当前字段
ofType:当前封装数据的类型
column:这个是数据库字段,说白了就是使用当前表的什么字段值去查找匹配对应的表数据 也可以理解外键和外键对应字段查询
select:调用StudentDao的查询标签方法
-->

可是这个写完也没出现延迟加载的效果呀

要查询官方文档会发现要设置Settings标签属性

#####改造SqlMapConfig.xml文件
<!--在properties 下面标签配置settings 保证顺序不会错-->
<!--配置settings标签属性-->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings> ######更改测试类的findByIdTeacher方法
//查询单个辅导员
@Test
public void findByIdTeacher(){
sqlSession = factory.openSession();
TeacherDao mapper = sqlSession.getMapper(TeacherDao.class);
Teacher teacher = mapper.findById(1);
System.out.println("获取辅导员信息 不打印对应的从表(副表)数据");
System.out.println(teacher.getName()+" "+teacher.getSex()+" "+teacher.getAddress());
System.out.println("开始打印对应的从表数据学生");
for (Student student:teacher.getStudents()){
System.out.println("学生数据:"+student);
return;
}
}

4:一对一延迟加载(建议不使用延迟加载)

其实一对一的延迟加载和一对多的延迟加载差不多,只是标签使用的不同,我假设一个学生都对应了一个家庭表信息,那一个家庭表信息对应一个学生,简单的一对一,接下来我来展示一下延迟加载,我先对代码进行一些改造。

##### 因为基本的查询单个学生的代码已经写好了,只是缺少一个测试

    //根据id查询学生id
@Test
public void findByIdStudent(){
sqlSession = factory.openSession();
StudentDao mapper = sqlSession.getMapper(StudentDao.class);
Student student = mapper.findById(1);
System.out.println("打印学生的基本信息");
System.out.println(student.getName()+" "+student.getAge()+" "+student.getAddress());
System.out.println("打印家庭信息");
System.out.println(student.getFamily());
}

开始对一对一延迟加载的演示

##### 开始对StudentDao.xml文件的配置进行修改

<!--配置学生类映射关系-->
<resultMap id="studentMapper" type="student">
<id column="sid" property="id"></id>
<result column="sname" property="name"></result>
<result column="ssex" property="sex"></result>
<result column="sage" property="age"></result>
<result column="scredit" property="credit"></result>
<result column="smoney" property="money"></result>
<result column="saddress" property="address"></result>
<result column="senrol" property="enrol"></result>
<association property="family" javaType="family" column="fid" select="cn.xw.dao.FamilyDao.findById"></association>
</resultMap>
<!--
association:关联查询(一对一查询、多对一查询)标签
property:Student里的属性字段 private Family family 封装到当前字段
ofType:当前封装数据的类型
column:这个是数据库字段,说白了就是使用当前表的什么字段值去查找匹配对应的表数据 也可以理解外键和外键对应字段查询 当前表的fid对应从表的fid
select:调用FamilyDao的查询指定标签方法
-->

出现这个问题是因为我们没用加settings配置 和上面的一对多配置是一样的

<!--在properties 下面标签配置settings 保证顺序不会错-->
<!--配置settings标签属性-->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

五:小结

  Mybatis的延迟加载策略是在多表查询的关联嵌套查询集合嵌套查询的基础上展开的,总的来说一对多推荐延迟加载,但是一对一就不建议了,因为一对一后面的从表数据也就一条,使用前面的关联嵌套结果查询就可以了,就是说直接写多表连接查询然后直接映射结果,省去了来回查询的步骤

三:Mybatis缓存

1:什么是Mybatis缓存

缓存的英文名称叫cache,数据存在内存之中,用于零时存储,但是项目停止或者断电将失去缓存数据。使用缓存可以减少与数据库的交互次数,提高执行效率,但是经常发送改变的数据不推荐缓存,Mybatis也为我们提供了一级缓存和二级缓存

2:一级缓存

一级缓存是存在与SqlSession对象里面的,只要SqlSession没用使用flush或者close,它就会存在缓存

##### 我们针对测试类的findAllStudent方法进行修改   

 //查询全部学生
@Test
public void findAllStudent() {
//被初始化了一个SqlSession的一级缓存对象
sqlSession = factory.openSession();
//第一个代理对象
StudentDao mapper1 = sqlSession.getMapper(StudentDao.class);
List<Student> students1 = mapper1.findAll();
System.out.println("打印第一个查询出来的hashcode"+students1.hashCode());
//第二个代理对象
StudentDao mapper2 = sqlSession.getMapper(StudentDao.class);
List<Student> students2 = mapper2.findAll();
System.out.println("打印第二个查询出来的hashcode"+students2.hashCode());
System.out.println("比较2个对象是不是同一个");
System.out.println(students1==students2);
}

那怎么样才可以让一级缓存消失呢?

清除一级缓存:

当SqlSession调用修改update、删除delete、添加insert、commint()、close()、clearCache()等会清除一级缓存

clearCache是清除缓存的方法

    //查询全部学生
@Test
public void findAllStudent() {
//被初始化了一个SqlSession的一级缓存对象
sqlSession = factory.openSession();
//第一个代理对象
StudentDao mapper1 = sqlSession.getMapper(StudentDao.class);
List<Student> students1 = mapper1.findAll();
System.out.println("打印第一个查询出来的hashcode"+students1.hashCode()); //清除缓存
sqlSession.clearCache(); //第二个代理对象
StudentDao mapper2 = sqlSession.getMapper(StudentDao.class);
List<Student> students2 = mapper2.findAll();
System.out.println("打印第二个查询出来的hashcode"+students2.hashCode());
System.out.println("比较2个对象是不是同一个");
System.out.println(students1==students2);
} /**
* 注意 一级缓存SqlSession对象在当前的实例对象SqlSession对象内部起作用
* 什么意思呢?就是说通过factory工厂创建了多个SqlSession对象,它们之间的一级缓存
* 互不干扰,在SqlSessionA的一级缓存里面操作数据增删改及清除缓存都不会影响到
* SqlSessionB的一级缓存
*/

清除一级缓存

  所以总的来说,如果想要清除一级缓存之间使用clearCache方法或者commit方法即可,但是如果又想保留当前SqlSession缓存,又要进行增删改操作,那么避开的方法就是定义别的方法和重写使用工厂开辟一个SqlSession实例,然后在那边操作

3:二级缓存

二级缓存是存在与mapper映射级别的缓存(如:xxDao.xml),多个SqlSession去操作同一个Mapper映射的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的

首先要实现二级缓存是要手都配置属性和标签,否则不会出现二级缓存

注:首先要再SqlMapConfig.xml文件里配置stettings标签
<!--在properties 下面标签配置settings 保证顺序不会错-->
<!--配置settings标签属性 cacheEnabled默认是true 可以不配置-->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings> ++++++++++++++++++++++++++++++++++++++++++ 在Teacher实体类序列化 实现Serializable接口 public class Teacher implements Serializable {
.......
} ++++++++++++++++++++++++++++++++++++++++++ 在TeacherDao.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="cn.xw.dao.TeacherDao">
<!--一定要配置此属性 代表这个Mapper开启二级缓存-->
<cache></cache> <!--辅导员关系映射-->
<resultMap id="teacherMapper" type="teacher">
<id column="tid" property="id"></id>
<result column="tname" property="name"></result>
<result column="tsex" property="sex"></result>
<result column="tage" property="age"></result>
<result column="tsalary" property="salary"></result>
<result column="taddress" property="address"></result>
</resultMap> <!--查询全部辅导员信息--><!--一定要设置useCade="true"-->
<select id="findAll" resultMap="teacherMapper" useCache="true">
select * from teacher;
</select> <!--查询单个辅导员信息 根据id-->
<select id="findById" parameterType="Integer" resultMap="teacherMapper">
select * from teacher where tid=#{id};
</select>
</mapper>

 问题一:为什么要在指定的select语句标签里设置useCache=“true”?

  因为在Dao.xml设置<cache/>标签代表这个mapper可以使用二级缓存了,但是这个Dao.xml里的哪些查询语句要有二级缓存呢?所以通过useCache属性来区别,因为我有的查询要进行二级缓存,有的重要数据,经常更新的数据就不能有二级缓存,而哪些增删改就不能二级缓存,也没意义,而且也没有useCache属性

问题二:为什么当前的mapper的数据有缓存后,而查询的hashcode和对象比较都不一样呢?

  因为二级缓存和一级缓存不一样,一级缓存是缓存其Map结果,而Map里面包含查询的对象,如{ "key1" , new Student("小王","男",25)};而二级缓存结果也是Map,但是缓存的是散装数据,如{ "key1" , "小王" },{ "key2" , "小张" }

												

Mybatis缓存及延迟加载策略的更多相关文章

  1. Mybatis 延迟加载策略

    延迟加载: 就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据.延迟加载也称懒加载. 好处: 先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速 ...

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

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

  3. 【MyBatis】MyBatis 延迟加载策略

    MyBatis 延迟加载策略 文章源码 什么是延迟加载 延迟加载,就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据,也被成为懒加载. 好处:先从单表查询,需要时再从关联表去关联查询,大大提 ...

  4. Mybatis缓存 缓存配置文件 good

    一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Se ...

  5. Mybatis缓存处理机制

    一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Se ...

  6. MyBatis入门学习教程-MyBatis缓存

    一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了 package me.gacl.test; 2 import me.gacl.domain.User; import ...

  7. MyBatis学习总结(七)——Mybatis缓存(转载)

      孤傲苍狼 只为成功找方法,不为失败找借口! MyBatis学习总结(七)--Mybatis缓存 一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的 ...

  8. MyBatis学习总结(七)——Mybatis缓存

    一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Se ...

  9. MyBatis学习总结(七)——Mybatis缓存

    一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Se ...

随机推荐

  1. mysql批量修改

    update odr_order_base INNER JOIN (select merchant_id,order_base_id from odr_order_commodity) b on od ...

  2. async/await的语法和使用

    1. async 函数     (1)函数的返回值为promise对象     (2)promise对象的结果由async函数执行的返回值决定 2. await 表达式     (1)await右侧的 ...

  3. 「雕爷学编程」Arduino动手做(27)——BMP280气压传感器

    37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的.鉴于本人手头积累了一些传感器和模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里 ...

  4. 王艳 201771010127《面向对象程序设计(java)》第十七周学习总结

    实验十七  线程同步控制 实验时间 2018-12-10 一.理论部分 1.线程同步:多线程并发运行不确定性问题解决方案:引入线程同步机制,使得另一线程要使用该方法,就只能等待. 解决方案: 1)锁对 ...

  5. poj1780欧拉回路

    转载 #include<cstdio> #include<cstring> ; bool vis[N]; char ans[N]; int main() { int n; wh ...

  6. vscode环境配置(一)——C Program运行

    ctrl + shift +p 打开应用商店 搜索 C/C++  和 Code Runner(一键编译运行)  

  7. CICD:Jenkins入门和使用

    最近,我们使用的开发服务器被回收了,换了一台新的服务器,CI/CD平台需要重新搭建. 我的运维能力一直薄弱,所以借此机会学习了一番如何使用Jenkins进行持续集成开发和部署,实践并踩了一些坑,在此记 ...

  8. 使用Buildpacks高效构建Docker镜像

    1. 前言 Spring Boot 2.3.0.RELEASE 正式发布了几天了,其中有个新的特性:可以将Spring Boot应用代码直接打包为Docker镜像.这是什么科技?我赶紧去官网查了一番才 ...

  9. 【转】团队项目的Git分支管理规范

    原文地址: http://blog.jboost.cn/git-branch.html 分支管理 创建项目时(一般是服务型项目,工具型或辅助型项目可以简单一些),会针对不同环境创建三个常设分支: de ...

  10. 一文带你了解nginx基础

    学习nginx,就要先了解什么是nginx,为什么使用nginx,最后才是了解怎么使用nginx nginx简介 nginx安装 一.Linux中安装nginx 二.Docker中安装nginx 三. ...