Mybatis缓存及延迟加载策略
一:引言
通过前面几篇的文章介绍了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缓存及延迟加载策略的更多相关文章
- Mybatis 延迟加载策略
延迟加载: 就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据.延迟加载也称懒加载. 好处: 先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关联查询多张表速 ...
- mybatis探究之延迟加载和缓存
mybatis探究之延迟加载和缓存 一.什么是延迟加载 1.延迟加载的概念 在mybatis进行多表查询时,并非所有的查询都需要立即进行.例如在查询带有账户信息的用户信息时,我们们并不需要总是在加载用 ...
- 【MyBatis】MyBatis 延迟加载策略
MyBatis 延迟加载策略 文章源码 什么是延迟加载 延迟加载,就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据,也被成为懒加载. 好处:先从单表查询,需要时再从关联表去关联查询,大大提 ...
- Mybatis缓存 缓存配置文件 good
一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Se ...
- Mybatis缓存处理机制
一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Se ...
- MyBatis入门学习教程-MyBatis缓存
一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了 package me.gacl.test; 2 import me.gacl.domain.User; import ...
- MyBatis学习总结(七)——Mybatis缓存(转载)
孤傲苍狼 只为成功找方法,不为失败找借口! MyBatis学习总结(七)--Mybatis缓存 一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的 ...
- MyBatis学习总结(七)——Mybatis缓存
一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Se ...
- MyBatis学习总结(七)——Mybatis缓存
一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Se ...
随机推荐
- 搭建私有镜像仓库registry 2.0
搭建 docker run -d -p 5000:5000 --restart=always --name registry2 registry:2 就可以将自己的镜像 push到这个私有的镜像仓库 ...
- (Redis基础教程之六)如何使用Redis中的List
如何在ubuntu18.04上安装和保护redis 如何连接到Redis数据库 如何管理Redis数据库和Keys 如何在Redis中管理副本和客户端 如何在Redis中管理字符串 如何在Redis中 ...
- java 字符串转为list
List<String> idList = Arrays.asList(irIds.split(","));
- iOS私有api检测工具使用
背景:这两天提审了一款新的APP,由于项目中使用了老版本的TZImagePicker中访问了私有API,导致提审失败. 预审经验分享: https://baijiahao.baidu.com/s?id ...
- Hyperledger Fabric——balance transfer(四)安装和实例化chaincode
详细解析blance transfer示例的安装(install)和实例化(Instantiate)链码(chaincode)的过程.安装chaincode会根据本地的链码文件生成chaincode镜 ...
- 技术大佬:我去,你竟然还不会用 this 关键字
上一篇文章写的是 Spring Boot 的入门,结果有读者留言说,Java 都还没搞完,搞什么 Spring Boot,唬得我一愣一愣的.那这篇就继续来搞 Java,推出广受好评的我去系列第四集:你 ...
- Spring MVC基于注解@Controller和@RequestMapping开发的一个例子
1.创建web项目 2.在springmvc的配置文件中指定注解驱动,配置扫描器 在 Spring MVC 中使用扫描机制找到应用中所有基于注解的控制器类,所以,为了让控制器类被 Spring MVC ...
- 彻底理解JavaScript ES6中的import和export
0.前言 前端工程,在最早的时候是没有模块的概念的.随着前端工程的发展,前端开发也越来越规范化,更像是软件工程了.那么随之而来的,为了解决工程化的问题,就引入了模块的概念.但是在早期,因为ecmasc ...
- Docker 笔记一相关命令
Centos 7 : Service network restart 重启网络 Ip addr 查看ip地址 Uname -r 查看内核版本 Yum install docker 安装docker 命 ...
- python argparse总结
python2.7废除optparse,原因:http://code.google.com/p/argparse/ 说了半天好像是重开了个小号练级 抓紧写下来一会又得忘了 用法: import arg ...