MyBatis 缓存介绍

MyBatis 缓存介绍

一级缓存

一级缓存是本地缓存,和BaseExecutor关联,BaseExecutor有三个实现类,SimpleExecutor、ReuseExecutor和BatchExecutor,
SqlSession初始化时会创建Executor的实例,Mybatis默认使用的是SimpleExecutor,初始化代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//Configuration.java
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    //如果启用二级缓存,用CachingExecutor装饰类
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

BaseExecutor初始化时会初始化本地缓存,实现类为PerpetualCache,它的实现比较简单,里面就是一个HashMap来保存对象。

1
2
3
4
5
6
7
8
9
public class PerpetualCache implements Cache {

  private String id;

  private Map<Object, Object> cache = new HashMap<Object, Object>();

  public PerpetualCache(String id) {
    this.id = id;
  }

一级缓存是SqlSession级别的缓存,在sqlSession提交时会清空本地缓存,因为commit操作一般对应插入、更新或者删除操作,清空缓存防止读取脏数据。

1
2
3
4
5
6
7
8
9
10
11
//BaseExecutor.java
public void commit(boolean required) throws SQLException {
if (closed) {
  throw new ExecutorException("Cannot commit, transaction is already closed");
}
clearLocalCache();
flushStatements();
if (required) {
  transaction.commit();
}
}

二级缓存

如果用户在全局配置文件SqlMapConfig.xml或者mapper文件里配置了”cacheEnabled=true”,
如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//SqlMapConfig.xml
<configuration>
  <settings>
    <setting name="cacheEnabled" value="true"/>
  </settings>
</configuration>
//UserMapper.xml
<mapper namespace="com.ezlippi.mybatis.mapper.UserMapper">
<!-- 开启本mapper namespace下的二级缓存 -->
<cache
  eviction="FIFO"//缓存过期策略,可以是LRUFIFOSOFTWEAK
  flushInterval="60000"//缓存刷新间隔,除了语句刷新外到了这个时间间隔强制刷新
  size="512"
  readOnly="true"/>
</mapper>

MyBatis在为SqlSession对象创建Executor对象时,会给Executor对象加上一个装饰者:CachingExecutor,这时SqlSession使用CachingExecutor对象来完成操作请求。CachingExecutor对于查询请求,会先判断该查询请求在二级缓存中是否有缓存,如果有则直接返回缓存结果;如果没有再交给真正的Executor对象来完成查询操作,之后CachingExecutor会将真正Executor返回的查询结果放置到缓存中,然后再返回给用户。

MyBatis的二级缓存是可以热插拔的,你可以用MyBatis自带的LRUCache、FIFOCache等,也可以用第三方的缓存库,比如Memcached或者EhCache,
二级缓存的作用域比一级缓存更广,作用域为一个Mapper的namespace,namaspace相同则使用同一个二级缓存区域,比如一个UserMapper类里面有SelectOne()
和selectList()两个查询操作,这两个操作共享同一个缓存区域。同一个Mapper的不同SqlSession可以共享一个二级缓存,如果任意一个sqlSession执行了commit()操作
则清空该namespace对应的二级缓存。

你还可以为每条Mapper语句设置是否要刷新缓存,可以指定select语句是否使用缓存,如下所示:

1
2
3
4
<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>

这里需要注意的是:二级缓存需要查询结果映射的pojo对象实现Java.io.Serializable接口,如果存在父类、成员pojo都需要实现序列化接口。
最后贴一下CachingExecutor的查询语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
  throws SQLException {
//获取MapperStatement关联的Cache,和Mapper的Namespace相关联
Cache cache = ms.getCache();
if (cache != null) {
   //获取Mapper语句的flushCache配置
  flushCacheIfRequired(ms);
  if (ms.isUseCache() && resultHandler == null) {
    ensureNoOutParams(ms, parameterObject, boundSql);
    //从TransactionalCacheManager获取缓存的对象
    List<E> list = (List<E>) tcm.getObject(cache, key);
    //如果缓存中没有找到则调用实际的Executor执行查询语句,然后再更新缓存
    if (list == null) {
      list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      tcm.putObject(cache, key, list); // issue #578 and #116
    }
    return list;
  }
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值