MyBatis 二级缓存使用

2018-01-09 10:30:16来源:oschina作者:IamOkay人点击

分享

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


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"属性即可。如:



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 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 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 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 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节点即可,参考配置如下:



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

这个更高级的配置创建了一个FIFO缓存,并每隔60秒刷新,存数结果对象或列表的512个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会 导致冲突。可用的收回策略有,默认的是LRU:

LRU–最近最少使用的:移除最长时间不被使用的对象。
FIFO–先进先出:按对象进入缓存的顺序来移除它们。
SOFT–软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK–弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

测试代码如下:


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 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 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 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 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 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依赖:





org.mybatis.caches
mybatis-ehcache
1.1.0




org.slf4j
slf4j-log4j12
1.7.25

而对于使用log4j组件时,需要下载一个特殊的包slf4j-log4j12才能正常输出日志,参考:http://jiajun-kucoo.blog.163.com/blog/static/64148688201352791439772/


2、在resources中增加ehcache.xml文件,内容如下:


<?xml version="1.0" encoding="UTF-8"?>



maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="FIFO">



详细配置参考:http://blog.csdn.net/etttttss/article/details/17141485


3、在user.xml中,也就是Mapper文件配置第三方缓存:





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


自定义自己的缓存:


其实从上面的配置来看,节点中的type指定的是具体的类,而这个类继承的是Cache接口,所以只要自己自定义继承这个接口实现自己特定类也能达到缓存效果。


基本上不用自己写这些自定义缓存,因为提供的第三方缓存框架来说已经很强大了。


1、参考:http://www.cnblogs.com/lzy1991/p/5335249.html

微信扫一扫

第七城市微信公众平台