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
相關文章
相關標籤/搜索