MyBatis3-缓存使用
一级缓存和二级缓存的区别:
1、一级缓存:基于PerpetualCache的HashMap本地缓存,其存储作用域为同一个SqlSession,当Session flush或close之后,该Session中的所有Cache就将清空。
2、二级缓存:与一级缓存其机制相同,默认也是采用PerpetualCache,HashMap存储,不同在于其存储作用域为Mapper(Namespace),并且可自定义存储源,如Ehcache。
3、对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了C/U/D操作后,默认该作用域下所有select中的缓存将被clear。
如果要实现MyBatis的二级缓存,一般来说有如下两种方式:
1. 采用MyBatis内置的Cache机制。
2. 采用三方Cache框架, 比如EhCache, OSCache等等。
下面是基于MyBatis的内置的一级和二级缓存测试:
1、一级缓存配置
默认是开启的,如果不想用缓存,直接在select节点中增加useCache="false"和flushCache="true"属性即可。如:
<select id="getUserArticles" parameterType="int" resultMap="resultUserArticleList" useCache="false" flushCache="true">
select user.id,user.userName,user.userAddress,article.id as aid,article.title,article.content from user,article where user.id=article.userid and user.id=#{id}
</select>
测试代码如下:
注意:此时在select节点中缓存是开启的。
package com.jsoft.testmybatis.test1; import java.io.IOException;
import java.io.InputStream;
import java.util.List; import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder; import com.jsoft.testmybatis.inter.IUserOperation;
import com.jsoft.testmybatis.models.Article;
import com.jsoft.testmybatis.models.User; public class App {
public static void main(String[] args) throws IOException {
InputStream inputStream = Resources.getResourceAsStream("Configuration.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
IUserOperation userOperation = session.getMapper(IUserOperation.class); try {
System.out.println("第一次查询开始");
//List
List<User> users = userOperation.selectUsers("%");
for (User tempUser : users) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("第一次查询结束"); System.out.println("第二次查询开始");
//List
List<User> users2 = userOperation.selectUsers("%");
for (User tempUser : users2) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("第二次查询结束"); System.out.println("加入数据开始开始");
//Add
User addUser = new User();
addUser.setUserAddress("guangdong,guangzhou");
addUser.setUserName("eason");
addUser.setUserAge("80");
int addRetCount = userOperation.addUser(addUser);
session.commit();//必须提交事务,否则不会写入到数据库。如果session不commit,那么,数据就不会放入cache中。所以,只有commit后,才能取得。
System.out.println("增加数据影响的行数:" + addRetCount);
if (addUser.getId() > 0) {
System.out.println("增加数据成功,新增的id为:" + addUser.getId());
}
System.out.println("加入数据开始结束"); System.out.println("第三次查询开始");
//List
List<User> users3 = userOperation.selectUsers("%");
for (User tempUser : users3) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("第三次查询结束"); //强制刷新缓存
session.clearCache(); System.out.println("第四次查询开始");
//List
List<User> users4 = userOperation.selectUsers("%");
for (User tempUser : users4) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("第三次查询结束"); } catch (Exception e) {
// TODO: handle exception
session.rollback();//有异常时回滚数据
e.printStackTrace();
} } finally {
session.close();//close之后缓存清空
}
}
}
结果如下:

可以看出,缓存生效了。
下面测试select节点中配置缓存关闭的情况,结果如下:

可以看出缓存去除了,全部都是真实查询。
其实上面的测试示例还少了一个Session2的测试,不然效果不佳。
2、二级缓存测试:
开启二级缓存,在XML配置文件中添加Cache节点即可,参考配置如下:
<cache eviction="FIFO" <!--回收策略为先进先出 -->
flushInterval="60000" <!--自动刷新时间60s -->
size="512" <!--最多缓存512个引用对象 -->
readOnly="true"/> <!--只读 -->
测试代码如下:
package com.jsoft.testmybatis.test1; import java.io.IOException;
import java.io.InputStream;
import java.util.List; import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder; import com.jsoft.testmybatis.inter.IUserOperation;
import com.jsoft.testmybatis.models.Article;
import com.jsoft.testmybatis.models.User; public class App2 {
public static void main(String[] args) throws IOException {
InputStream inputStream = Resources.getResourceAsStream("Configuration.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
SqlSession session2 = sqlSessionFactory.openSession();
try {
IUserOperation userOperation = session.getMapper(IUserOperation.class);
IUserOperation userOperation2 = session.getMapper(IUserOperation.class);
try {
System.out.println("Session1第一次查询开始");
//List
List<User> users = userOperation.selectUsers("%");
for (User tempUser : users) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("Session1第一次查询结束"); System.out.println("Session2第二次查询开始");
//List
List<User> users2 = userOperation2.selectUsers("%");
for (User tempUser : users2) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("Session2第二次查询结束"); System.out.println("Session1加入数据开始开始");
//Add
User addUser = new User();
addUser.setUserAddress("guangdong,guangzhou");
addUser.setUserName("eason");
addUser.setUserAge("80");
int addRetCount = userOperation.addUser(addUser);
session.commit();//必须提交事务,否则不会写入到数据库。如果session不commit,那么,数据就不会放入cache中。所以,只有commit后,才能取得。
System.out.println("增加数据影响的行数:" + addRetCount);
if (addUser.getId() > 0) {
System.out.println("增加数据成功,新增的id为:" + addUser.getId());
}
System.out.println("Session1加入数据开始结束"); System.out.println("Session1第三次查询开始");
//List
List<User> users3 = userOperation.selectUsers("%");
for (User tempUser : users3) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("Session1第三次查询结束"); //强制刷新缓存
session.clearCache(); System.out.println("Session1第四次查询开始");
//List
List<User> users4 = userOperation.selectUsers("%");
for (User tempUser : users4) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("Session1第四次查询结束"); System.out.println("Session2第五次查询开始");
//List
List<User> users5 = userOperation2.selectUsers("%");
for (User tempUser : users5) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("Session2第五次查询结束");
} catch (Exception e) {
// TODO: handle exception
session.rollback();//有异常时回滚数据
session2.rollback();//有异常时回滚数据
e.printStackTrace();
} } finally {
session.close();//close之后缓存清空
session2.close();//close之后缓存清空
}
}
}
测试结果如下:

可以看出Cache不受Session的限制,且操作缓存的方法是一致的。
3、总结
1、映射语句文件中的所有select语句将会被缓存。
2、映射语句文件中的所有insert,update和delete语句会刷新缓存。
3、缓存会使用Least Recently Used(LRU,最近最少使用的)算法来收回。
4、缓存会根据指定的时间间隔来刷新。
5、每个缓存可以存储 1024 个列表或对象的引用(不管查询出来的结果是什么) 。
6、缓存将作为“读/写”缓存,意味着获取的对象不是共享的且对调用者是安全的。不会有其它的调用者或线程潜在修改。
4、参考:
http://www.yihaomen.com/article/java/428.htm(文中结论主要转自此篇)
http://www.cnblogs.com/xdp-gacl/p/4270403.html(文中结论主要转自此篇)
http://blog.csdn.net/u012373815/article/details/47069223
http://blog.csdn.net/luanlouis/article/details/41390801
http://blog.csdn.net/luanlouis/article/details/41280959
http://blog.csdn.net/luanlouis/article/details/41408341
5、测试工程:https://github.com/easonjim/5_java_example/tree/master/mybatis/test12
下面是第三方缓存框架EhCache的配置使用:
说明:为什么要使用EhCache,因为它提供了很多内置缓存没有的强大功能,比如缓存的集群等。但是有一点,EhCache使用的日志框架是slf4j,需要而外配置使用slf4j,如果不想使用slf4j,也可以配置使用log4j。下面就是使用log4j的示例。
1、添加POM依赖:
<!-- ehcache -->
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
<!-- change slf4j for log4j -->
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
而对于使用log4j组件时,需要下载一个特殊的包slf4j-log4j12才能正常输出日志,参考:http://jiajun-kucoo.blog.163.com/blog/static/64148688201352791439772/
2、在resources中增加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" />
<!--
name:Cache的唯一标识
maxElementsInMemory:内存中最大缓存对象数
maxElementsOnDisk:磁盘中最大缓存对象数,若是0表示无穷大
eternal:Element是否永久有效,一但设置了,timeout将不起作用
overflowToDisk:配置此属性,当内存中Element数量达到maxElementsInMemory时,Ehcache将会Element写到磁盘中
timeToIdleSeconds:设置Element在失效前的允许闲置时间。仅当element不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds:设置Element在失效前允许存活时间。最大时间介于创建时间和失效时间之间。仅当element不是永久有效时使用,默认是0.,也就是element存活时间无穷大
diskPersistent:是否缓存虚拟机重启期数据
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)
备注: 持久化到硬盘的路径由虚拟机参数"java.io.tmpdir"决定. 例如, 在Windows中, 会在此路径下 C:\Documents and Settings\Jim\Local Settings\Temp,在Linux中, 通常会在:/tmp下,测试:System.out.println(System.getProperty("java.io.tmpdir"));
-->
<defaultCache maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="FIFO">
</defaultCache>
<!--
<cache name="test" overflowToDisk="true" eternal="false"
timeToIdleSeconds="300" timeToLiveSeconds="600" maxElementsInMemory="1000"
maxElementsOnDisk="10" diskPersistent="true" diskExpiryThreadIntervalSeconds="300"
diskSpoolBufferSizeMB="100" memoryStoreEvictionPolicy="LRU" />
-->
</ehcache>
详细配置参考:http://blog.csdn.net/etttttss/article/details/17141485
3、在user.xml中,也就是Mapper文件配置第三方缓存:
<!-- 以下两个<cache>标签二选一,第一个可以输出日志,第二个不输出日志 -->
<cache type="org.mybatis.caches.ehcache.LoggingEhcache" />
<!-- <cache type="org.mybatis.caches.ehcache.EhcacheCache"/> -->
4、最后在log4j.properties文件中增加日志的切入点
log4j.logger.net.sf.ehcache=DEBUG
这句话的意思是在这个包net.sf.ehcache下的DEBUG级别的日志全部输出。
5、打印的日志如下:

6、参考:
http://www.yihaomen.com/article/java/428.htm
http://www.mybatis.org/ehcache-cache/dependencies.html
7、测试工程:https://github.com/easonjim/5_java_example/tree/master/mybatis/test13
自定义自己的缓存:
其实从上面的配置来看,<cache type="org.mybatis.caches.ehcache.LoggingEhcache" />节点中的type指定的是具体的类,而这个类继承的是Cache接口,所以只要自己自定义继承这个接口实现自己特定类也能达到缓存效果。
基本上不用自己写这些自定义缓存,因为提供的第三方缓存框架来说已经很强大了。
1、参考:http://www.cnblogs.com/lzy1991/p/5335249.html
MyBatis3-缓存使用的更多相关文章
- Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法
一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基 ...
- mybatis3.3 + struts2.3.24 + mysql5.1.22开发环境搭建及相关说明
一.新建Web工程,并在lib目录下添加jar包 主要jar包:struts2相关包,mybatis3.3相关包,mysql-connector-java-5.1.22-bin.jar, gson-2 ...
- SpringMVC4 + Spring + MyBatis3 基于注解的最简配置
本文使用最新版本(4.1.5)的springmvc+spring+mybatis,采用最间的配置方式来进行搭建. 1. web.xml 我们知道springmvc是基于Servlet: Dispatc ...
- Mybatis3 框架理解
最近工作比较闲,维护一个政府机关的短信发送平台,大部分业务逻辑都在Oracle数据库上,但自己明明应聘的是Java开发啊!!!整天写存储过程的我还是有一颗写高级语言的心啊!!!好吧!!!先找个数据库方 ...
- SpringMVC4 + Spring + MyBatis3
SpringMVC4 + Spring + MyBatis3 本文使用最新版本(4.1.5)的springmvc+spring+mybatis,采用最间的配置方式来进行搭建. 1. web.xml 我 ...
- 6.MyBaits的分页和缓存查询
1. 创建javaweb项目MyBaits_Page_CaChe 2.在项目的WebRoot下的WEB-INF下的lib文件下加入jar文件 log4j-1.2.17.jar mybatis-3.2. ...
- MyBatis-3.2.2
note SqlSessionFactory 它是一个线程安全的 SqlSession 线程非安全,不能做类的公用变量 当数据库字段和实体对象名称不一至时,通过sql的字段命名别名,别名跟实体对象属性 ...
- MyBatis框架——动态SQL、缓存机制、逆向工程
MyBatis框架--动态SQL.缓存机制.逆向工程 一.Dynamic SQL 为什么需要动态SQL?有时候需要根据实际传入的参数来动态的拼接SQL语句.最常用的就是:where和if标签 1.参考 ...
- Mybatis3 快速入门
Mybatis3 快速入门 目前常见的持久层java框架有Hibernate,Mybatis,SpringData.笔者比较喜欢用SpringData.Hibernate 和 Mybatis 也经常用 ...
- Mybatis【逆向工程,缓存,代理】知识要点
前言 本文主要讲解Mybatis的以下知识点: Mybatis缓存 一级缓存 二级缓存 与Ehcache整合 Mapper代理 使用Mapper代理就不用写实现类了 逆向工程 自动生成代码 Mybat ...
随机推荐
- java 分解整数 【个 十 百】(数组案例)
求一个数两位数的个位数,十位数,百位数及千位: int num = 53; int g = (num / 1) % 10; //个位 int s = (num / 10) % 10; //十位 in ...
- [ POI 2010 ] Antisymmetry
\(\\\) \(Description\) 给出一个长度为 \(N\) 的二进制串,定义一个子串是优秀的,当且仅当其正着看,和倒着按位取反后看结果是一样的,求整个串有多少个优秀的子串. \(N\le ...
- P3373 【模板】线段树 2 区间求和 区间乘 区间加
题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含三个整数N.M.P,分别 ...
- rabbitmq 简单示例(Hello World)
一:消息中间件: AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计 RabbitMQ是实现AMQP( ...
- [算法天天练] - C语言实现约瑟夫环(2)
Linux下 #include <stdlib.h>#include <stdio.h> int main(){ int n,m,i,s = 0; printf("E ...
- MySQL性能优化必备25条
1. 为查询缓存优化你的查询 大多数的MySQL服务器都开启了查询缓存.这是提高性最有效的方法之一,而且这是被MySQL的数据库引擎处理的.当有很多相同的查询被执行了多次的时候,这些查询结果会被放到一 ...
- 编写Java脚本统计工程代码总行数
在新公司工作将近一年了,一直独自一人负责服务端集群的运维和代码的编写.不知不觉从一个Project发展到了七八个Project. 看着越来越多的代码,今天突然想统计一下一共写了多少代码.[这里只统计完 ...
- Mac上简单常用Terminal命令
方案1 SSH是一个非常伟大的工具,如果你要在互联网上远程连接到服务器,那么SSH无疑是最佳的候选.SSH是加密的,OpenSSH加密所有通信(包括密码),有效消除了窃听,连接劫持和其它攻击.本文将为 ...
- 安卓app测试之Monkeyrunner
一.MonkeyRunner简介 MonkeyRunner提供了系列的API ,MonkeyRunner可以完成模拟事件及截图操作 ,分为以下三类: MonkeyRunner:用来连接设备或模拟器的 ...
- php中 如何找到session 的保存位置
[前言] 刚刚想测试FQ操作,需要删除session,这里记录分享下 [主体] (1)想要查看session保存的目录,需要先找到 php.ini配置文件 (2)在php.ini文件中查找 sessi ...