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

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. 转 Shell调试篇

    检查语法 -n选项只做语法检查,而不执行脚本. sh -n script_name.sh 启动调试 sh -x script_name.sh 进入调试模式后,Shell依次执行读入的语句,产生的输出中 ...

  2. 设计模式学习之代理模式(Proxy)

    一.代理模式的初衷 不想让客户端直接访问具体实现接口,客户端不应该直接访问具体实现,一般会涉及到以下四个对象 业务接口:定义客户端访问的接口,是实现类和代理类都需要实现的 实现类:实现了业务接口,真正 ...

  3. Spring: (一) -- 春雨润物之 核心IOC

    作为一个Java人,想必都或多或少的了解过Spring.对于其优势也能道个一二,诸如方便解耦.支持AOP编程.支持声明式事务.方便测试等等.Spring也不仅仅局限于服务器端开发,它可以做非常多的事情 ...

  4. 卸载掉原有mysql

    [root@xiaoluo ~]# rpm -qa | grep mysql // 这个命令就会查看该操作系统上是否已经安装了mysql数据库 有的话,我们就通过 rpm -e 命令 或者 rpm - ...

  5. Python批量下载电视剧电影--自己动手丰衣足食

    前言 为了看美剧<天蝎>,在电影天堂找到了,于是就想下载下来好好欣赏. 废话不说了,直接上代码. 代码 import requests,re,os,time url = "htt ...

  6. iOS 声明属性关键字讲解

    atomic: 原子操作(原子性是指事务的一个完整操作,操作成功就提交,反之就回滚. 原子操作就是指具有原子性的操作)在objective-c 属性设置里面 默认的就是atomic ,意思就是 set ...

  7. jboss之启动加载过程详解

    今天看了看jboss的boot.log和server.log日志,结合自己的理解和其他的资料,现对jboss的启动和加载过程做出如下总结: boot.xml是服务器的启动过程的日志,不涉及后续的操作过 ...

  8. The user specified as a definer ('root'@'%') does not exist 解决方法

    mysql> grant all privileges on *.* to root@"%" identified by "."; Query OK, r ...

  9. Bootstrap Datatable 简单的基本配置

    $(document).ready(function() {     $('#example').dataTable({ "sScrollX": "100%", ...

  10. Windows提高_2.3第三部分:内核区同步

    第三部分:内核区同步 等待函数(WaitForObject) 等待函数的形式 单个:WaitForSingleObject 多个:WaitForMultipleObjects 一个可以被等待的对象通常 ...