引子:
再上一篇文章中介绍了一级缓存,但是也可以看出一些一级缓存的局限,一是一级缓存的范围是session级别的,不同的session之间无法实现缓存共享,二是一级缓存是放到内存中的一旦数据过大有可能会出现内存不足的情况。为此mybatis还有一种二级缓存机制。
二级缓存
首先二级缓存的范围要比session大属于mapper级别的,即在同一个namespace下的缓存,因此可以实现不同session之间共享缓存,其次二级缓存既可以利用Mybatis自带的PerpetualCache ,也可利用其他缓存框架(如EhCache),前提是要实现mybatis中的Cache接口,同时缓存不一定会放在内存中,所以被缓存的数据要声明可序列化.
二级缓存的条件:
- settings中开启二级缓存,cacheEnabled为true
- 2级缓存的类型要实现序列化接口Serializable
- mapper文件设置支持二级缓存
- select标签useache=true(默认为true)
- session提交或关闭后才会将session中的一级缓存刷入到二级缓存
OK我们可以先来看一看运行代码
SqlSession session =factory.openSession();
SqlSession session2 = factory.openSession();
BbsuserMapper bbsuserMapper1 = session.getMapper(BbsuserMapper.class);
Bbsuser bbsuser = new Bbsuser();
System.out.println("二级缓存");
BbsuserMapper bbsuserMapper2 = session2.getMapper(BbsuserMapper.class);
bbsuser.setName("李四");
bbsuser.setPassword("456");
System.out.println(bbsuserMapper2.searchBbuser(bbsuser));
//关闭或者提交session后,一级缓存会到二级缓存中
session2.commit();
session2.close();
System.out.println("调用其他session");
System.out.println(bbsuserMapper1.searchBbuser(bbsuser));
session.commit();
session.close();
可以看到,当我们关闭了session2后,session依然找到了缓存这边是二级缓存,也就是mapper级别的缓存.但是如果在session2关闭前,执行了更新等操作时会清空一级缓存也就会导致没有将一级缓存放入到二级缓存.
可以看到在session2执行了插入语句后,二级缓存里并没有这个数据了..
二级缓存的问题
前面也说过了二级缓存是mapper级别的缓存,因此一旦有其他的mapper对数据库里通样的执行了更新等操作的话,很有可能会造成数据的不一致.这里写了一个测试方法:
public void badAche() {
SqlSession session =factory.openSession();
SqlSession session2 = factory.openSession();
BbsuserMapper bbsuserMapper1 = session.getMapper(BbsuserMapper.class);
Bbsuser bbsuser = new Bbsuser();
bbsuser.setName("张三");
bbsuser.setPassword("123");
//查询一个用户下收到的消息数量
System.out.println("二级缓存问题:\n"+bbsuserMapper1.searchBbuser(bbsuser).getMessage().size());
session.close();
MessageMapper mapper = session2.getMapper(MessageMapper.class);
BbsuserMapper bbsuserMapper2 = session2.getMapper(BbsuserMapper.class);
//利用其他mapper下插入一个消息
mapper.insertMessage(new Message("张三", "李四", "问题", new Date(), "ss"));
System.out.println("在不同namespace下执行了更新操作\n");
session2.commit();
System.out.println("二级缓存仅针对一个namespace下");
System.out.println(bbsuserMapper2.searchBbuser(bbsuser).getMessage().size());
session2.close();
}
结果
可以看到利用其他的MessageMapper插入后,BbsuserMapper 并不会刷新二级缓存,而这也是比较局限的地方,但是我们可以通过配置定时刷新二级缓存来减少这样的数据不一致