一:引言

通过前面几篇的文章介绍了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. Kubernetes学习笔记(二):部署托管的Pod -- 存活探针、ReplicationController、ReplicaSet、DaemonSet、Job、CronJob

    存活探针 Kubernetes可以通过存活探针(liveness probe)检查容器是否存活.如果探测失败,Kubernetes将定期执行探针并重新启动容器. 官方文档请见:https://kube ...

  2. mysql运维入门4:索引、慢查询、优化

    MySQL索引用来快速地寻找那些具有特定值的记录,所有MySQL索引都是以B-树的形式保存 如果没有索引,执行查询时,MySQL必须从第一个记录开始整表扫描,知道查询到符合要求的记录,记录越大,花费时 ...

  3. Kubernetes fabric8 JavaAPI

    Kubernetes fabric8 JavaAPI 一.依赖准备 <dependency> <groupId>io.fabric8</groupId> <a ...

  4. place-holder样式

    input::-webkit-input-placeholder, textarea::-webkit-input-placeholder { color: #777; } input:-moz-pl ...

  5. jquery-ui-i18n.js源码

    /* Afrikaans initialisation for the jQuery UI date picker plugin. */ /* Written by Renier Pretorius. ...

  6. Python 图像处理 OpenCV (5):图像的几何变换

    前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...

  7. opencv3学习1:opencv3.4.10与vs2017环境配置

    原教程网址:https://jingyan.baidu.com/article/dca1fa6f13bd55f1a44052b9.html 具体教程网上很多,我也相信大家的搜素能力,作为一个初入C++ ...

  8. Less定义变量

    1. 定义: 使用 @ 符号来定义变量 ,在Less中开头是 @ 则是变量,关于变量的命名方法,大家可以参考js中命名的规则,毕竟是做前端的,有着统一的习惯有助于我们统一风格.个人推荐变量名的命名规则 ...

  9. vue-cli3或者4中如何正确的使用public中的图片

    标题说的很清楚了,就是要使用public中的图片 那么为什么要把图片放到public中呢,其实官网上面也说了,要么是需要动态引入非常多的图片,特别是小图标,如果放在assert中的话,会被webpac ...

  10. 域对象的作用范围 & 请求的转发和重定向

    1. 和属性相关的方法: ①. 方法 void setAttribute(String name, Object o): 设置属性 Object getAttribute(String name): ...