Mybatis - 二级缓存


介绍

Mybatis 的二级缓存是 Application 级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。

读取缓存数据

main 方法

public class Test {
  public static void main(String[] args) throws Exception {
    String resource = "SqlMapConfig.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession session = factory.openSession();
    UserMapper userMapper = session.getMapper(UserMapper.class);
    User user = userMapper.findUserById(2);
    System.out.println(user);
    session.close();
  }
}

DefaultSqlSession selectOne 方法

  public  T selectOne(String statement, Object parameter) {
    // 调用 selectList 方法
    List list = this.selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } 
  }

DefaultSqlSession selectList 方法

  public  List selectList(String statement, Object parameter) {
    // 调用重载方法
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }

  public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
    MappedStatement ms = configuration.getMappedStatement(statement);
    // 执行器查询
    return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
  }
}

CachingExecutor query 方法

  private TransactionalCacheManager tcm = new TransactionalCacheManager();

  public  List 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  List 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, parameterObject, boundSql);
        @SuppressWarnings("unchecked")
        // 查找二级缓存
        List list = (List) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          // 缓存数据
          tcm.putObject(cache, key, list);
        }
        return list;
      }
    }
    return delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

TransactionalCacheManager getObject 方法

  private Map transactionalCaches = new HashMap();

  public Object getObject(Cache cache, CacheKey key) {
    return getTransactionalCache(cache).getObject(key);
  }

  private TransactionalCache getTransactionalCache(Cache cache) {
    TransactionalCache txCache = transactionalCaches.get(cache);
    if (txCache == null) {
      txCache = new TransactionalCache(cache);
      transactionalCaches.put(cache, txCache);
    }
    return txCache;
  }

TransactionalCache getObject 方法

  // 上面方法传入的 SynchronizedCache 对象
  private Cache delegate;

  public Object getObject(Object key) {
    // 二级缓存中查找
    Object object = delegate.getObject(key);
    return object;
  }

缓存数据

TransactionalCacheManager putObject 方法

  public void putObject(Cache cache, CacheKey key, Object value) {
    // 上面定义过
    getTransactionalCache(cache).putObject(key, value);
  }

TransactionalCache putObject 方法

  private Map entriesToAddOnCommit;

  @Override
  public void putObject(Object key, Object object) {
    entriesToAddOnCommit.put(key, object);
  }

提交操作写入缓存

DefaultSqlSession commit 方法

  @Override
  public void commit() {
    commit(false);
  }

  @Override
  public void commit(boolean force) {
    try {
      // 执行器提交
      executor.commit(isCommitOrRollbackRequired(force));
      dirty = false;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

CachingExecutor commit 方法

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

TransactionalCacheManager commit 方法

  public void commit() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.commit();
    }
  }

TransactionalCache commit 方法

  public void commit() {
    if (clearOnCommit) {
      delegate.clear();
    }
    // 刷新操作
    flushPendingEntries();
    reset();
  }

  private void flushPendingEntries() {
    for (Map.Entry entry : entriesToAddOnCommit.entrySet()) {
      // 存放入二级缓存
      delegate.putObject(entry.getKey(), entry.getValue());
    }
    for (Object entry : entriesMissedInCache) {
      if (!entriesToAddOnCommit.containsKey(entry)) {
        delegate.putObject(entry, null);
      }
    }
  }