原文网址:MyBatis原理--缓存机制_IT利刃出鞘的博客-CSDN博客_mybatis原理
简介
说明
Mybatis的缓存,包括一级缓存和二级缓存。
一级缓存的作用域是一个sqlsession内;二级缓存作用域是针对mapper进行缓存。
实际上,在开发过程中根本不会用到Mybatis的这两个缓存。因为这两个都不支持分布式,如果想用缓存,那么直接用Redis的功能就好了呀。虽然二级缓存可以使用MemCache、Ehcache、Redis等可以做到支持分布式,但不如直接使用Redis方便。
一级缓存
概述
简介
开启一级缓存后:在参数和SQL完全一样的情况下,同一个SqlSession对象调用一个Mapper方法,只执行一次SQL。因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。
一级缓存的缺点
- 多个 SqlSession 或者分布式的环境下,数据库写操作会引起脏数据。
- MyBatis 一级缓存内部设计简单,只是一个没有容量限定的 HashMap,在缓存的功能性上有所欠缺
一级缓存命中条件
- 相同的 sql 和参数
- 会话(Session)级别缓存,必须是相同的会话
- 相同的方法
- 相同的 namespce (mapper)
一级缓存清除场景
- 增删改操作后,执行了commit
- 关闭了sqlSession
- 调用了clearCache(),或者查询语句中包含了flush
启用与关闭
MyBatis中一级缓存默认是开启的。
关闭单个SQL的一级缓存的方法:将flushCache属性设置为true。示例如下
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap" flushCache="true">
select
<include refid="Base_Column_List" />
from cbondissuer
where OBJECT_ID = #{objectId,jdbcType=VARCHAR}
</select>
关闭所有SQL的一级缓存的方法:在MyBatis的主配置文件中,关闭所有的一级缓存:设置localCacheScope。示例如下:
<setting name="localCacheScope" value="SESSION"/>
- value的值
- SESSION:在一个 MyBatis 会话中执行的所有语句,都共享这一个缓存
- STATEMENT:缓存只对当前执行的这一个 Statement 有效
详述
每个SqlSession 中持有 Executor,每个 Executor 中有一个 LocalCache。当用户发起查询时,MyBatis 根据当前执行的语句生成 MappedStatement,在 LocalCache 进行查询,如果缓存命中,就直接返回给用户,如果缓存没有命中的话,就另外查询数据库,将结果写入 LocalCahe,最后将结果返回给用户。
一级缓存的获取流程
- 对于某个查询,根据statementId,params,rowBounds来构建一个key值,根据这个key值去缓存Cache中取出对应的key值存储的缓存结果;
- 判断从Cache中根据特定的key值取的数据数据是否为空,即是否命中;
- 如果命中,则直接将缓存结果返回;
- 如果没命中:
- 去数据库中查询数据,得到查询结果;
- 将key和查询到的结果分别作为key,value对存储到Cache中;
- 将查询结果返回;
- 结束。
缓存的key的确定
Cache最核心的实现其实就是一个Map,将本次查询使用的特征值作为key,将查询结果作为value存储到Map中。
现在最核心的问题出现了:怎样来确定一次查询的特征值?
换句话说就是:怎样判断某两次查询是完全相同的查询?
也可以这样说:如何确定Cache中的key值?
MyBatis认为,对于两次查询,如果以下条件都完全一样,那么就认为它们是完全相同的两次查询:
- 传入的 statementId
- 查询时要求的结果集中的结果范围 (结果的范围通过rowBounds.offset和rowBounds.limit表示);
- 这次查询所产生的最终要传递给JDBC java.sql.Preparedstatement的Sql语句字符串(boundSql.getSql() )
- 传递给java.sql.Statement要设置的参数值
上边只是部分内容,为便于维护,本文已迁移到此地址:MyBatis原理-缓存机制 - 自学精灵