Mybatis源码学习(29)-Mybatis中的执行器CachingExecutor

一、前言

  在前面的内容中已经分析了Executor的三个实现类SimpleExecutor、ReuseExecutor和BatchExecutor。这里开始分析Executor接口的装饰器类CachingExecutor。

二、CachingExecutor类

  CachingExecutor类直接实现了Excutor接口,是装饰器类,主要用来增强缓存相关功能。在CachingExecutor类中,为了完成缓存相关功能,需要TransactionalCacheManager和TransactionalCache两个类的支持,其中TransactionalCacheManager类主要用来管理缓存数据的对象TransactionalCache,而TransactionalCache对象是用来真正缓存数据的对象。

1、构造函数、属性
/**
   * 真正用来操作数据库操作的 Executor对象
   */
  private final Executor delegate;
  /**
   * 用于管理缓存的TransactionalCacheManager对象
   */
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();

  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this);
  }
2、事务、关闭对象相关方法

  在CachingExecutor类中,事务、关闭对象相关方法都是通过真正的Executor对象实例delegate和TransactionalCacheManager 实例tcm配合完成,比较简单,这里不再具体分析。示例如下:

 @Override
  public void commit(boolean required) throws SQLException {
    delegate.commit(required);
    tcm.commit();
  }

  @Override
  public void rollback(boolean required) throws SQLException {
    try {
      delegate.rollback(required);
    } finally {
      if (required) {
        tcm.rollback();
      }
    }
  }
@Override
  public void close(boolean forceRollback) {
    try {
      //issues #499, #524 and #573
      if (forceRollback) { 
        tcm.rollback();
      } else {
        tcm.commit();
      }
    } finally {
      delegate.close(forceRollback);
    }
  }

  @Override
  public boolean isClosed() {
    return delegate.isClosed();
  }
3、update()方法

  该方法中,首先通过flushCacheIfRequired()方法清理缓存,然后再调用真正的Executor对象的update()方法,执行update操作。

@Override
  public int update(MappedStatement ms, Object parameterObject) throws SQLException {
    flushCacheIfRequired(ms);
    return delegate.update(ms, parameterObject);
  }
4、queryCursor()方法

  通update()方法类似。

@Override
  public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
    flushCacheIfRequired(ms);
    return delegate.queryCursor(ms, parameter, rowBounds);
  }
5、query()方法

  在CachingExecutor类中,真正受影响的其实就是query()方法。逻辑如下:

  1. 获取 BoundSql对象,创建查询语句对应的CacheKey对象
  2. 检测是否开启了二级缓存,如果没有开启二级缓存,则直接调用真正的Executor 对象的query()方法查询数据库,否则进行后续操作。
  3. 检测查询操作是否包含输出类型的参数,如果包含,则抛出异常
  4. 调用TransactionalCacheManager.getObject()方法查询二级缓存,如果缓存中存在结果,则追返回
  5. 如果二级缓存没有相应的结果对象,则调用底层Executor对象的query()方法。
 @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) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        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);
  }
private void ensureNoOutParams(MappedStatement ms, BoundSql boundSql) {
    if (ms.getStatementType() == StatementType.CALLABLE) {
      for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
        if (parameterMapping.getMode() != ParameterMode.IN) {
          throw new ExecutorException("Caching stored procedures with OUT params is not supported.  Please configure useCache=false in " + ms.getId() + " statement.");
        }
      }
    }
  }
三、TransactionalCache类

  用于保存在事务中需要向某个二级缓存中添加的缓存数据。nsactionalCache类各个方法的作用,详见方法的注释,没有复杂逻辑,不再具体分析。

public class TransactionalCache implements Cache {

  private static final Log log = LogFactory.getLog(TransactionalCache.class);
  //底层封装的二级缓存所对应的 Cache对象
  private final Cache delegate;
  //当该字段为true时,则表示当前 TransactionalCache不可查询, 且提交事务时会将底层Cache清空
  private boolean clearOnCommit;
  //暂时记录添加到TransactionalCache中的数据 在事务提交时,会将其中的数据添加到二级缓存中
  private final Map<Object, Object> entriesToAddOnCommit;
  //记录缓存未命中CacheKey对象
  private final Set<Object> entriesMissedInCache;

  public TransactionalCache(Cache delegate) {
    this.delegate = delegate;
    this.clearOnCommit = false;
    this.entriesToAddOnCommit = new HashMap<Object, Object>();
    this.entriesMissedInCache = new HashSet<Object>();
  }

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public int getSize() {
    return delegate.getSize();
  }
  /**
   * 首先会查询底层的二级缓存,并将未命中的key记录到entriesMissedInCache中,</br>
   * 之后会根据clearOnCommit字段的值决定具体的返回值
   */
  @Override
  public Object getObject(Object key) {
    // issue #116
    Object object = delegate.getObject(key);
    if (object == null) {
      entriesMissedInCache.add(key);
    }
    // issue #146
    //如果clearOnCommit为true ,则当前TransactionalCache不可查询,始终返回 null
    if (clearOnCommit) {
      return null;
    } else {
      return object;
    }
  }

  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }
  /**
   * 该方法并没有直接将结果对象记录到其封装二级缓存中,</br>
   * 而是暂时保存在 entriesToAddOnCommit集合中,</br>
   * 在事务提交时才会将这些结果对象从entriesToAddOnCommit集合添加到二级缓存中。
   */
  @Override
  public void putObject(Object key, Object object) {
    entriesToAddOnCommit.put(key, object);
  }

  @Override
  public Object removeObject(Object key) {
    return null;
  }
  /**
   * 该方法会清空entriesToAddOnCommit集合,并设置 clearOnCommit为true
   */
  @Override
  public void clear() {
    clearOnCommit = true;
    entriesToAddOnCommit.clear();
  }
  /**
   * 根据clearOnCommit字段的值决定是否清空二级缓存,</br>
   * 然后调用flushPendingEntries()方法将 entriesToAddOnCommit集合中记录的结果对象保存到缓存中
   */
  public void commit() {
    if (clearOnCommit) {
      delegate.clear();
    }
    //将entriesToAddOnCommit集合中的数据保存到二级缓存
    flushPendingEntries();
    reset();
  }
  /**
   * 将entriesMissedlnCache集合中记录的缓存项从二级缓存中删除,</br>
   * 并清空entriesToAddOnCommit集合和entriesMissedlnCache集合
   */
  public void rollback() {
    unlockMissedEntries();
    reset();
  }
  /**
   * 重置clearOnCommit == false ,并清空entriesToAddOnCommit和entriesMissedInCache集合
   */
  private void reset() {
    clearOnCommit = false;
    entriesToAddOnCommit.clear();
    entriesMissedInCache.clear();
  }
  /**
   * 保持数据到二级缓存中
   */
  private void flushPendingEntries() {
	//遍历entriesToAddOnCommit集合,将其中记录的缓存项添加到二级缓存中
    for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
      delegate.putObject(entry.getKey(), entry.getValue());
    }
    //将仍未命中的的缓存项,把value设置为null,存入二级缓存中
    for (Object entry : entriesMissedInCache) {
      if (!entriesToAddOnCommit.containsKey(entry)) {
        delegate.putObject(entry, null);
      }
    }
  }
  /**
   * 将entriesMissedlnCache集合中记录的缓存项从二级缓存中删除
   */
  private void unlockMissedEntries() {
    for (Object entry : entriesMissedInCache) {
      try {
        delegate.removeObject(entry);
      } catch (Exception e) {
        log.warn("Unexpected exception while notifiying a rollback to the cache adapter."
            + "Consider upgrading your cache adapter to the latest version.  Cause: " + e);
      }
    }
  }
}
四、TransactionalCacheManager类

  用于管理 CachingExecutor中使用的二级缓存对象TransactionalCache。该类中的方法基本上都是基于缓存Cache接口的实现,不再具体分析。

public class TransactionalCacheManager {
  /**
   * 其中的key,对应的CachingExecutor中使用的 二级缓存对象, </br>
   * value,对应的TransactionalCache对象,
   * 在该TransactionalCache中封装了对应二级缓存对象,也就是这里的 key
   */
  private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();

  public void clear(Cache cache) {
    getTransactionalCache(cache).clear();
  }
  public Object getObject(Cache cache, CacheKey key) {
    return getTransactionalCache(cache).getObject(key);
  }
 
  public void putObject(Cache cache, CacheKey key, Object value) {
    getTransactionalCache(cache).putObject(key, value);
  }
  public void commit() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.commit();
    }
  }
  public void rollback() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.rollback();
    }
  }
  private TransactionalCache getTransactionalCache(Cache cache) {
    TransactionalCache txCache = transactionalCaches.get(cache);
    if (txCache == null) {
      txCache = new TransactionalCache(cache);
      transactionalCaches.put(cache, txCache);
    }
    return txCache;
  }

}
发布了71 篇原创文章 · 获赞 3 · 访问量 5279

猜你喜欢

转载自blog.csdn.net/hou_ge/article/details/104409791