程序与数据库交互的过程也是一个比较耗费性能的的过程,所以一般都会通过缓存来减少与数据库的直接交互,提升程序的运行效率。
一级缓存
MyBatis中默认开启了SqlSession缓存,即同一个SqlSession对象调用同一个<select>
时,只有第一次访问数据库,第一次之后把查询结果缓存到SqlSession缓存区(内存)中。
缓存的是statement对象,在mybatis中一个<select>
对应一个statement对象,且缓存的有效范围必须是同一个SqlSession对象。
例如,同一个SqlSession执行同一个<select>
查询的情况;
每个SqlSession中持有了Executor,每个Executor中有一个LocalCache。当用户发起查询时,MyBatis根据当前执行的语句生成MappedStatement,在Local Cache进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入Local Cache,最后返回结果给用户。
如果执行了修改数据的操作(更新、新增、删除),会造成缓存失效,例如在第三次查询之前执行了新增:
第二次查询依然走的是缓存,执行完新增操作后,第三次查询缓存失效,查询的数据库。
新开一个SqlSession对象进行数据修改操作:
由于一级缓存的有效范围是同一个SqlSession,所以另外一个SqlSession执行的修改操作不会使第一个SqlSession的缓存失效,所以第三次查询依然走的是缓存,此时查询到的是“脏数据”。
一级缓存的执行时序图:
如果缓存命中,直接从缓存区返回查询结果;如果缓存没有命中,将查询数据库,把数据库查询到的结果缓存到缓存区,并返回查询结果。
二级缓存
一级缓存的有效范围是同一个SqlSession内,如果需要在不同的SqlSession中共享缓存,则需要用到二级缓存。开启了二级缓存后,进入一级缓存之前会先进行二级缓存的查询。
开启二级缓存需要在mybatis全局配置文件中配置相应的setting标签:
<setting name="cacheEnabled" value="true"/>
同时需要在sql语句映射文件mapper.xml中添加:
<cache readOnly="true"/>
当SqlSession对象commit()时会把SqlSession缓存的数据flush到二级缓存中:
上述例子中session和session2都是在最后才提交,所以二级缓存不生效:
稍微修改一下,在执行第一条查询语句的时候,进行session提交:
由于在执行第一条查询sql的时候就提交了,所以已经将SqlSession缓存flush到了二级缓存,后面的sql语句会命中缓存。
二级缓存在SqlSession执行了改变数据库的操作(更新、删除、新增)之后会失效,例如:
在执行第一条sql语句后提交了,所以第二条sql语句会命中缓存,之后session2执行了新增sql,会导致二级缓存失效: