Mybatis之二级缓存(八)
1. 介绍
Mybatis缓存分为一级缓存和二级缓存,在本节中我们介绍下二级缓存的使用及其特性
MyBatis的一级缓存是在一个Session域内有效的,当Session关闭后,缓存内容也随之销毁。但是Mybatis的一级缓存在Spring中是没有作用的,而我们实际项目中也经常把Mybatis和Spring整合到一起使用。这个时候我们就需要用到二级缓存。
2. 准备
请先完成Mybatis之一级缓存(七)章节的内容。
3. Mybatis二级缓存学习
1) 开启二级缓存
Mybatis是默认不开启二级缓存的,要开启二级缓存我们需要做如下准备:
a. 调整Mybatis.xml,在<configuration>节点中增加<settings><setting name="cacheEnabled" value="true"></settings>。这里是Mybatis二级缓存的总开关。
<configuration> <settings> <setting name="cacheEnabled" value="true"/> </settings> . . . </configuration>
b. 调整Mapper文件,开启针对Mapper的二级缓存开关。
<mapper namespace="com.mybatis.dao.CacheDao"> <cache eviction="FIFO" flushInterval="60000" size="1024" readOnly="true"/> . . . </mapper>
eviction="FIFO" 是缓存的淘汰算法,可选值有"LRU"、"FIFO"、"SOFT"、"WEAK",缺省值是LRU。
flushInterval="60000" 指缓存过期时间,单位为毫秒,缺省值为空,即只要容量足够,永不过期。
size="1024" 缓存容量。
readOnly="true" 是否只读。
如果为true, 则所有相同的sql返回的是同一个对象(有助于提高性能,但并发操作同一条数据时,可能不安全)。
如果为false,则所有相同的sql返回的是同一个对象的副本拷贝。
c. 调整要缓存的对象,使其实现com.io.Serializabie接口。
package com.mybatis.entity; import java.io.Serializable; public class CacheInfo implements Serializable{ . . . }
2) 二级缓存的测试使用
package com.mybatis; import java.io.InputStream; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import com.mybatis.entity.CacheInfo; @SuppressWarnings("unused") public class TestMain { public static void main(String[] args) { InputStream iStream = TestMain.class.getClassLoader().getResourceAsStream("mybatis.xml"); SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(iStream); CacheInfo cacheInfo; SqlSession session1 = sessionFactory.openSession(); cacheInfo = session1.selectOne("com.mybatis.dao.CacheDao.getCache1", 1); System.out.println("第1次打印 ID:"+cacheInfo.getId()+","+cacheInfo.getTxt()); session1.close(); SqlSession session2 = sessionFactory.openSession(); cacheInfo = session2.selectOne("com.mybatis.dao.CacheDao.getCache1", 1); System.out.println("第1次打印 ID:"+cacheInfo.getId()+","+cacheInfo.getTxt()); session2.close(); } }
打印结果为:
2016-02-22 11:41:06.895 [DEBUG] com.mybatis.dao.CacheDao.getCache1 ==> Preparing: SELECT * FROM tbCache1 WHERE ID=? 2016-02-22 11:41:06.954 [DEBUG] com.mybatis.dao.CacheDao.getCache1 ==> Parameters: 1(Integer) 2016-02-22 11:41:07.021 [DEBUG] com.mybatis.dao.CacheDao.getCache1 <== Total: 1 第1次打印 ID:1,111 . . . 2016-02-22 11:41:07.021 [DEBUG] com.mybatis.dao.CacheDao Cache Hit Ratio [com.mybatis.dao.CacheDao]: 0.5 第2次打印 ID:1,111
从上面的结果中,可以看到“第2次打印”并没有从数据库中读取数据,而是直接读取的缓存。
4. 总结及其他
1)Mybatis的二级缓存可以跨Session访问,但是必须在同一个Session工厂内。
2)Mybatis的二级缓存更新是以Mapper(确切的说是namespace,因为Mybatis允许多个Mapper对用同一个namespace)为单位的,一个Mapper中的增删改查只能影响到当前Mapper所关联的缓存内容,不同Mapper之间的缓存是相互独立的。
3)Mybatis的二级缓存的弊端也就显而易见了。假如AMapper中是对A表的增删改查操作,BMapper中是对B表的增删改查操作,CMapper中执行了一个针对A表和B表的联合查找。那么,
首先,我们执行CMapper的联合查找,然后针对CMapper产生一个缓存对象。
然后,我们执行AMapper中对A表进行更新操作,由于二级缓存是针对Mapper为单位的,所以此时并不会更新CMapper中的缓存对象。
在这种情况下,我们再重新执行CMapper的联合查找(查询条件不变),就会读取CMapper中的二级缓存内容,也就会读到脏数据。
或许有人会说,我们可以把AMapper、BMapper、CMapper合并到一个Mapper中啊?这样会带来另外一些问题:
首先,这样会导致我们的单个的Mapper文件容量变得极大
其次,这样会导致这个融合后的Mapper对应的缓存文件不停的被更新,导致效率低下甚至失去缓存意义。