一级缓存和二级缓存的区别:

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>
flushCache:将其设置为true,无论语句什么时候被调用,都会导致缓存被清空。默认值:false。
useCache:将其设置为true,将会导致本条语句的结果被缓存。默认值:true。

测试代码如下:

注意:此时在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-缓存使用的更多相关文章

  1. Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法

    一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基 ...

  2. 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 ...

  3. SpringMVC4 + Spring + MyBatis3 基于注解的最简配置

    本文使用最新版本(4.1.5)的springmvc+spring+mybatis,采用最间的配置方式来进行搭建. 1. web.xml 我们知道springmvc是基于Servlet: Dispatc ...

  4. Mybatis3 框架理解

    最近工作比较闲,维护一个政府机关的短信发送平台,大部分业务逻辑都在Oracle数据库上,但自己明明应聘的是Java开发啊!!!整天写存储过程的我还是有一颗写高级语言的心啊!!!好吧!!!先找个数据库方 ...

  5. SpringMVC4 + Spring + MyBatis3

    SpringMVC4 + Spring + MyBatis3 本文使用最新版本(4.1.5)的springmvc+spring+mybatis,采用最间的配置方式来进行搭建. 1. web.xml 我 ...

  6. 6.MyBaits的分页和缓存查询

    1. 创建javaweb项目MyBaits_Page_CaChe 2.在项目的WebRoot下的WEB-INF下的lib文件下加入jar文件 log4j-1.2.17.jar mybatis-3.2. ...

  7. MyBatis-3.2.2

    note SqlSessionFactory 它是一个线程安全的 SqlSession 线程非安全,不能做类的公用变量 当数据库字段和实体对象名称不一至时,通过sql的字段命名别名,别名跟实体对象属性 ...

  8. MyBatis框架——动态SQL、缓存机制、逆向工程

    MyBatis框架--动态SQL.缓存机制.逆向工程 一.Dynamic SQL 为什么需要动态SQL?有时候需要根据实际传入的参数来动态的拼接SQL语句.最常用的就是:where和if标签 1.参考 ...

  9. Mybatis3 快速入门

    Mybatis3 快速入门 目前常见的持久层java框架有Hibernate,Mybatis,SpringData.笔者比较喜欢用SpringData.Hibernate 和 Mybatis 也经常用 ...

  10. Mybatis【逆向工程,缓存,代理】知识要点

    前言 本文主要讲解Mybatis的以下知识点: Mybatis缓存 一级缓存 二级缓存 与Ehcache整合 Mapper代理 使用Mapper代理就不用写实现类了 逆向工程 自动生成代码 Mybat ...

随机推荐

  1. Spark SQL概念学习系列之Spark SQL入门(八)

    前言 第1章   为什么Spark SQL? 第2章  Spark SQL运行架构 第3章 Spark SQL组件之解析 第4章 深入了解Spark SQL运行计划 第5章  测试环境之搭建 第6章 ...

  2. LN : leetcode 263 Ugly Number

    lc 263 Ugly Number lc 263 Ugly Number Write a program to check whether a given number is an ugly num ...

  3. JAVA使用Ldap操作AD域

    项目上遇到的需要在集成 操作域用户的信息的功能,第一次接触ad域,因为不了解而且网上其他介绍不明确,比较费时,这里记录下. 说明: (1). 特别注意:Java操作查询域用户信息获取到的数据和域管理员 ...

  4. IFormattable,ICustomFormatter, IFormatProvider接口

    定                 义 1.IFormattable   提供一种功能,用以将对象的值格式化为字符串表示形式. 2.IFormatProvider  提供用于检索控制格式化的对象的机制 ...

  5. WordPress腾讯云存储搭建教程,完美解决

    写在前面的话: 为什么会有今天的话题:WordPress+腾讯云存储? 因为博主不想使用七牛云,也不想使用又拍云,所以才有了今天的话题. 在使用腾讯云存储的过程中是很不顺利的,万幸的是现在终于完美融合 ...

  6. Appium环境部署

    Appium 是一个开源.跨平台的自动化测试工具,用于测试原生和轻量移动应用,支持 iOS, Android平台. 需要部署的软件:python环境.nodejs..net framework4.5. ...

  7. Android屏幕尺寸与度量单位(px,dp,sp)简介

    MarkdownPad Document *:first-child { margin-top: 0 !important; } body>*:last-child { margin-botto ...

  8. jsp学习笔记 - 内置对象 session

    1.session 主要用来用户的登录和注销 设置用户名,获取用户名 session.setAttribute("username","johnson"); s ...

  9. C#基础知识面试经典[整理]

    个人网站:http://www.51pansou.com .net视频下载:.net视频教程 .net源码下载:.net源码 当初学 C# 时是找个人大概问了一下数据类型和分支语句就开始做项目了.这两 ...

  10. <东方梦符祭> N1无尽30波终于通了

    嘛也算是第一次通关 纪念一下 阵容:xjb搭的杂牌队 主C:古明绝恋 露米娅(真·R卡战神)比那名居天子 斯卡雷特 控制:琪露诺 蕾蒂 灵梦 挂件:小伞 纳兹琳 古明地觉 永江依玖 第一发就直接抽到了 ...