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系列(九)---缓存的更多相关文章

  1. 深入浅出Mybatis系列九-强大的动态SQL

    注:本文转载自南轲梦 注:博主 Chloneda:个人博客 | 博客园 | Github | Gitee | 知乎 上篇文章<深入浅出Mybatis系列(八)---mapper映射文件配置之se ...

  2. 深入浅出Mybatis系列(九)---强大的动态SQL

    上篇文章<深入浅出Mybatis系列(八)---mapper映射文件配置之select.resultMap>简单介绍了mybatis的查询,至此,CRUD都已讲完.本文将介绍mybatis ...

  3. 深入浅出Mybatis系列(九)---强大的动态SQL(转载)

    原文出处:http://www.cnblogs.com/dongying/p/4092662.html 上篇文章<深入浅出Mybatis系列(八)---mapper映射文件配置之select.r ...

  4. 深入浅出Mybatis系列(八)---mapper映射文件配置之select、resultMap

    上篇<深入浅出Mybatis系列(七)---mapper映射文件配置之insert.update.delete>介绍了insert.update.delete的用法,本篇将介绍select ...

  5. 深入浅出Mybatis系列(七)---mapper映射文件配置之insert、update、delete

    上篇文章<深入浅出Mybatis系列(六)---objectFactory.plugins.mappers简介与配置>简单地给mybatis的配置画上了一个句号.那么从本篇文章开始,将会介 ...

  6. 深入浅出Mybatis系列(八)---mapper映射文件配置之select、resultMap good

    上篇<深入浅出Mybatis系列(七)---mapper映射文件配置之insert.update.delete>介绍了insert.update.delete的用法,本篇将介绍select ...

  7. 深入浅出Mybatis系列(八)---mapper映射文件配置之select、resultMap[转]

    上篇<深入浅出Mybatis系列(七)---mapper映射文件配置之insert.update.delete>介绍了insert.update.delete的用法,本篇将介绍select ...

  8. 深入浅出Mybatis系列八-mapper映射文件配置之select、resultMap

    注:本文转载自南轲梦 注:博主 Chloneda:个人博客 | 博客园 | Github | Gitee | 知乎 上篇<深入浅出Mybatis系列(七)---mapper映射文件配置之inse ...

  9. 深入浅出Mybatis系列七-mapper映射文件配置之insert、update、delete

    注:本文转载自南轲梦 注:博主 Chloneda:个人博客 | 博客园 | Github | Gitee | 知乎 上篇文章<深入浅出Mybatis系列(六)---objectFactory.p ...

  10. 深入浅出Mybatis系列(六)---objectFactory、plugins、mappers简介与配置

    上篇文章<深入浅出Mybatis系列(五)---TypeHandler简介及配置(mybatis源码篇)>简单看了一下TypeHandler, 本次将结束对于mybatis的配置文件的学习 ...

随机推荐

  1. PYTHON 当前.PY文件名不能与引入的模块同名

    当前文件名:sqlite3.py 文件引入import sqlite3 运行会出错,因为调用sqlite3的方法首先从当前文件找方法,当然找不到,所以会报错了

  2. PyCharm代码区不能编辑的解决办法

    问题: 修改之前的Python代码时发现代码区无法编辑,无意中输入i后又可以编辑了. 解决: 原因是打开了工具中的vim Emulator编辑模式,把vim Emulator前面的勾取消即可.

  3. C语言变量为何先定义后使用

    C语言中,对变量的使用,首先要先定义.说明其数据类型.原因可能如下: 1不同类型的变量,其编码表示方式可能不同. 2不同类型的变量,其占有的空间大小不同.不事先说明无法在内存中开辟空间.

  4. Cannot read property 'data' of undefined —— 小程序开发

    由于疫情原因目前处于半下岗状态,在家的时候就研究起了小程序开发.由于是新手,所以总会遇到各种问题,顺便记录一下. wx.showModal({ title: '提示', content: '这是一个模 ...

  5. CentOS下 Django部署 uWSGI+Django(一)

    由于新冠疫情的缘故,公司要求员工停薪休假,赋闲在家的时候还是决定做点正事,学学习. 本人Linux入门水平,Python入门水平,所以在网上找的那些python部署的帖子,看的是云里雾里的,也没有达到 ...

  6. POJ1417 True Liars 题解

    通过读题,容易发现,当回答为yes时 \(x,y\) 必属于同类,当回答为no时二者必为异类(并且当 \(x=y\) 时,回答必为yes,不过这题不用这个性质). 于是先按关系维护连通块,然后求出每个 ...

  7. C++ Socket编程(基础)

    一.基本简介 在计算机通信领域,socket 被翻译为"套接字",它是计算机之间进行通信的一种约定或一种方式. 通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也 ...

  8. 创建多个Dialog时,namespace冲突问题的解决 -- 基于QT 5.2

    问题来源: 我用MainWindow作为主界面,Dialog作为设置界面,还需要一个AboutDialog作为关于界面. 设置界面的Dialog头文件dialog.h是这样的: // dialog.h ...

  9. java小游戏java九宫格

    问题来源于吾爱破解https://www.52pojie.cn/thread-1484202-1-1.html 编程目标一:根据下面的"游戏说明",实现该游戏程序,完成响应用户的输 ...

  10. OpenGL学习笔记(二)画三角形

    目录 渲染管线(Graphics Pipeline) 编码实现 顶点数据 顶点缓冲对象(VBO) 顶点着色器 编译着色器 片段着色器 着色器程序 链接顶点属性 顶点数组对象 最终绘制三角形 索引缓冲对 ...