java ssm框架 缓存_SSM框架之MyBatis3专题4:查询缓存

查询缓存的使用,主要是为了提高查询访问速度。将用户对同一数据的重复查询过程简化,不再每次均从数据库中查询获取结果数据,从而提高访问速度。

MyBatis的查询缓存机制,根据缓存区的作用域(声明周期)可划分为两种:一级查询缓存和二级查询缓存。

1 一级查询缓存

MyBatis一级查询缓存是基于org.apache.ibatis.cache.impl.PerpetualCache类的HashMap本地缓存,其作用域是SqlSession。在同一个SqlSession中两次执行相同的sql查询语句,第一次执行完毕后,会将查询结果写入到缓存中,第二次会从缓存中直接获取数据,而不再到数据库中进行查询,从而提高查询效率。

当一个SqlSession结束后,该SqlSession中的一级缓存也就不存在了。MyBatis默认一级查询缓存是开启状态的,且不能够关闭。

一级缓存执行原理示意图:

b2c960e2bb140fc59c087495e6e2e7c0.png

一级缓存执行流程图:

178cda45436b6f1ebb98aa8e28fd06ff.png

1.1 一级查询缓存的存在性证明

1.1.1 修改测试类:

// 证明一级缓存是存在的

@Test

public void test01() {

Student student = dao.selectById(3);

System.out.println(student);

Student student2 = dao.selectById(3);

System.out.println(student2);

}

1.1.2 查看控制台:

执行完毕后,发现只是执行了一次从DB中的查询,第二次的结果是直接输出的。说明,第二次是从SqlSession缓存中读取的。

6ea68c91d0885af8670c0eb137d96269.png

1.2 从缓存读取数据的依据是Sql的id

一级缓存缓存的是相同的Sql映射id的查询结果,而非相同Sql语句的查询结果。因为myBatis内部对于查询缓存,无论是以及一级缓存还是二级查询缓存,其底层均是使用一个HashMap实现的:key为Sql的id相关内容,value为从数据库中查询出的结果。

1.2.1 修改映射文件

在映射文件中对于某一个标签进行完全复制,然后修改一下这个SQL映射的id。也就是说,这两个SQL映射除了id不同,其他均相同,即查询结果肯定是相同的。

select * from t_student where id=#{jjj}

select * from t_student where id=#{jjj}

1.2.2 修改Dao接口

Student selectById(int id);

Student selectById2(int id);

1.2.3 修改测试类

@Test

public void test02() {

Student student = dao.selectById(3);

System.out.println(student);

Student student2 = dao.selectById2(3);

System.out.println(student2);

}

1.2.4 查看控制台

查看控制台,发现第二次查询结果与第一次的完全相同,但是第二次查询并没有从缓存中读取数据,而是直接从DB中进行的查询。这是因为从缓存读取数据的依据是查询SQL的映射id,而非查询结果。

0ac70388e3d6201b9f6af4616be19df3.png

1.3 增删改对一级查询缓存的影响

1.3.1 修改测试类

增、删、改操作,无论是否进行提交sqlSession.commit(),均会清空一级查询缓存,使得查询再次从DB中select。

@Test

public void test03() {

Student student = dao.selectById(3);

System.out.println(student);

dao.deleteById(4);

Student student2 = dao.selectById(3);

System.out.println(student2);

}

1.3.2 查看控制台

93abb21970060020a4a0a9bf4c4d9133.png

2 内置二级缓存查询

MyBatis查询缓存的作用域是根据映射文件mapper的namespace划分的,相同的namespace的mapper查询数据存放在同一个缓存区域中。不同的namespace中的数据互不干扰。无论是一级缓存还是二级缓存,都是按照namespace进行分别存放的。

但一级、二级缓存的不同之处在于,SqlSession一旦关闭,则SqlSession中的数据将不存在,即一级缓存就不复存在。而二级缓存的生命周期会与整个应用同步,与SqlSession是否关闭无关。

使用二级缓存的目的,不是共享查询,因为MyBatis从缓存中读取数据的依据是SQL的id,而非查询出的对象。所以,二级缓存中的数据不是为了在多个查询之间共享(所有查询中只要查询结果中存在该对象,就直接从缓存中读取,这是对数据的共享,Hibernate中的缓存就是为了共享,但是MyBatis的不是。),而是为了延长该查询结果的保存时间,提高系统性能。

MyBatis内置的二级缓存为:org.apache.ibatis.cache.impl.PerpetualCache。

2.1 二级缓存用法

二级查询缓存的使用很简单,只需要完成两步即可:

2.1.1 实体序列化

要求查询结果所涉及到的实体类要实现Java.io.Serializable接口。若该实体类存在父类,或者其具有域属性,则父类与域属性也要实现序列化接口。

public class Student implements Serializable{

private static final long serialVersionUID = -6979637229268429997L;

private Integer id;

private String name;

private int age;

private double score;

}

2.1.2 mapper映射中添加标签

在mapper映射文件的标签中添加子标签。

2.1.3 二级缓存的配置

为标签添加一些相关属性设置,可以对二级缓存的运行性能进行控制。当然,若不指定设置,则均保持默认值。

readOnly="true" size="512"/>

1、eviction:逐出策略。当二级缓存中的对象达到最大值时,就需要通过逐出策略将缓存中的对象溢出缓存。默认为LRU。常用的策略有:

FIFO:First In First Out,先进先出;

LRU:Least Recently Used,未被使用时时间最长;

2、flushInterval:刷新缓存的时间间隔,单位毫秒。这里的刷新缓存即清空缓存。一般不指定,即当执行增删改时刷新缓存;

3、readOnly:设置缓存中的数据是否为只读,只读的缓存会给所有调用者返回缓存对象的相同实例,因此这些对象是不能够被修改的,这提供了很重要的性能优势。但是读写的缓存会返回缓存对象的拷贝。这会慢一些,但是很安全,因此默认值为false。

4、size:二级缓存中可以存放的最多对象个数。默认为1024个;

2.2 二级查询缓存的存在性证明

对于映射文件中的同一个查询,肯定是同一个namespace中的查询(因为就是同一个查询)。在一次查询后,将SqlSession关闭,再进行一次相同查询,发现并没有到DB中进行select查询,说明二级查询缓存是存在的。

2.2.1 修改测试类

@Test

public void test04() {

sqlSession = MyBatisUtils.getSqlSession();

dao = sqlSession.getMapper(IStudentDao.class);

//第一次查询

Student student = dao.selectById(3);;

System.out.println(student);

//关闭SqlSession

sqlSession.close();

sqlSession = MyBatisUtils.getSqlSession();

dao = sqlSession.getMapper(IStudentDao.class);

//第二次查询

Student student2 = dao.selectById(3);

System.out.println(student2);

}

2.2.2 查看控制台

Cache Hit Ratio表示缓存命中率。开启二级缓存后,每次执行一次查询,系统都会计算一次二级缓存的命中率。第一次查询也是先从缓存中查询,只不过缓存中一定是没有的。所以会再从DB中查询。由于二级缓存中不存在该数据,所以命中率为0。但是第二次查询是从二级缓存中读取的,所以这一次的命中率为1/2 = 0.5。当然,若有第三次查询的话,则命中率会为1/3=0.66。

9157f8a2f402f7300ac92d7812fec2c0.png

2.3 增删改对二级查询缓存的影响

2.3.1 默认对缓存的刷新

增删改操作,无论是否进行提交sqlSession.commit(),均会清空一级、二级查询缓存,使得查询再次从DB中select。

1、修改测试类:

@Test

public void test05() {

sqlSession = MyBatisUtils.getSqlSession();

dao = sqlSession.getMapper(IStudentDao.class);

//第一次查询

Student student = dao.selectById(3);;

System.out.println(student);

//关闭SqlSession

sqlSession.close();

sqlSession = MyBatisUtils.getSqlSession();

dao = sqlSession.getMapper(IStudentDao.class);

//删除一个对象

dao.deleteById(4);

//第二次查询

Student student2 = dao.selectById(3);

System.out.println(student2);

}

2、查看控制台:

注意,在第二次查询时的缓存命中率为0.5,但还是从DB中查询了。这说明在缓存中与该查询相对应的key是存在的,但是其value被清空。而value被清空的原因是前面执行了对象DB的增删改操作,所以不会从缓存中直接将null值返回,而是从DB中进行查询。

053d3588e7abc4ff03e678f90f83ff0d.png

2.3.2 设置增删改操作不刷新二级缓存

若要使得某个增删改操作不清空二级缓存,则需要在其或者或者中添加属性flushCache="false",默认值为true。

delete from t_student where id = #{xxx}

0d6e2f3141d7c94aa98a720c2f5d94a1.png

2.4 二级缓存的关闭

二级缓存默认为开启状态。若要将其关闭,则需要进行相关配置。

根据关闭的范围大小,可以分为全局关闭和局部关闭。

2.4.1 全局关闭

所谓全局关闭是指,整个应用的二级缓存全部关闭,所以查询均不使用二级缓存。全局开关设置在主配置文件的全局设置中,该属性为cacheEnabled,设置为false,则关闭;设置为true,则开启,默认值为true。即,二级缓存默认是开启的。

2.4.2 局部关闭

所谓局部关闭是指,整个应用的二级缓存是开启的,但是只是对于某个查询,不使用二级缓存。此时可以单独只关闭该标签的二级缓存。

在该要关闭的二级缓存的标签中,将其属性useCache设置为false,即可关闭该查询的二级缓存。该属性默认为true,即每个查询的二级缓存默认是开启的。

select * from t_student where id=#{jjj}

2.5 二级缓存的使用原则

2.5.1 只能够在一个命名空间下使用二级缓存

由于二级缓存中的数据时基于namespace的,即不同的namespace中的数据互不干扰。在多个namespace中若均存在对同一个表的操作,那么这多个namespace中的数据可能就会出现不一致现象。

2.5.2 在单表上使用二级缓存

如果一个表与其他表有关联关系,那么就非常有可能存在多个namespace对同一数据的操作。而不同的namespace中的数据互不干扰,所以有可能出现这多个namespace中的数据不一致现象。

2.5.3 查询多于修改时使用二级缓存

在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存,对二级缓存的频繁刷新就会降低系统性能。

3 ehcache二级查询缓存

mybatis的特长是SQL操作,缓存数据管理不是其特长,为了提高缓存的性能,myBatis允许使用第三方缓存产品。ehCache就是其中一种。

注意,使用ehcache二级缓存,实体类无需实现序列化接口。

3.1 导入Jar包

这里需要两个Jar包:一个为ehcache的核心Jar包,一个是myBatis与ehcache整合的插件Jar包。它们可以从https://github.com/mybatis/ehcache-cache/releases 下面。

fa69ecddeb0d56513ee0266bb440764b.png

解压该文件,获取到它们。其中lib下的是ehcache的核心Jar包。

e314fded1fba06b9e6b45e2e536335d3.png

3.2 ehcache.xml

解压EHCache的核心Jar包ehcache-core-2.6.8.jar,将其中的一个配置文件ehcache.failsafe.xml直接放到项目的src目录下,并更名为ehcache.xml。

e24ced950e3966a09f1e84160016937c.png

3.2.1 < diskStore/>标签

指定一个文件目录,当内存空间不够,需要将二级缓存中的数据写到硬盘上时,会写到这个指定目录中。其值一般为java.io.tmpdir,表示当前系统的默认文件临时目录。

cefc8699868952b23498af96d59ceea7.png

当前系统的默认文件临时目录,可以通过System.property()方法查看:

@Test

public void test() {

String path = System.getProperty("java.io.tempdir");

System.out.println(path);

}

3.2.2 < defaultCache/>标签

maxElementsInMemory="10000"

eternal="false"

timeToIdleSeconds="120"

timeToLiveSeconds="120"

maxElementsOnDisk="10000000"

diskExpiryThreadIntervalSeconds="120"

memoryStoreEvictionPolicy="LRU">

设定缓存的默认属性数据:

1、maxElementsInMemory:指定该内存缓存可以存放缓存对象的最多个数;

2、eternal:设定缓存对象是否不会过期。若设为true,表示对象永远不会过期,此时会忽略timeToIdleSeconds与timeToLiveSeconds属性。默认值为false;

3、timeToldleSeconds:设定允许对象处于空闲状态的最长时间,以秒为单位。当对象自从最近一次被访问后,若处于空闲状态的时间超过了timeToldleSeconds设定的值,这个对象就会过期。当对象过期,ehcache就会将它从缓存中清除。设置值为0,则对象可以无限期地处于空闲状态。

4、timeToLiveSeconds:设定对象允许存在于缓存中的最长时间,以秒为单位。当对象自从被存放到缓存后,若处于缓存中的时间超过了timeToLiveSeconds设定的值,这个对象就会过期。当对象过期,ehcache就会将它从缓存中清除。设置值为0,则对象可以无限期地存在于缓存中。注意,只有timeToLiveSeconds>=timeToldleSeconds时,才有意义;

5、overflowToDisk:设定为true,表示当缓存对象达到maxElementsInMemory界限时,会将溢出的对象写到元素指定的硬盘目录缓存中;

6、maxElementsOnDisk:指定硬盘缓存区可以存放缓存对象的最多个数;

7、diskPersistent:指定当程序结束时,硬盘缓存区中的缓存对象是否做持久化;

8、diskExpiryThreadIntervalSeconds:指定硬盘中缓存对象的失效时间间隔;

9、memoryStoreEvictionPolicy:如果内存缓存区超过限制,选择移向硬盘缓存区中的对象时使用的策略。支持三种策略:

FIFO:First In First Out,先进先出;

LFU:Less Frequenlty Used,最少使用;

LRU,Least Recently Used,最近最少使用;

3.3 启用ehcache缓存机制

在映射文件的mapper中的中通过type指定缓存机制为ehcache缓存。默认为myBatis内置的二级缓存org.apache.ibatis.cache.impl.PerpetualCache。

该类在mybatis-ehcache的jar包中可以找到。

98800885f8284ef5f6fca4aadef9128d.png

3.4 ehcache在不同mapper中的个性化设置

在ehcache.xml中设置的属性值,会对该项目中所有使用ehcache缓存机制的缓存区域起作用。一个项目中可以有多个mapper,不同的mapper有不同的缓存区域。对于不同缓存区域也可进行专门针对于当前区域的个性化设置,可通过指定不同mapper的属性值来设置。

属性值的优先级高于ehcache.xml中的属性值。

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

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

相关文章

matplotlib画图_漂亮,超详细的matplotlib画图基础

来自 | 逐梦erhttps://zhumenger.blog.csdn.net/article/details/106530281本文仅作技术交流&#xff0c;如有侵权&#xff0c;请联系后台删除。数据可视化非常重要&#xff0c;因为错误或不充分的数据表示方法可能会毁掉原本很出色的数据分析工作。matplotlib 库是专门用于开发…

c# 2维数组 取一维_C#| 不同类型的一维数组声明

c# 2维数组 取一维In the below example, we are declaring an integer array (one dimensional) with following styles: 在下面的示例中&#xff0c;我们声明具有以下样式的整数数组(一维) &#xff1a; 1) One dimensional Array declaration with initialization (without…

Java编程经典10道_Java经典编程题50道之十二

企业发放的奖金根据利润提成&#xff1a;利润(I)低于或等于10万元时&#xff0c;奖金可提10%&#xff1b;利润高于10万元&#xff0c;低于20万元时&#xff0c;低于10万元的部分按10%提成&#xff0c; 高于10万元的部分 &#xff0c;可提成7.5%&#xff1b;20万到40万之间时&am…

RHEL7 单独安装图形 X11

RHEL7 默认是最小化安装&#xff08;Minimal Install&#xff09;&#xff0c;没有图形界面&#xff0c; 我们应该选择Server with GUI。若已错过此步骤&#xff0c;我们采用以下方式补充安装GUI界面。 先配置yum源可以参考我的这篇文章http://blog.itpub.net/27771627/viewspa…

android recycleview长按多选_UI设计中Android和IOS设计差异总结

由于设计师、产品经理使用的移动设备大部分是iPhone&#xff0c;所以在做设计时&#xff0c;容易忽略Android和iOS的差异&#xff0c;按照iOS的规范进行设计&#xff0c;两端只做一套。只做一套的会存在两个问题&#xff1a;1、安卓用户的使用习惯不太适应iOS的设计&#xff0c…

Kotlin程序用于打印JVM版本的Kotlin(打印Java属性)

Here, we will create a Kotlin program to print Kotlin, JVM version (printing Java properties). As Kotlin can be seen as an upgrade of Java, so we will get all versions of java (JVM) using Kotlin also. 在这里&#xff0c;我们将创建一个Kotlin程序以打印JVM版本…

自定义动画属性java_创建酷炫动画效果的10个JavaScript库

原标题&#xff1a;创建酷炫动画效果的10个JavaScript库1) Dynamics.jsDynamics.js是设计基于物理规律的动画的重要Java库。它可以赋予生命给所有包含CSS 和SVG属性的DOM(文本对象模型)元素&#xff0c;换句话说&#xff0c;Dynamics.js适用于所有Java对象以及一系列其它的元素…

php xlsx里插入图片_常见的 PHP 面试题和答案分享

如何直接将输出显示给浏览器&#xff1f;将输出直接显示给浏览器&#xff0c;我们必须使用特殊标记 <&#xff1f;and&#xff1f;>。PHP 是否支持多重继承&#xff1f;PHP 只支持单继承。PHP 的类使用关键字 extends 继承另一个类获取图片属性&#xff08;size, width, …

java调用构造函数中某一个值_Java如何在枚举的构造函数中调用另一个枚举值

Java中的枚举(enum)是一种存储一组常量值的数据类型。您可以使用枚举来存储固定值&#xff0c;例如一周中的天&#xff0c;一年中的月等。您可以使用关键字 enum定义枚举&#xff0c;后跟枚举的名称为-enum Days {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATUR…

python 示例_Python日历类| yeardatescalendar()方法与示例

python 示例Python Calendar.yeardatescalendar()方法 (Python Calendar.yeardatescalendar() Method) Calendar.yeardatescalendar() method is an inbuilt method of the Calendar class of calendar module in Python. It uses an instance of this class and returns a lis…

js:插入节点appendChild insertBefore使用方法

首先 从定义来理解 这两个方法&#xff1a; appendChild() 方法&#xff1a;可向节点的子节点列表的末尾添加新的子节点。语法&#xff1a;appendChild(newchild) insertBefore() 方法&#xff1a;可在已有的子节点前插入一个新的子节点。语法 &#xff1a;insertBefore(newchi…

pandas concat_pandas-数据合并-concat(最全参数解释,含代码和实例)

pandas中的concat的功能&#xff1a;假设你现在需要将多个数据合并&#xff0c;前提是&#xff1a;这几个文件列名都一致&#xff0c;也就是说这几个文件格式完全一样&#xff0c;只是数据不太一样&#xff0c;类似于合并多个文件这种&#xff0c;实际数据分析中也会遇到这种情…

java中的de是什么_【转】java中main函数解析

源地址&#xff1a;http://www.cnblogs.com/xwdreamer/archive/2012/04/09/2438845.html从写java至今&#xff0c;写的最多的可能就是主函数public static void main(String[] args) {}但是以前一直都没有问自己&#xff0c;为什么要这么写&#xff0c;因为在c语言中就没有这样…

JAVA多线程(一)线程安全问题产生的原因

JAVA线程内存与主存间映射示意图Java内存模型中规定了所有的变量都存储在主内存中&#xff0c;每条线程还有自己的工作内存&#xff0c;线程的工作内存中保存了该线程使用的变量到主内存副本拷贝&#xff0c;线程对变量的所有操作&#xff08;读取、赋值&#xff09;都必须在工…

两顶点的路径长度为k_计算两个顶点之间的所有可能路径

两顶点的路径长度为kWhat to Learn? 学什么&#xff1f; How to count all possible paths between two vertices? 如何计算两个顶点之间的所有可能路径&#xff1f; In the graph there are many alternative paths from vertex 0 to vertex 4 在图中&#xff0c;有许多从…

php debug_print_backtrace,php中debug_backtrace、debug_print_backtrace和匿名函数用法实例

本文实例讲述了php中debug_backtrace、debug_print_backtrace和匿名函数用法。分享给大家供大家参考。具体分析如下&#xff1a;debug_print_backtrace() 是一个很低调的函数,很少有人注意过它.不过当我们对着一个对象调用另一个对象再调用其它的对象和文件中的一个函数出错时,…

covariance matrix r语言_时间序列分析|ARIMAX模型分步骤详解和R中实践

这是关于时间序列的第N篇文章&#xff0c;本文将介绍ARIMAX模型&#xff0c;简单来说就是在ARIMA的基础上增加一个外生变量。ARIMAX和ARIMA相比在理论上没有太多新的内容&#xff0c;所以本文直接介绍在R里怎么一步一步跑ARIMAX。在阅读这篇文章前&#xff0c;需要对ARIMA有一定…

linux系统编程之文件与I/O(六):fcntl 函数与文件锁

2013-05-14 11:26 8290人阅读 评论(2) 收藏 举报分类&#xff1a;linux系统编程&#xff08;19&#xff09; 版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 一、fcntl函数 功能&#xff1a;操纵文件描述符&#xff0c;改变已打开的文件的属性 int…

python 使用异常函数_您如何测试Python函数引发异常?

python 使用异常函数This article elaborates on how to implement a test case for a function that raises an exception. 本文详细介绍了如何为引发异常的函数实现测试用例 。 Consider the following function: 考虑以下功能&#xff1a; import redef check_email_forma…