mybatis的缓存分为一级缓存和二级缓存。一级缓存也叫本地缓存,mybatis会默认启用,并且不会被更改。通常说的mybatis缓存都是指二级缓存。
一级缓存
mybatis的一级缓存在于一次sqlSession生命周期中,mybatis的一级缓存本质是一个map,
在同一个sqlSession查询中,mybatis会将执行的方法和参数通过一定算法生成一个key,将key和查询结果存放在map中,第二次查询如果能匹配上则直接返回上一次查询的实例。
这里的问题是,在一次sqlSession中,第一次查询到结果后,对返回的实例对象做了set更新属性值,但是没有更新到数据库中,紧接着第二次相同条件的查询,会直接返回前一个实例,出现了非预期的情况。
在一次sqlSession的两次查询过程中如果有任意更新操作(不限于当前数据的增删改操作),则第二次查询的返回结果会是一个新的实例,因为一级缓存被清空了。
如果不想使用一级缓存,可以再mapper上配置flushCache="true"强制更新cache,即查询前也会强制清空当前的一级缓存。
<select id=”selectByid” flushCache=”true”
二级缓存
二级缓存不同于一级缓存只存在于sqlSession生命周期中,二级缓存可以理解为存在于sqlSessionFactory周期中,与map的命名空间绑定。
关于配置
在mybatis的全局配置中有一个参数cacheEnabled,默认为true,如果配置成false,则二级缓存全局范围不可用,如果需要可以在mybatis-config.xml中配置:
<settings>
< !-- 其他配置 -->
<setting name="cacheEnabled" value="true" />
</settings>
二级缓存是和命名空间绑定的,需要配置在mapper.xml文件的根节点上或者mapper.java接口上,属性名为namespace,在mapper接口上配置,命名空间就是接口的全限定名称。
<mapper namespace=” t k . mybatis . smple.mapper . RoleMapper ”>
<cache/>
</mapper>
一个cache标签即代表开启了二级缓存,二级缓存默认配置:
1.该命名空间下的所有select会被缓存
2.增删改操作会刷新缓存
3.使用LRU(Least Recently Used 最近最少使用)算法回收缓存
4.不会自动根据时间段刷新
5.存储集合或者对象的1024个引用,即size=1024
6.缓存是可读写的,支持多线程同时操作而不互相影响,即如果只支持读,就是返回给多个线程同样的实例,如果支持写,就是返回缓存对象的copy,各自操作各自的。
mybatis的一级缓存和二级缓存本质都是内存级别的缓存,使用hashmap作为存储结构。二级缓存还可以使用外部缓存如redis,步骤如下:
1.业务实体类要实现Serializable接口
image.png
2.全局配置与上述一致,map上的配置改为如下:
image.png
二级缓存的缺陷-脏数据:
由于二级缓存是map级别的,通常某一实体对应一个map,包含该实体的增删改查操作,由于关系型数据库的设计,业务查询中经常少不了关联查询,这些查询会根据业务处理规则放在某个map中,map对应二级缓存的更新并不能影响其他map对应的二级缓存,这样就会产生脏数据。
二级缓存适用场景:
1.以查询为主的应用,尽可能少的更新操作,对数据实时性要求不高。例如某些统计查询,物流跟踪查询等。
2.绝大多数单表操作的map,由于没有关联查询,不会有脏数据的问题。
缺陷解决方案:
考虑在service层自定义处理缓存(自己维护map或者直接操作redis),代替二级缓存。
网友评论