深入浅出Mybatis系列(九)---缓存
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存。
1、默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。
2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
3、为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
1,一级缓存
一级缓存(local cache), 即本地缓存, 作用域默认为sqlSession。当Session flush或close 后,该Session中的所有Cache将被清空。本地缓存不能被关闭, 但可以调用clearCache() 来清空本地缓存, 或者改变缓存的作用域。在mybatis3.1之后, 可以配置本地缓存的作用域,在mybatis.xml 中配置 。同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中,key:hashCode+查询的SqlId+编写的sql查询语句+参数.
public static void main(String[] args)
throws IOException
{
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
try
{
PersonMapper mapper = sqlSession.getMapper(PersonMapper.class); Person person1 = mapper.getPerson(1);
System.out.println(person1); Person person2 = mapper.getPerson(1);
System.out.println(person2); System.out.println(person1 == person2);
}
finally
{
sqlSession.close();
}
}
一级缓存失效的四种情况 (未 close SqlSession情况下)
1、不同的SqlSession对应不同的一级缓存
public static void main(String[] args)
throws IOException
{
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession01 = sqlSessionFactory.openSession();
SqlSession sqlSession02 = sqlSessionFactory.openSession();
try
{
PersonMapper mapper = sqlSession01.getMapper(PersonMapper.class);
Person person1 = mapper.getPerson(1);
System.out.println(person1); mapper = sqlSession02.getMapper(PersonMapper.class);
Person person2 = mapper.getPerson(1);
System.out.println(person2); System.out.println(person1 == person2);//false
}
finally
{
sqlSession01.close();
sqlSession02.close();
}
}
2、同一个SqlSession但是查询条件不同
public static void main(String[] args)
throws IOException
{
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
try
{
PersonMapper mapper = sqlSession.getMapper(PersonMapper.class); Person person1 = mapper.getPerson(1);//查询id为1
System.out.println(person1); Person person2 = mapper.getPerson(2);//查询id为2
System.out.println(person2); System.out.println(person1 == person2);//false
}
finally
{
sqlSession.close();
}
}
3、同一个SqlSession两次查询期间执行了任何一次增删改操作
public static void main(String[] args)
throws IOException
{
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
try
{
PersonMapper mapper = sqlSession.getMapper(PersonMapper.class); Person person1 = mapper.getPerson(1);
System.out.println(person1); mapper.deletePerson(2);//删除一个数据 Person person2 = mapper.getPerson(1);
System.out.println(person2); System.out.println(person1 == person2);//false
}
finally
{
sqlSession.commit();
sqlSession.close();
}
}
4、同一个SqlSession两次查询期间手动清空了缓存
public static void main(String[] args)
throws IOException
{
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
try
{
PersonMapper mapper = sqlSession.getMapper(PersonMapper.class); Person person1 = mapper.getPerson(1);
System.out.println(person1); sqlSession.clearCache();//手动清除了缓存 Person person2 = mapper.getPerson(1);
System.out.println(person2); System.out.println(person1 == person2);//false
}
finally
{
sqlSession.close();
}
}
2、二级缓存
二级缓存(全局缓存):基于namespace级别的缓存,一个namespace对应一个二级缓存。
工作机制:
1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;
2、如果会话关闭,一级缓存中的数据会被保存到二级缓存中,新的会话查询信息,就可以参照二级缓存中的内容;
不同namespace查出的数据会放在自己对应的缓存中(map),查出的数据都会被默认先放在一级缓存中。只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中。
使用方式:
1)、开启全局二级缓存配置:<setting name="cacheEnabled" value="true"/>
2)、去mapper.xml中配置使用二级缓存: 只有在哪个mapper下面配置下面的,才会用到二级缓存,否则即使开启二级全局缓存,二级缓存也不生效
<cache></cache>
cache标签可以配置的属性:
eviction:缓存的回收策略:
LRU – 最近最少使用的:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是 LRU。
flushInterval:缓存刷新间隔
缓存多长时间清空一次,默认不清空,设置一个毫秒值
readOnly:是否只读:
true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。
mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快
false:非只读:mybatis觉得获取的数据可能会被修改。
mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢
size:缓存存放多少元素;
type="":指定自定义缓存的全类名: <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
实现Cache接口即可;
blocking: 若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存。
3)、POJO需要实现序列化接口
二级缓存代码示例
1)<setting name="cacheEnabled" value="true"/>
2)public class Person implements Serializable{...}
3)mapper映射文件加入<cache></cache>
为了能观察日志情况,我们简单配置一下日志打印:
<setting name="logImpl" value="STDOUT_LOGGING" />
下面是测试代码
public static void main(String[] args)
throws IOException
{
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
try
{
PersonMapper mapper1 = sqlSession1.getMapper(PersonMapper.class);
Person person1 = mapper1.getPerson(1);
System.out.println(person1); sqlSession1.close(); PersonMapper mapper2 = sqlSession2.getMapper(PersonMapper.class);
Person person2 = mapper2.getPerson(1);
System.out.println(person2); }
finally
{
sqlSession2.close();
}
}
解释:sqlSession1查询结果之后,关闭sqlSession1,会将结果写入二级缓存,然后sqlSession2查询会从二级缓存中查询,不从数据查询数据了。下面日志可以证明:.
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Cache Hit Ratio [com.yefengyu.mybatis.mapper.PersonMapper]: 0.0
Opening JDBC Connection
Sun Jun 16 17:16:22 CST 2019 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Created connection 327177752.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@13805618]
==> Preparing: select * from person where id = ?
==> Parameters: 1(Integer)
<== Columns: id, first_name, last_name, age, email, address
<== Row: 1, tom, Carine, 25, null, beijing
<== Total: 1
Person{id=1, firstName='tom', lastName='Carine', age=25, email='null', address='beijing'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@13805618]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@13805618]
Returned connection 327177752 to pool.
Cache Hit Ratio [com.yefengyu.mybatis.mapper.PersonMapper]: 0.5
Person{id=1, firstName='tom', lastName='Carine', age=25, email='null', address='beijing'}
注意:只有一级缓存关闭的情况下二级缓存才会生效,下面演示中一级缓存没有关闭,二级缓存没有起作用,注意sqlSession1.close()的位置
public static void main(String[] args)
throws IOException
{
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
try
{
PersonMapper mapper1 = sqlSession1.getMapper(PersonMapper.class);
Person person1 = mapper1.getPerson(1);
System.out.println(person1); PersonMapper mapper2 = sqlSession2.getMapper(PersonMapper.class);
Person person2 = mapper2.getPerson(1);
System.out.println(person2); }
finally
{
sqlSession1.close();
sqlSession2.close();
}
}
Cache Hit Ratio [com.yefengyu.mybatis.mapper.PersonMapper]: 0.0
Opening JDBC Connection
Sun Jun 16 17:22:38 CST 2019 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Created connection 327177752.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@13805618]
==> Preparing: select * from person where id = ?
==> Parameters: 1(Integer)
<== Columns: id, first_name, last_name, age, email, address
<== Row: 1, tom, Carine, 25, null, beijing
<== Total: 1
Person{id=1, firstName='tom', lastName='Carine', age=25, email='null', address='beijing'}
Cache Hit Ratio [com.yefengyu.mybatis.mapper.PersonMapper]: 0.0
Opening JDBC Connection
Sun Jun 16 17:22:38 CST 2019 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Created connection 1589683045.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5ec0a365]
==> Preparing: select * from person where id = ?
==> Parameters: 1(Integer)
<== Columns: id, first_name, last_name, age, email, address
<== Row: 1, tom, Carine, 25, null, beijing
<== Total: 1
Person{id=1, firstName='tom', lastName='Carine', age=25, email='null', address='beijing'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@13805618]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@13805618]
Returned connection 327177752 to pool.
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5ec0a365]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5ec0a365]
Returned connection 1589683045 to pool.
其它知识点:
* 和缓存有关的设置/属性:
* 1)、cacheEnabled=true:false:关闭缓存(二级缓存关闭)(一级缓存一直可用的)
* 2)、每个select标签都有useCache="true":
* false:不使用缓存(一级缓存依然使用,二级缓存不使用) :在全局开启的情况下可以禁止部分查询使用二级缓存
* 3)、【每个增删改标签的:flushCache="true":(一级二级都会清除)】
* 增删改执行完成后就会清除缓存;
* 测试:flushCache="true":一级缓存就清空了;二级也会被清除;
* 查询标签:flushCache="false":
* 如果flushCache=true;每次查询之后都会清空缓存;缓存是没有被使用的;
* 4)、sqlSession.clearCache();只是清除当前session的一级缓存;
* 5)、localCacheScope:本地缓存作用域:(一级缓存SESSION);当前会话的所有数据保存在会话缓存中;
* STATEMENT:可以禁用一级缓存;
3、外部缓存
外部缓存可以使用第三方提供的缓存包,比如EhCache:
1、首先在类路径下面添加ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="D:\ehcache" /> <defaultCache
maxElementsInMemory="10000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
属性说明:
diskStore:指定数据在磁盘中的存储位置。
defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
以下属性是必须的:
maxElementsInMemory - 在内存中缓存的element的最大数目
maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
以下属性是可选的:
timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
2、在mapper文件下面使用下面的缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
3、注意依赖包,主要是缓存包,适配包
深入浅出Mybatis系列(九)---缓存的更多相关文章
- 深入浅出Mybatis系列九-强大的动态SQL
注:本文转载自南轲梦 注:博主 Chloneda:个人博客 | 博客园 | Github | Gitee | 知乎 上篇文章<深入浅出Mybatis系列(八)---mapper映射文件配置之se ...
- 深入浅出Mybatis系列(九)---强大的动态SQL
上篇文章<深入浅出Mybatis系列(八)---mapper映射文件配置之select.resultMap>简单介绍了mybatis的查询,至此,CRUD都已讲完.本文将介绍mybatis ...
- 深入浅出Mybatis系列(九)---强大的动态SQL(转载)
原文出处:http://www.cnblogs.com/dongying/p/4092662.html 上篇文章<深入浅出Mybatis系列(八)---mapper映射文件配置之select.r ...
- 深入浅出Mybatis系列(八)---mapper映射文件配置之select、resultMap
上篇<深入浅出Mybatis系列(七)---mapper映射文件配置之insert.update.delete>介绍了insert.update.delete的用法,本篇将介绍select ...
- 深入浅出Mybatis系列(七)---mapper映射文件配置之insert、update、delete
上篇文章<深入浅出Mybatis系列(六)---objectFactory.plugins.mappers简介与配置>简单地给mybatis的配置画上了一个句号.那么从本篇文章开始,将会介 ...
- 深入浅出Mybatis系列(八)---mapper映射文件配置之select、resultMap good
上篇<深入浅出Mybatis系列(七)---mapper映射文件配置之insert.update.delete>介绍了insert.update.delete的用法,本篇将介绍select ...
- 深入浅出Mybatis系列(八)---mapper映射文件配置之select、resultMap[转]
上篇<深入浅出Mybatis系列(七)---mapper映射文件配置之insert.update.delete>介绍了insert.update.delete的用法,本篇将介绍select ...
- 深入浅出Mybatis系列八-mapper映射文件配置之select、resultMap
注:本文转载自南轲梦 注:博主 Chloneda:个人博客 | 博客园 | Github | Gitee | 知乎 上篇<深入浅出Mybatis系列(七)---mapper映射文件配置之inse ...
- 深入浅出Mybatis系列七-mapper映射文件配置之insert、update、delete
注:本文转载自南轲梦 注:博主 Chloneda:个人博客 | 博客园 | Github | Gitee | 知乎 上篇文章<深入浅出Mybatis系列(六)---objectFactory.p ...
- 深入浅出Mybatis系列(六)---objectFactory、plugins、mappers简介与配置
上篇文章<深入浅出Mybatis系列(五)---TypeHandler简介及配置(mybatis源码篇)>简单看了一下TypeHandler, 本次将结束对于mybatis的配置文件的学习 ...
随机推荐
- ERROR 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: NO)解决办法
问题:ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO) 很久没用这台电脑的mysql ...
- ORM研究3 - odoo fields常用的字段属性
之前我们已经讲解了odoo ORM中的一些对字段常用的API操作方法,今天我们继续研究一下Odoo orm中字段的一些通用属性字段的使用,学会它们可以为自己创建数据映射并使用有更好的帮助. 通用字段属 ...
- Pytest单元测试框架之setup/teardown模块示例操作
"""模块级(setup_module/teardown_module)开始于模块始末,全局的函数级(setup_function/teardown_function)只 ...
- 使用xampp在本地环境配置虚拟域名
最近在学习ThinkPHP5.1.手册里面提到"实际部署中,应该是绑定域名访问到public目录,确保其它目录不在WEB目录下面."所以把使用xampp在本地配置虚拟域名的过程记录 ...
- 理解 React Hooks 心智模型:必须按顺序、不能在条件语句中调用的规则
前言 自从 React 推出 hooks 的 API 后,相信大家对新 API 都很喜欢,但是它对你如何使用它会有一些奇怪的限制.比如,React 官网介绍了 Hooks 的这样一个限制: 不要在循环 ...
- GetOverlappedResult 函数
BOOL GetOverlappedResult( HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWORD lpNumberOfBytesTransferre ...
- 14Java进阶网络编程API
1.网络协议的三要素:语义.语法和时序 语义表示要做什么,语法表示要怎么做,时序表示做的顺序. 2.网络OSI七层模型 OSI/RM 模型(Open System Interconnection/Re ...
- 【动画消消乐】HTML+CSS 白云飘动效果 072
前言 Hello!小伙伴! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出- 自我介绍 ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计 ...
- vsCode开发flutter项目
Visual Studio Code 安装: 下载链接:https://code.visualstudio.com/ 下载完成后根据步骤自行安装. 使用vsCode开发flutter项目需要部 ...
- tp phpexcel 导出后台访问方法
public function phpexcel(){ //-----excel表导出 $dataid = $_GET['dataid']; $status = $_GET['status']; $F ...