通常的ORM(Object Relational Mapping)框架都会提供缓存功能以减小访问数据库的次数,减轻数据库压力,提升查询效率。MyBatis中有一级和默认实现的二级缓存,并且MyBatis也预留了集成第三方缓存的接口(二级缓存接口的自定义实现)。sql
先经过一段源码来看看mybatis的查询的执行流程。数据库
缓存
2> 执行query方法mybatis
1 @Override 2 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) 3 throws SQLException { 4 //获取缓存对象 5 Cache cache = ms.getCache(); 6 if (cache != null) {//存在缓存 7 //尝试刷新缓存,确保缓存是最新 8 flushCacheIfRequired(ms); 9 //若是缓存启用而且该查询没有用到ResultHandler 10 if (ms.isUseCache() && resultHandler == null) { 11 //参数检查 12 ensureNoOutParams(ms, boundSql); 13 @SuppressWarnings("unchecked") 14 //从缓存中读取 15 List<E> list = (List<E>) tcm.getObject(cache, key); 16 if (list == null) {//未命中,调用默认执行器执行query 17 list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); 18 //查询结果放入缓存 19 tcm.putObject(cache, key, list); // issue #578 and #116 20 } 21 return list; 22 } 23 } 24 return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); 25 }
经过上面一段代码,能够发现如下两个问题:app
首先,你们大概能够猜想出mybatis中一级缓存和二级缓存的优先级顺序了吧?😂没错,在开启二级缓存的状况下会先从二级缓存中读取——>二级缓存未命中的话会调用默认执行器的query方法——>默认执行器再读取本身的一级缓存——>未命中则访问数据库——>查询结果放入缓存。框架
二级缓存是保存在MappedStatement中的,MappedStatement是用来存放咱们的定义DAO层的mapper的信息。一个mapper接口对应一个MappedStatement,那么二级缓存的做用域就是同一mapper下有效。ide
mybatis 的一级缓存是sqlSession级别的,缓存做用域是在一次数据库会话中有效。一级缓存是持久化缓存(本地缓存)而且是默认开启的,不须要手动开启。ui
为何说一级缓存是Session级别的而且是本地缓存呢?mybatis中每个sqlSession都会对应一个继承自BaseExecutor的Executor(默认是SimipleExecutor)。而BaseExecutor中存在两个PerpetualCache类型的实现Cache接口的对象,见名知意,它就是一个持久化的缓存对象,具体的持久化实现很少赘述(没看😢)。spa
为了不脏读,sqlSession执行了DML操做(insert、update、delete),并commit了以后,mybatis会清空当前sqlSession缓存中的全部缓存数据。code
从开头的源码解读能够看出,二级缓存的做用域是同一mpper接口,全部的sqlSession查询结果都会走MapperStatement中的Cache。mybatis中的二级缓存默认是关闭的须要手动开启。
开启mybatis二级缓存的方法:
在mybatis配置文件中添加下面的设置
<settings> <setting name="cacheEnabled" value="true"/> </settings>
在须要开启二级缓存的mapper.xml文件中添加下面的配置
<!-- 当前mapper下全部语句开启二级缓存 --> <!-- 也能够在对应的mapper接口上添加@CacheNamespace注解 --> <cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
具体的执行流程能够看开头的源码解析,不过多赘述