【MyBatis框架】查询缓存-二级缓存原理

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:
二级缓存原理

1.原理

首先看图



首先开启mybatis的二级缓存。

sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。

如果SqlSession3去执行相同 mapper下sql,执行commit提交,清空该 mapper下的二级缓存区域的数据。

sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。

二级缓存与一级缓存区别,二级缓存的范围更大,多个sqlSession可以共享一个UserMapper的二级缓存区域。
UserMapper有一个二级缓存区域(按namespace分) ,其它mapper也有自己的二级缓存区域(按namespace分)。
每一个namespace的mapper都有一个二缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql查询到数据将存在相同 的二级缓存区域中。

2.开启二级缓存

mybaits的二级缓存是mapper范围级别,除了在SqlMapConfig.xml设置二级缓存的总开关,还要在具体的mapper.xml中开启二级缓存。

在核心配置文件SqlMapConfig.xml中加入
<setting name="cacheEnabled" value="true"/>
描述 允许值 默认值
cacheEnabled 对在此配置文件下的所有cache 进行全局性开/关设置。 true false true

在UserMapper.xml中开启二缓存,UserMapper.xml下的sql执行完成会存储到它的缓存区域(HashMap)。

<!-- 开启本Mapper的namespace下的二级缓存 -->
    <cache/>

3.调用pojo类实现序列化接口
public class User implements Serializable{
	private int id;
	private String username;// 用户姓名
	private String sex;// 性别
	private Date birthday;// 生日
	private String address;// 地址
	//...
}
为了将缓存数据取出执行反序列化操作,因为二级缓存数据存储介质多种多样(内存、硬盘、服务器),不一样在内存。

4.测试方法
先测试二级缓存的存在
//测试二级缓存
@Test
public void testCache2() throws Exception{
	SqlSession sqlSession1 = sqlSessionFactory.openSession();
	SqlSession sqlSession2 = sqlSessionFactory.openSession();
	SqlSession sqlSession3 = sqlSessionFactory.openSession();
	
	UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);//创建代理对象
	//下边查询使用一个SqlSession
	//第一次发起请求,查询id为1的用户
	User user1 = userMapper1.findUserById(1);
	System.out.println(user1.getUsername());
	//不关闭SqlSession无法写进二级缓存区域中
	sqlSession1.close();
	
	UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);//创建代理对象
	//第二次发起请求,查询id为1的用户
	User user2 = userMapper2.findUserById(1);
	System.out.println(user2.getUsername());
	sqlSession2.close();
	
	
}
测试结果和输出日志:
DEBUG [main] - Cache Hit Ratio [cn.edu.hpu.mybatis.mapper.UserMapper]: 0.0
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 26255574.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.Connection@190a0d6]
DEBUG [main] - ==>  Preparing: SELECT * FROM USER WHERE id=? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
张三
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.Connection@190a0d6]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.Connection@190a0d6]
DEBUG [main] - Returned connection 26255574 to pool.
DEBUG [main] - Cache Hit Ratio [cn.edu.hpu.mybatis.mapper.UserMapper]: 0.5
张三

我们可以发现,日志输出中有一句我们之前没有见过,是:
Cache Hit Ratio [cn.edu.hpu.mybatis.mapper.UserMapper]: 0.0
这句的意思是缓存命中率为0.0,说明第一次是在缓存中找,可是没找到
DEBUG [main] - Cache Hit Ratio [cn.edu.hpu.mybatis.mapper.UserMapper]: 0.5
这句的意思是缓存命中率为0.5,说明第一次是在缓存中找,可是没找到,第二次找到了。

这个测试证明了二级缓存的存在

下面证明第三方sqlSession执行增删改会清空缓存的事实:
//测试二级缓存
@Test
public void testCache2() throws Exception{
	SqlSession sqlSession1 = sqlSessionFactory.openSession();
	SqlSession sqlSession2 = sqlSessionFactory.openSession();
	SqlSession sqlSession3 = sqlSessionFactory.openSession();
	
	UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);//创建代理对象
	//下边查询使用一个SqlSession
	//第一次发起请求,查询id为1的用户
	User user1 = userMapper1.findUserById(1);
	System.out.println(user1.getUsername());
	//不关闭SqlSession无法写进二级缓存区域中
	sqlSession1.close();
	
	UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);//创建代理对象
	User user=userMapper3.findUserById(1);
	user.setUsername("张明明");
	userMapper3.updateUser(user);
	//执行提交,清空UserMapper二级缓存
	sqlSession3.commit();
	sqlSession3.close();
	
	UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);//创建代理对象
	//第二次发起请求,查询id为1的用户
	User user2 = userMapper2.findUserById(1);
	System.out.println(user2.getUsername());
	sqlSession2.close();
	
	
}

输出结果和日志信息:
DEBUG [main] - Cache Hit Ratio [cn.edu.hpu.mybatis.mapper.UserMapper]: 0.0
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 189219.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.Connection@2e323]
DEBUG [main] - ==>  Preparing: SELECT * FROM USER WHERE id=? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
张三
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.Connection@2e323]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.Connection@2e323]
DEBUG [main] - Returned connection 189219 to pool.
DEBUG [main] - Cache Hit Ratio [cn.edu.hpu.mybatis.mapper.UserMapper]: 0.5
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 189219 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.Connection@2e323]
DEBUG [main] - ==>  Preparing: update user set username=?,birthday=?,sex=?,address=? where id=? 
DEBUG [main] - ==> Parameters: 张明明(String), 2015-06-07(Date), 男(String), 河南焦作(String), 1(Integer)
DEBUG [main] - <==    Updates: 1
DEBUG [main] - Committing JDBC Connection [com.mysql.jdbc.Connection@2e323]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.Connection@2e323]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.Connection@2e323]
DEBUG [main] - Returned connection 189219 to pool.
DEBUG [main] - Cache Hit Ratio [cn.edu.hpu.mybatis.mapper.UserMapper]: 0.3333333333333333
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 189219 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.Connection@2e323]
DEBUG [main] - ==>  Preparing: SELECT * FROM USER WHERE id=? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
张明明
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.Connection@2e323]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.Connection@2e323]
DEBUG [main] - Returned connection 189219 to pool.
发现第二次再去查1号用户的时候,又再一次查询了数据库,查到了最新的数据"张明明"。

二级缓存远远没有那么简单,他还有一些配置的参数。

5.useCache配置

在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。
<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">

总结:针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存。

6.刷新缓存(就是清空缓存)
在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。

 设置statement配置中的flushCache="true" 属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。
如下:
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">

总结:一般下执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。


转载请注明出处:http://blog.csdn.net/acmman/article/details/46793289

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
9天前
|
XML Java 数据库连接
mybatis中在xml文件中通用查询结果列如何使用
mybatis中在xml文件中通用查询结果列如何使用
9 0
|
SQL Java 数据库连接
MyBatis 优秀的持久层框架(一)
MyBatis 优秀的持久层框架
61 0
|
16天前
|
缓存 关系型数据库 MySQL
MySQL 查询优化:提速查询效率的13大秘籍(索引设计、查询优化、缓存策略、子查询优化以及定期表分析和优化)(中)
MySQL 查询优化:提速查询效率的13大秘籍(索引设计、查询优化、缓存策略、子查询优化以及定期表分析和优化)(中)
|
21天前
|
存储 XML 缓存
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(一)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南
42 0
|
6天前
|
SQL Java 数据库连接
什么是MyBatis持久层框架?
MyBatis 是一个优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs (Plain Old Java Objects, 普通的 Java 对象) 映射成数据库中的记录。
16 5
|
21天前
|
缓存 应用服务中间件 数据库
【分布式技术专题】「缓存解决方案」一文带领你好好认识一下企业级别的缓存技术解决方案的运作原理和开发实战(多级缓存设计分析)
【分布式技术专题】「缓存解决方案」一文带领你好好认识一下企业级别的缓存技术解决方案的运作原理和开发实战(多级缓存设计分析)
26 1
|
22天前
|
SQL XML Java
这样使用MyBatis框架,被攻击了
这样使用MyBatis框架,被攻击了
8 0
|
23天前
|
Java fastjson Apache
Spring Boot+Gradle+ MyBatisPlus3.x搭建企业级的后台分离框架
Spring Boot+Gradle+ MyBatisPlus3.x搭建企业级的后台分离框架
30 1
|
1月前
|
SQL Java 数据库连接
Mybatis查询的时候BigDecimal类型的值查询失效的解决办法
Mybatis查询的时候BigDecimal类型的值查询失效的解决办法