MyBatis 一级二级缓存【学习记录】

news2024/12/24 20:19:13

一级缓存

1)首先做个测试,创建一个mapper配置文件和mapper接口,我这里用了最简单的查询来演示。

<mapper namespace="cn.elinzhou.mybatisTest.mapper.UserMapper">
    <select id="findUsers" resultType="cn.elinzhou.mybatisTest.pojo.User">
        SELECT * FROM user
    </select>
</mapper>
public interface UserMapper {
	List<User> findUsers() throws Exception;
}

然后编写一个单元测试:

public class UserMapperTest{
	SqlSession sqlSession = null;
	@Before
	public void setUp() throws Exception{
		//通过配置文件获取数据库连接信息
		Reader reader = Resources.getResourceAsReader("cn/elinzhou/mybatisTest/config/mybatis.xml");
		//通过配置信息构建一个SqlSessionFactory
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		//通过sqlSessionFactory打开一个数据库会话
		sqlSession = sqlSessionFactory.openSession();
	}
	@Test
	public void testFindUsers() throws Exception {
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		List<User> users = userMapper.findUsers();
		System.out.println(users);
	}
}

运行,可以看到控制台输出(先配好log4j)为类似如下图日志:(日志说明了该操作执行的sql语句已经查询的内容,最后一行是我手动通过System.out.println输出的结果)

在这里插入图片描述

然后再加一条语句 users = userMapper.findUsers(); ,单元测试代码如下:

@Test
public void testFindUsers() throws Exception {
	UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
	List<User> users = userMapper.findUsers();
	users = userMapper.findUsers();
	System.out.println(users);
}

也就是执行完 userMapper.findUsers(); 后立刻再执行一遍 userMapper.findUsers(); 可以想象,其实这两个操作执行的sql是完全相同的,而且在这期间没有对数据库进行过其他操作。执行该单元测试,发现打印的日志跟上面执行的sql是完全相同,也就是执行第二次 userMapper.findUasers(); 操作时没有对数据库进行查询,那么得到的数据是从哪里来的?答案是一级缓存。

MyBatis一级缓存是指在内存中开辟一块区域,用来保存用户对数据库的操作信息(sql)和数据库返回的数据,如果下一次用户再执行相同的请求,那么直接从内存中读取数据而不是从数据库读取。

其中数据的生命周期有两个影响因素:

1)对sqlSession执行commit操作时:

对sqlSeesion执行commit操作,也就意味着用户执行了update、delete等操作,那么数据库中的数据势必会发生变化,如果用户请求数据仍然使用之前内存中的数据,那么将读到脏数据。所以在执行sqlSession操作后,会清除保存数据的HashMap,用户在发起查询请求时就会重新读取数据并放入一级缓存中了。

在下面代码中增加 sqlSession.commit(); 后,再执行单元测试:

@Test
public void testFindUsers() throws Exception {
	UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
	List<User> users = userMapper.findUsers();
	sqlSession.commit();
	users = userMapper.findUsers();
	System.out.println(users);
}
2015-06-30 13:56:27,070 [main] DEBUG [cn.elinzhou.mybatisTest.mapper.UserMapper.findUsers] - ==>  Preparing: SELECT * FORM user
2015-06-30 13:56:27,070 [main] DEBUG [cn.elinzhou.mybatisTest.mapper.UserMapper.findUsers] - ==>  Parameters:
2015-06-30 13:56:27,070 [main] DEBUG [cn.elinzhou.mybatisTest.mapper.UserMapper.findUsers] - <== Total: 8  
2015-06-30 13:56:27,070 [main] DEBUG [cn.elinzhou.mybatisTest.mapper.UserMapper.findUsers] - ⇒  Preparing: SELECT * FROM user
2015-06-30 13:56:27,070 [main] DEBUG [cn.elinzhou.mybatisTest.mapper.UserMapper.findUsers] - ==>  Parameters:
2015-06-30 13:56:27,070 [main] DEBUG [cn.elinzhou.mybatisTest.mapper.UserMapper.findUsers] - <== Total: 8  
[User{address='null',id=1,name='null',birthday=null,sex=2},User{address='null',id=1,name='null',birthday=null,sex=2},User{address='null',id=1,name='null',birthday=null,sex=2}...]
Process finished with exit code 0

上述测试就是在第一查询完成后执行了commit操作,再进行查询。与之前的测试不同的是,这次测试控制台打印了两组结果,说明在commit之后MyBatis对数据重新进行了查询。

2)关闭sqlSession:

一般在MyBatis集成Spring时,会把SqlSessionFactory设置为单例注入到IOC容器中,不把SqlSession也设置为单例的原因是SqlSession是线程不安全的,所以不能为单例。那也就意味着其实是有关闭SqlSession的过程的。其实,对于每一个service中的SqlSession是不同的,这是通过MyBatis-Spring中的org.mybatis.spring.mapper.MapperScannerConfigurer创建SqlSession自动注入到service中的。而一级缓存的设计是每个SqlSession单独使用一个缓存空间,不同的SqlSession是不能相互访问数据的。当然,在SqlSession关闭后,其中数据自然被清空。

特此警告!!!!当MyBatis与Spring整合后,如果没有事务,一级缓存是失效的!一级缓存是失效的!一级缓存是失效的!

原因就是两者结合后,SqlSession如果发现当前没有事务,那么执行一个mapper方法后,SqlSession就被关闭了。如果需要维持一级缓存的可用性,有两种途径:

  1. 添加事务
  2. 使用二级缓存

二级缓存(也可以用redis代替)

在使用二级缓存之前,先测试之前提到过的关闭SqlSession后会清空缓存的问题,把Junit代码修改一下

@Test
public void testFindUsers() throws Exception{
	UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
	List<User> users = userMapper.findUsers();
	//关闭SqlSession
	sqlSession.close();
	//通过SqlSessionFactory创建一个新的SqlSession
	sqlSession = SqlSessionFactory.openSession();
	//获取mapper对象
	userMapper = sqlSession.getMapper(UserMapper.class);
	users = userMapper.findUsers();
	System.out.println(users);
}

这段代码在第一次查询完毕后关闭SqlSession,然后创建新的SqlSession和Mapper来重新执行一次查询操作,可以遇见,执行结果如图:

2015-06-30 13:56:27,070 [main] DEBUG [cn.elinzhou.mybatisTest.mapper.UserMapper.findUsers] - ==>  Preparing: SELECT * FORM user
2015-06-30 13:56:27,070 [main] DEBUG [cn.elinzhou.mybatisTest.mapper.UserMapper.findUsers] - ==>  Parameters:
2015-06-30 13:56:27,070 [main] DEBUG [cn.elinzhou.mybatisTest.mapper.UserMapper.findUsers] - <== Total: 8  
2015-06-30 13:56:27,070 [main] DEBUG [cn.elinzhou.mybatisTest.mapper.UserMapper.findUsers] - ⇒  Preparing: SELECT * FROM user
2015-06-30 13:56:27,070 [main] DEBUG [cn.elinzhou.mybatisTest.mapper.UserMapper.findUsers] - ==>  Parameters:
2015-06-30 13:56:27,070 [main] DEBUG [cn.elinzhou.mybatisTest.mapper.UserMapper.findUsers] - <== Total: 8  
[User{address='null',id=1,name='null',birthday=null,sex=2},User{address='null',id=1,name='null',birthday=null,sex=2},User{address='null',id=1,name='null',birthday=null,sex=2}...]
Process finished with exit code 0

说明关闭了SqlSession后的确把之前的缓存数据清空了,之后再执行同样的查询操作也会再访问一遍数据库。

为了解决这个问题,需要使用二级缓存。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/189212.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux关于执行文件路径的变量:$PATH

目录 前言 环境变量PATH 问题思考 总结 前言 Linux目录的配置都是依据FHS&#xff0c;FHS的标准文件指出&#xff0c;它们的主要目的是希望让用户可以了解到已安装软件通常放置于哪个目录下。也就是说&#xff0c;FHS的重点在于规范每个特定的目录下应该要放置什么样子的数…

Docker搭建私有仓库

搭建私有仓库 参考地址&#xff1a;搭建私有仓库 安装运行 docker-registry 查看docker存储路径与对应路径下的大小 docker info | grep Dir查看对应路径下的大小 du -sh /var/lib/docker如果大小没有问题的话就可以直接安装了 拉取registry镜像并运行 docker run -d \-…

数据库常用语句练习总结

show databases; 显示数据库 use student; 使用该数据库 3.show full columns from student; 展示数据库所有的字段 show columns from student; 展示数据库添加的字段 alter table student change column Sname Snames char(200); 添加 student表中的字段 alter tabl…

手写智能识别:SmartZoneOCR/SmartZoneICR Crack

Accusoft.SmartZoneOCR/Accusoft.SmartZoneICR 智能区 OCR/ICR 在您的应用程序中使用自动区域数据捕获技术消除手动数据输入。 概述 使用我们强大的 ICR 和 OCR SDK 改进字符识别。 SmartZone 先进的区域字符识别技术允许您从文档图像中的指定字段捕获打字 (OCR) 或手写 (ICR)…

24种设计模式之单例模式(饿汉式、懒汉式)

一、单例模式单例模式( Singleton Pattern &#xff09;是指确保一个类在任何情况下都绝对只有一个实例&#xff0c;并提供一个全局访问点。单例模式是创建型模式。单例模式在现实生活中应用也非常广泛&#xff0c;例如,总统&#xff0c;班主任等。J2EE标准中的ServletContext …

反对称矩阵乘任意矩阵满足交换性?

看论文的时候有疑惑 R˙R[ω]\dot{\mathbf{R}}\mathbf{R}[\omega]_{\times}R˙R[ω]​ R˙[ω]R\dot{\mathbf{R}}[\omega]_{\times}\mathbf{R}R˙[ω]​R 难道反对称矩阵乘任意矩阵满足交换性&#xff1f; 在maple里面验证一下吧&#xff1a; 很明显是不一样的&#xff0c;一…

字节二面:100Wqps短链系统,如何设计?

前段时间&#xff0c;社群小伙伴&#xff0c;在交流一个字节的二面真题&#xff1a; 100Wqps短链系统&#xff0c;怎么设计&#xff1f; 这道题&#xff0c;看上去业务简单&#xff0c;其实&#xff0c;覆盖的知识点非常多&#xff1a; 高并发、高性能分布式 IDRedis Bloom …

考虑电动汽车灵活性的微网多时间尺度协调调度研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【Qt】2.Qt坐标系、信号和槽、Lambda表达式

目录 Qt坐标系 信号和槽 需求 优点 自定义信号 自定义槽函数 触发自定义信号 代码 main.cpp widget.h widget.cpp teachar.h teachar.cpp student.h student.cpp 结果 重载 解决方法 信号和槽拓展 断开信号和槽 触发多个槽函数 Lambda表达式 [] () {}…

【数据结构与算法】DP路径问题

问题&#xff1a;最小路径和 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 说明&#xff1a;每次只能向下或者向右移动一步。 示例 1&#xff1a; 输入&#xff1a;grid [[1,3,1],[1,5,…

二叉树28:二叉搜索树的最近公共祖先

主要是我自己刷题的一些记录过程。如果有错可以指出哦&#xff0c;大家一起进步。 转载代码随想录 原文链接&#xff1a; 代码随想录 leetcode链接&#xff1a;235. 二叉搜索树的最近公共祖先 题目&#xff1a; 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。…

对于Go 语言的进阶与依赖管理| 青训营笔记

一.Go 语言进阶与依赖管理 1.1并发和并行 Go可以充分发挥多核优势&#xff0c;高效运行。 多线程程序在单核心的 cpu 上运行&#xff0c;称为并发&#xff1b; 多线程程序在多核心的 cpu 上运行&#xff0c;称为并行。 并发与并行并不相同&#xff0c;并发主要由切换时间片…

2016年专业408算法题

文章目录0 结果1 题目2 思路2.1 思路1&#xff08;较优解&#xff1a;排序&#xff09;2.2 思路2&#xff08;最优解&#xff1a;类快排思想排序&#xff09;附录0 结果 较优解&#xff1a; 最优解&#xff1a; 1 题目 2 思路 为了使&#xff5c;n1−n2&#xff5c;&#…

1.2.3存储结构:主存编址计算、主存编址的过程、存储单元、编址内容、存储总容量

1.2.3存储结构&#xff1a;主存编址计算、主存编址的过程、存储单元、编址内容、存储总容量主存编址的过程存储单元主存编址存储单元编址内容存储总容量例题主存编址的过程 计算机是一个机器&#xff0c;它能够识别的是机器语言&#xff0c;电器信号。因此计算机当中所有的数据…

英语学习 2

1 词汇积累 1、imply and infer 暗示和推断 2、indicate 显示、指出 3、outgoing 外向的 4、sympathy 同情心 5、sympathetic 有同情心的 6、evolution 进化 8、agreement 一致 10、resourceful 足智多谋的 11、appear 似乎 12、manufacturers 厂家、制造商 13、toilet paper …

linux定时器crond使用方式简介

文章目录一、简介二、cron.d下文件示例三、被调用的脚本文件四、检查脚本是否执行五、遇到的脚本未执行的情况一、简介 一般来说在/etc目录下&#xff0c;有5个以cron开头的目录&#xff0c;分别是/etc/cron.hourly&#xff0c;/etc/cron.daily&#xff0c;/etc/cron.weekly&a…

java泛型6

到底何时使用泛型方法&#xff1f;何时使用类型通配符呢&#xff1f;大多数时候都可以使用泛型方法来代替类型通配符。 这种场景下效果一样。 上面方法使用了泛型形式&#xff0c;这时定义泛型形参时设定上限&#xff08;其中E是Collection接口里定义的泛型&#xff0c;在该接…

搭建企业知识库的意义

当客户跟你达成合作关系后&#xff0c;需要持续的关系维护&#xff0c;在一定的销售点&#xff0c;定期和客户沟通&#xff0c;据调查&#xff0c;赢得一个新客户的成本可能是保留一个现有客户的5到25倍&#xff0c;作为营销策略&#xff0c;客户服务支持必须满足他们的期望。建…

Java---微服务---Seata的部署和集成

Seata的部署和集成一、部署Seata的tc-server1.下载2.解压3.修改配置4.在nacos添加配置5.创建数据库表6.启动TC服务二、微服务集成seata1.引入依赖2.修改配置文件三、TC服务的高可用和异地容灾1.模拟异地容灾的TC集群2.将事务组映射配置到nacos3.微服务读取nacos配置一、部署Sea…

PEM格式RSA密钥解析(二)

PEM格式RSA密钥解析&#xff08;二&#xff09; RSA密钥参数解析 上一部分讲解了将Base64编码的密钥数据转换成hex格式数据&#xff0c;本章将介绍如何获从转码后的数据中获取RSA密钥的相关参数。 根据 RSA 密钥语法中的结构对私钥解析结果如下&#xff1a; 上一节转码后的私…