MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。 MyBatis系统中默认定义了两级缓存。一级缓存和二级缓存。 – 1、默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。 – 2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存。 – 3、为了提高扩展性。 MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存是sqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap),用于存储缓存数据。不同的sqlSession之间的缓存区域(HashMap)是互不影响的。
一级缓存(local cache), 即本地缓存, 作用域默认为sqlSession。当 Session flush或 close 后, 该Session 中的所有 Cache 将被清空。 • 本地缓存不能被关闭, 但可以调用clearCache()来清空本地缓存, 或者改变缓存的作用域. • 在mybatis3.1之后, 可以配置本地缓存的作用域.在 mybatis.xml 中配置
Mybatis提供了一级缓存的方案来优化在数据库会话间重复查询的问题。实现的方式是每一个SqlSession中都持有了自己的缓存,一种是SESSION级别,即在一个Mybatis会话中执行的所有语句,都会共享这一个缓存。一种是STATEMENT级别,可以理解为缓存只对当前执行的这一个statement有效。Statement,即不使用一级缓存。
根据一级缓存的工作流程,我们绘制出一级缓存执行的时序图,如下图所示。 主要步骤如下:
对于某个Select Statement,根据该Statement生成key。判断在Local Cache中,该key是否用对应的数据存在。如果命中,则跳过查询数据库,继续往下走。如果没命中: 4.1 去数据库中查询数据,得到查询结果; 4.2 将key和查询到的结果作为key和value,放入Local Cache中。 4.3. 将查询结果返回;判断缓存级别是否为STATEMENT`级别,如果是的话,清空本地缓存。(1). 第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。 (2). 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。(3). 第二次发起查询用户id为1的用户信息,先去找缓存`中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。mybatis默认支持一级缓存的。测试很简单,这里就不贴代码了。
• 二级缓存(second level cache),全局作用域缓存 • 二级缓存默认不开启,需要手动配置 • MyBatis提供二级缓存的接口以及实现,缓存实现要求;POJO实现Serializable接口 • 二级缓存在 SqlSession 关闭或提交之后才会生效
– 1、全局配置文件中开启二级缓存 • <setting name="cacheEnabled" value="true"/> – 2、需要使用二级缓存的映射文件处使用cache配置缓存或cache-ref:cache-ref代表引用别的命名空间的Cache配置,两个命名空间的操作使用的是同一个Cache。 • – 3、注意:POJO需要实现Serializable接口
映射文件的缓存配置
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache> <cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache> --> <cache-ref namespace="mapper.StudentMapper"/>属性
eviction:缓存的回收策略: • LRU – 最近最少使用的:移除最长时间不被使用的对象。 • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。 • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。 • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 • 默认的是LRU。flushInterval:缓存刷新间隔 缓存多长时间清空一次,默认不清空,设置一个毫秒值readOnly:是否只读: true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。 mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快 false:非只读:mybatis觉得获取的数据可能会被修改。 mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢size:缓存存放多少元素;type:指定自定义缓存的全类名;实现Cache接口即可;blocking: 若缓存中找不到对应的key,是否会一直blocking,直到有对应的数据进入缓存当开启二级缓存后,会使用CachingExecutor装饰Executor,在进入后续执行前,先在CachingExecutor进行二级缓存的查询,具体的工作流程如下所示。 在二级缓存的使用中,一个namespace下的所有操作语句,都影响着同一个Cache,即二级缓存是被多个SqlSession共享着的,是一个全局的变量。 当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。
不同namespace查出的数据会放在自己对应的缓存中(map) 效果:数据会从二级缓存中获取
查出的数据都会被默认先放在一级缓存中。只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中整合后,MyBatis的一级缓存会失效。因为每次Mapper代理取寻找SqlSession对象的时候,都会新建一个对象。要想使用一级缓存,可以在事务中使用。
