Mybatis 一、二级缓存
mybatis缓存,一般都是指二级缓存,一级缓存(本地缓存)默认会开启,并且不能控制,因此很少会提到
-
<select id="selectById" flushCache="true" resultMap="userMappe"> select * from sys_user where id = #{id} </select>
使用属性 flushCache="true"
会在查询前将本地的一级缓存清空
-
一级缓存作用在SQLSession上
-
二级缓存作用在sqlSessionFactory上
-
二级缓存开启
mybatis-config.xml
<settings> <setting name = "cacheEnabled" value = "true" /> </settings>
-
mapper.xml
<mapper namespace = "..."> <cache/> </mapper>
cache可以配置内容
<cache eviction="FIFO" # 回收策略:LRU(默认)、FIFO、SOFT、WEAK> flushInterval="60000" # 刷新间隔,默认不设置 size="512" # 引用数目,要记住缓存的对象数目和运行环境的可用内存资源数目。默认值是1024 readOnly="true" </cache>
-
接口中也可以配置二级缓存
@CacheNameSpace public interface RoleMapper{ // 接口方法 }
注解同样可以配置各项属性
@CacheNamespace( eviction = FifoCache.class, flushCache = 60000, size = 512, readWrite = true )
同时使用上边两种方式,会抛出异常,因为Mapper接口和对应的xml文件是相同命名空间
如果要同时使用上述两种方式:
-
需要在Mapper接口上添加
@cacheNameSpaceRef(RoleMapper.class)
,这样就会使用命名空间为···.RoleMapper的缓存(即RoleMapper.xml配置的缓存) -
也可以在xml文件中配置
<cache-ref namespace="..."/>
-
-
-
如果配置的是可读可写,二级缓存使用的是SerializedCache序列化缓存,应用的不是同一个实例,且要求所有被序列化的对象必须实现Serializable接口;如果配置的是只读缓存,Mybatis就会使用Map类存储缓存值,这种情况下,村缓存中获取对象就是同一个实例.
-
二级缓存当涉及到多表关联时,可能出现脏读,解决办法就是修改为“参照引用”
<mapper namespace = "..."> <cache-ref namespace="..."/> </mapper>
集成Redis缓存
-
添加项目依赖
<dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-redis</artifactId> <version>1.0.0-beta2</version> </dependency>
-
配置Redis:redis.properties
host=localhost port=6370 connectionTimeOut=5000 soTimeout=5000 password= database=0 clientName=
-
修改RoleMapper.xml配置
<mapper namespace="..."> <cache type="org.mybatis.caches.redis.RedisCache"/> </mapper>
脏数据的产生和避免
Mybatis的二级缓存是和命名空间绑定的,所以通常情况下每一个Mapper映射文件都用于自己的二级缓存,不同的Mapper的二级缓存互不影响。在关联多表查询时肯定会将该查询放到某个命名空间下的映射文件中,这样一个多表的查询就会缓存在该命名空间下的二级缓存中。涉及这些表的增删改操作通常不在一个映射文件中,她们的命名空间不同,因此当有数据变化时,多表查询的缓存未必会清空,这种情况下就会产生脏数据。
避免方法:使用参照缓存,当几个表可以作为一个业务整体时,通常会将几个关联的ER表同时使用同一个二级缓存,这样就能解决脏数据问题。
<mapper namespace="···.UserMapper">
<cache-ref namespace="···.RoleMapper" />
</mapper>
这样当角色Role发生改变时,再次查询UserMapper就会发现没有使用二级缓存,而是重新从数据库进行了查询,虽然这应可以解决脏读问题,但如果有几十张表甚至所有表都以不同的关联关系存在于各自的映射文件时,使用参照缓存显然也没有太大意义
二级缓存适用场景
- 查询为主
- 绝大多数表以单表操作存在,很少存在互相关联
- 可以按业务划分对表进行分组时,比如关联表比较少,可以通过参照缓存进行配置
在无法保证数据不出现脏读的情况下,建议在业务层使用可控制的缓存代替二级缓存