深入浅出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的配置文件的学习 ...
随机推荐
- c语言:scanf(" %c",&bla); scanf("%c",&bla); 语句差别
%前有空格,%没有空格 scanf("%c",&c) 与 scanf(" %c",&c),后者只是在%前多了个空格,似乎没有什么区别,但使用起来 ...
- 如何让Spring Boot 的配置动起来?
前言 对于微服务而言配置本地化是个很大的鸡肋,不可能每次需要改个配置都要重新把服务重新启动一遍,因此最终的解决方案都是将配置外部化,托管在一个平台上达到不用重启服务即可一次修改多处生效的目的. 但是对 ...
- 【剑指offer】58 - II. 左旋转字符串
剑指 Offer 58 - II. 左旋转字符串 知识点:字符串: 题目描述 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部.请定义一个函数实现字符串左旋转操作的功能.比如,输入字符串 ...
- github在不同电脑上协同开发
当我换了电脑后,开发自己的github项目遇到了一些问题. 首先,git clone 'repository url'拉取下来项目,开始开发项目发.修改了一些文件后,当要git commit, git ...
- 图像处理算法的仿真平台之VGA时序
一 概述 图像处理算法一般是用matla或OpenCV实现的,若是用FPGA实现,设计思路差别极大.matlab和opencv的优势:这些工具的优势在于可以方便地载入图像文件,或输出数据到图像文件, ...
- 2019 Mac下安装运行Homestead环境
为了能提高自己的价值,还是要坚持学习新东西才行.这不,从多学会一个PHP开发框架开始.在开始使用Laravel之前,很多"经验"告诉我要先安装Homestead,至于好处,大家可以 ...
- 最大流最小割——bzoj1001狼抓兔子,洛谷P2598
前置知识 平面图 平面图就是平面上任意边都不相交的图.(自己瞎画的不算XD) 对偶图 比如说这个图,我们发现平面图肯定会把平面分成不同的区域(感觉像拓扑图),并把这些区域当做每个点(不被包围的区域独自 ...
- Git的使用(六)
前言 版本管理工具总结: 开发团队项目,对项目的版本进行管理. 使用过的版本管理工具: TFS.SVN与Git. TFS:管理项目,通过visual Studio管理源码,拉取分支,提交代码等.也可以 ...
- Android从一个Fragment跳转到另一个Fragment后原来的组件不消失
问题描述 Activity上放置了一个Fragment,Fragment上有按钮,点了按钮后,应该跳转到另一个Fragment, but 原来的Fragment的按钮不会消失,新的Fragment不是 ...
- 18Oracle入门
1 Oracle的服务 Oracle的监听服务:OralceOraDB12Home1TNSListener 需要通过程序链接数据库进行开发的时候,此服务必须打开,如果只是在本机使用,此服务可不启动 O ...