目录
- mybatis的一级缓存
- mybatis的二级缓存
- 和缓存框架的整合
- 二级缓存的问题
- 一级缓存
mybatis的一级缓存,指的是在一个SqlSession域内,session没有关闭的时候,执行缓存就会根据sql为key 进行缓存,如果缓存的那个数据进行了数据库操作,比如修改,删除等,mybatis的做法是将它从缓存中清掉,再查询的时候再往缓存里放,能保存缓存的准确性
mybatis默认支持一级缓存
public static void main(String[] args) throws IOException {
SqlSession session=MybatisUtil.oppenSession();
UserMapper userMapper=session.getMapper(UserMapper.class);
UserInfo user1=userMapper.getUserById(2); //进行查询,放缓存
UserInfo user2=userMapper.getUserById(2); //命中缓存
UserInfo user3=userMapper.getUserById(2); //命中缓存
UserInfo user6=userMapper.getUserById(3); //进行查询,放缓存
UserInfo user7=userMapper.getUserById(2); //命中缓存
UserInfo user8=userMapper.getUserById(2); //命中缓存
UserInfo user10=userMapper.getUserById(3); //命中缓存
UserInfo user11=userMapper.getUserById(3); //命中缓存
session.close();
}
从日志中可以看到,只输出2条查询语句
一级缓存,离不开 session 域,它的价值
- 二级缓存
- 二级缓存和一级缓存的机制是相同的
- 二级缓存与一级缓存机制相同,默认也是用 PerpetualCache (mybatis中关于缓存定义了 一个接口叫Cache,PerpetualCache就是Cache的一个实现类),HashMap存储
- 二级缓存和一级缓存的不同在于 作存储用域为 Mapper(Namespace),就是一个namespace下,共享同一份缓存
- 可以自定义存储源,比如 Ehcache
缓存的更新机制 :当某一个作用域(一级缓存的作用域是SqlSesiion,二级缓存的作用域是Namespace),进行了C/U/D (增删改)操作后,
默认该作用域下,所有的select中的缓存将被 clear ,这一点,一级缓存也是相同的
- 开启二级缓存
<cache /> //只要这一句就行
- 关于二级缓存的补充说明
-- 映射文件中,所有的select语句将会被缓存
-- 映射文件中的 所有的 insert ,update,delete 语句都会刷新缓存
-- 缓存使用 Least Recently Used (最近最少使用的) 的算法进行回收
-- 缓存会根据指定的时间间隔来刷新
-- 缓存默认会存储 1024个对象
- <cache>这个标签的常用属性
<cache
eviction="FIFO" //回收策略为先进先出
flushInterval ="60000" //自动刷新的时间间隔 60s
size="512" //最多缓存 512个对象
readOnly ="true" //只读
type="org.mybatis.caches.ehcache.LoggingEhcach" //指定缓存的提供者
/>
- mybatis 中有 flushCache, useCache两个配置属性,分下面两种情况
(1) 当 为select语句的时候,
flushCache 默认为false ,表示任何时候这个select语句执行,都不用清空缓存
userCache 默认为true , 表示对本条语句的执行结果进行缓存,并且使用缓存中的结果
(2) 当 为 upate,insert,delete 时
flushCache 默认为true ,表求执行这条语句,将会清空缓存
useCache 在该情况下没有,因为一个更新的语句怎么可以使用缓存呢
如果有必要,可以进行手功修改
<select id="save_user" parameterType="xxxxx", flushCache="true" ,useCache="false" >
...sql
</select>
- mybatis 和 缓存框架的整合
- EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是 Hibernate 中默认 CacheProvider。
- Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容 器,它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存 servlet 过滤器,支持REST和SOAP api等特点。
- Ehcache最初是由Greg Luck于2003年开始开发。2009年,该项目被Terracotta购买。软件仍然是开源,但一些新的主要功能(例如,快速可重启性之间的一致性的)只能在商业产品中使用,例如Enterprise EHCache and BigMemory。
- 维基媒体Foundationannounced目前使用的就是Ehcache技术。
1) 导入 相关的jar包
mybatis-ehcache-1.0.3.jar
ehcache-core-2.6.8.jar
slf4j-api-1.6.1.jar
2) 在配置文件中 比如 UserMapper.xml 中
//以下两个标签二选一
<cache type="org.mybatis.caches.ehcache.LoggingEhcache" /> //这个可以输出日志
<cache type="org.mybatis.caches.ehcache.EhcacheCache" /> //不能输出日志
3) ehcache 的配置文件 ehcache.xml ,放在 config(类路径下) 即可
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false">
<diskStore path="java.io.tmpdir/mybatis/g" /> //如果往磁盘上放,默认放在哪个目录
<!-- DefaultCache setting. -->
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"
maxEntriesLocalDisk="100000"
memoryStoreEvictionPolicy="LFU"/>
<!-- security entity-->
<cache
name="entityCache"
maxEntriesLocalHeap="10000"
eternal="true"
overflowToDisk="true"
maxEntriesLocalDisk="1000000" />
</ehcache>
- 二级缓存的问题
Cache使用时的注意事项
- 只能在【只有单表操作】的表上使用缓存
- 不只是要保证这个表在整个系统中只有单表操作,而且和该表有关的全部操作必须全部在一个namespace下。
- 在可以保证查询远远大于insert,update,delete操作的情况下使用缓存
- 这一点不需要多说,所有人都应该清楚。记住,这一点需要保证在1的前提下才可以!
附: 关于mybatis中的二级缓存
可能会有很多人不理解这里,二级缓存带来的好处远远比不上他所隐藏的危害。
- mybatis中 缓存是以namespace为单位的,不同namespace下的操作互不影响。
- insert,update,delete操作会清空所在namespace下的全部缓存。
- 通常使用 MyBatis Generator 生成的代码中,都是各个表独立的,每个表都有自己的 namespace。
- 为什么避免使用二级缓存 ?
- 在符合【Cache使用时的注意事项】的要求时,并没有什么危害。
- 其他情况就会有很多危害了。
- 针对一个表的某些操作不在他独立的namespace下进行的原因
- 例如在UserMapper.xml中有大多数针对user表的操作。但是在一个XXXMapper.xml中,还有针对user单表的操作。
- 这会导致user在两个命名空间下的数据不一致。如果在UserMapper.xml中做了刷新缓存的操作,
- 在XXXMapper.xml中缓存仍然有效,如果有针对user的单表查询,使用缓存的结果可能会不正确。
- 更危险的情况是在XXXMapper.xml做了insert,update,delete操作时,会导致UserMapper.xml中的各种操作充满未知和风险。
有关这样单表的操作可能不常见。但是你也许想到了一种常见的情况。
- 多表操作一定不能使用缓存 为什么不能?
首先不管多表操作写到那个namespace下,都会存在某个表不在这个namespace下的情况。
例如两个表:role和user_role,如果我想查询出某个用户的全部角色role,就一定会涉及到多表的操作。
<select id="selectUserRoles" resultType="UserRoleVO">
select * from user_role a,role b where a.roleid = b.roleid and a.userid = #{userid}
</select>
- 像上面这个查询,你会写到那个xml中呢??
- 不管是写到RoleMapper.xml还是UserRoleMapper.xml,或者是一个独立的XxxMapper.xml中。如果使用了二级缓存,都会导致上面这个查询结果可能不正确。
- 如果你正好修改了这个用户的角色,上面这个查询使用缓存的时候结果就是错的, 这点应该很容易理解。
- 在我看来,就以MyBatis目前的缓存方式来看是无解的。多表操作根本不能缓存。
- 如果你让他们都使用同一个namespace(通过<cache-ref>)来避免脏数据,那就失去了缓存的意义。
看到这里,实际上就是说,二级缓存不能用。再说多了也就没啥用