Mybatis之二级缓存简析

注:Mybatis的版本是3.5.0。java

    上一篇分析了一级缓存,这篇来分析二级缓存。git

    如下的内容,跳过了不少细节,能够去看这篇博客github

    一级缓存只能在同一个SqlSession里面共享,而二级缓存则能够在多个SqlSession里面共享。sql

    开启二级缓存,那么使用的是CachingExecutor和SimpleExecutor(不修改默认设置的状况下),以下List-1所示,SimpleExecutor是封装在CachingExecutor中的。数据库

    List-1 apache

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);
  }
  if (cacheEnabled) {
    //将SimpleExecutor封装起来
    executor = new CachingExecutor(executor);
  }
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

    CachingExecutor使用了委托模式,其属性"Executor delegate",缓存

    List-2安全

package org.apache.ibatis.executor;

 ......
/**
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
public class CachingExecutor implements Executor {
    private final Executor delegate;
    private final TransactionalCacheManager tcm = new TransactionalCacheManager();

    public CachingExecutor(Executor delegate) {
        this.delegate = delegate;
        delegate.setExecutorWrapper(this);
    }
    ......

    以下图1所示,CachingExecutor中使用的Cache是SynchronizedCache,它里面还封装了不少Cache,最终数据是存储在PerpetualCache中,是个HashMap。session

                             

                            图1 CachingExecutor中使用的Cache是SynchronizedCachemybatis

    因为二级缓存Cache封装在SynchronizedCache中,因此对二级缓存的操做是线程安全的,SynchronizedCache的几乎每一个方法上都加了Sychronized,这在实现线程安全的同时,也在必定程度上成了瓶颈,特别是对二级缓存操做的线程数量不少时。

    在List-2中的属性tcm,这个是理解二级缓存的一个关键点。

private final TransactionalCacheManager tcm = new TransactionalCacheManager();

    session1提交后,session2才能看到session1缓存的结果。

    在配置了二级缓存以后,select时,是二级缓存->一级缓存->数据库,以下是CachingExecutor中的query:

    List-3

@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
    throws SQLException {
    Cache cache = ms.getCache();
    if (cache != null) {
        flushCacheIfRequired(ms);
        if (ms.isUseCache() && resultHandler == null) {//1
            ensureNoOutParams(ms, boundSql);
            @SuppressWarnings("unchecked")
            List<E> list = (List<E>) tcm.getObject(cache, key);//2
            if (list == null) {
                list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); //3
                tcm.putObject(cache, key, list); // issue #578 and #116
            }
            return list;
        }
    }
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

    在List-3中

  1. 若是在select标签上使用useCache=false,则不会走二级缓存。mybatis中默认状况下useCache的值是true,如List-5中设置的
  2. 2处走二级缓存,若是二级缓存返回null,则走一级缓存
  3. 3处,委托给其它的executor来执行select,一级缓存就在这个delegate中,这个deletegate是Simple/Batch/ReuseExecutor,它们都继承了BaseExecutor,BaseExecutor中有localCache变量,是PerpetualCache,这个就是一级缓存。

    List-4

<select id="findByUsername" resultType="Person" parameterType="Person" useCache="false">

    MappedStatement中的useCache的值设置在MapperBuilderAssistant中,以下List-5的1处,若是是select标签,在没有显示设置useCache的状况下是true。

    List-5

public MappedStatement addMappedStatement(
    ...
    id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
        .resource(resource)
        .fetchSize(fetchSize)
        .timeout(timeout)
        .statementType(statementType)
        .keyGenerator(keyGenerator)
        .keyProperty(keyProperty)
        .keyColumn(keyColumn)
        .databaseId(databaseId)
        .lang(lang)
        .resultOrdered(resultOrdered)
        .resultSets(resultSets)
        .resultMaps(getStatementResultMaps(resultMap, resultType, id))
        .resultSetType(resultSetType)
        .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
        .useCache(valueOrDefault(useCache, isSelect))//1
        .cache(currentCache);
  }

  private <T> T valueOrDefault(T value, T defaultValue) {
    return value == null ? defaultValue : value;
  }

Reference

  1. https://github.com/mybatis/mybatis-3/tree/3.4.x
相关文章
相关标签/搜索