mybatis源码学习------执行器Executor(二)

SimpleExecutor

SimpleExecutor继承自BaseExecutor类,是最简单的Executor实现类,增删改查功能都依赖其持有的StatementHandler实例来完成。

doUpdate

public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
  Statement stmt = null;
  try {
    Configuration configuration = ms.getConfiguration();
    //创建StatementHandler对象
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
    //构建Statement对象
    stmt = prepareStatement(handler, ms.getStatementLog());
    //调用StatementHandler的具体实例完成更新操作
    return handler.update(stmt);
  } finally {
    closeStatement(stmt);
  }
}

doQuery

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  Statement stmt = null;
  try {
    Configuration configuration = ms.getConfiguration();
    //创建StatementHandler对象
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    //构建Statement对象
    stmt = prepareStatement(handler, ms.getStatementLog());
    //调用StatementHandler的具体实例完成查询操作
    return handler.query(stmt, resultHandler);
  } finally {
    closeStatement(stmt);
  }
}

doQueryCursor

protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
  Configuration configuration = ms.getConfiguration();
  //创建StatementHandler对象
  StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
  //构建Statement对象
  Statement stmt = prepareStatement(handler, ms.getStatementLog());
  Cursor<E> cursor = handler.queryCursor(stmt);
  stmt.closeOnCompletion();
  return cursor;
}

doFlushStatements

不提供批处理功能,直接返回空集合。

public List<BatchResult> doFlushStatements(boolean isRollback) {
  return Collections.emptyList();
}

prepareStatement

用于构建Statement对象

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  Connection connection = getConnection(statementLog);
  //创建Statement对象
  stmt = handler.prepare(connection, transaction.getTimeout());
  //填入实参
  handler.parameterize(stmt);
  return stmt;
}

ReuseExecutor

在JDBC编程中,重用Statement对象是常用的一种优化手段,该优化手段可以减少SQL预编译的开销以及创建和销毁Statement对象的开销,从而提高性能。

ReuseExecutor就是提供了Statement重用功能的一种实现,他通过一个Map来缓存使用过的Statement对象。

属性

Statement缓存,其中key为带?占位符的sql语句

private final Map<String, Statement> statementMap = new HashMap<>();

prepareStatement

创建Statement对象

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  BoundSql boundSql = handler.getBoundSql();
  String sql = boundSql.getSql();
  if (hasStatementFor(sql)) {//如果缓存命中,则复用缓存中的Statement对象
    stmt = getStatement(sql);
    //设置超时时间
    applyTransactionTimeout(stmt);
  } else {
    //获取连接对象
    Connection connection = getConnection(statementLog);
    //创建Statement对象
    stmt = handler.prepare(connection, transaction.getTimeout());
    //将Statement保存在缓存中
    putStatement(sql, stmt);
  }
  //用实参填充sql
  handler.parameterize(stmt);
  return stmt;
}

hasStatementFor

判断sql是否命中缓存

private boolean hasStatementFor(String sql) {
  try {
    //从缓存中获取对应的Statement对象
    Statement statement = statementMap.get(sql);
    return statement != null && !statement.getConnection().isClosed();
  } catch (SQLException e) {
    return false;
  }
}

getStatement

获取缓存对象

private Statement getStatement(String s) {
  return statementMap.get(s);
}

putStatement

private void putStatement(String sql, Statement stmt) {
  statementMap.put(sql, stmt);
}

doUpdate

@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
  Configuration configuration = ms.getConfiguration();
  StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
  Statement stmt = prepareStatement(handler, ms.getStatementLog());
  return handler.update(stmt);
}

doQuery

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  Configuration configuration = ms.getConfiguration();
  StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
  Statement stmt = prepareStatement(handler, ms.getStatementLog());
  return handler.query(stmt, resultHandler);
}

doQueryCursor

protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
  Configuration configuration = ms.getConfiguration();
  StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
  Statement stmt = prepareStatement(handler, ms.getStatementLog());
  return handler.queryCursor(stmt);
}

CachingExecutor

与二级缓存有关的配置

1、全局配置文件的cacheEnabled属性,是二级缓存配置的总开关

<!-- 全局配置参数,需要时再设置 -->
<settings>
    <!-- 开启二级缓存  默认值为true -->
    <setting name="cacheEnabled" value="true"/>
</settings>

2、mapper.xml文件中配置<cache><cache-ref>节点

<!--开启本mapper的namespace下的二级缓存-->
<!--
    eviction:代表的是缓存回收策略,目前MyBatis提供以下策略。
     (1) LRU,最近最少使用的,一处最长时间不用的对象
     (2) FIFO,先进先出,按对象进入缓存的顺序来移除他们
     (3) SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象
     (4) WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。
         这里采用的是LRU,  移除最长时间不用的对象
     flushInterval:刷新间隔时间,单位为毫秒,如果你不配置它,那么当SQL被执行的时候 才会去刷新缓存。
     size:引用数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。
         这里配置的是1024个对象
	 readOnly:只读,意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有
             办法修改缓存,他的默认值是false,不允许我们修改
      -->
<cache eviction="回收策略" type="缓存类"/>

3、查询节点中配置useCache属性,为true表示查询结果需要保存到二级缓存中

二级缓存的生命周期和作用域

二级缓存的作用域是一个namespace,在这个namespace中操作的所有SqlSession都可以共享这个二级缓存。

二级缓存的生命周期是整个应用级别。

二级缓存的结构

在这里插入图片描述

TransactionalCache

TransactionalCache是CachingExecutor依赖的组件,TransactionalCache实现了Cache接口,作用是保存某个sqlSession的某个事务中需要向某个二级缓存中添加的缓存数据,换句话说就是:某些缓存数据会先保存在这里,然后再提交到二级缓存中,他是二级缓存防止脏读的关键。

属性

private static final Log log = LogFactory.getLog(TransactionalCache.class);
//被装饰的二级缓存对象
private final Cache delegate;
//当clearOnCommit为true时,在事务提交的时候会将delegate清空,此时表示当前缓存不可查询
private boolean clearOnCommit;
//事务提交时需要被添加到二级缓存的数据
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<>();
  this.entriesMissedInCache = new HashSet<>();
}

getObject

public Object getObject(Object key) {
  // issue #116
  //查询二级缓存中是否存在
  Object object = delegate.getObject(key);
  if (object == null) {
    //如果二级缓存中不存在则将其key添加到entriesMissedInCache中
    entriesMissedInCache.add(key);
  }
  //clearOnCommit为true表示在提交事务后会情况二级缓存,所以这里会返回一个null
  if (clearOnCommit) {
    return null;
  } else {
    return object;
  }
}

putObject

将数据暂存到entriesToAddOnCommit 中,等提交事务的时候再将其保存到二级缓存中

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

commit

//提交事务
public void commit() {
  if (clearOnCommit) {
    //如果需要提交则将二级缓存清除
    delegate.clear();
  }
  //将entriesToAddOnCommit中暂存的数据写入二级缓存
  flushPendingEntries();
  reset();
}

rollback

回滚事务

public void rollback() {
  unlockMissedEntries();
  reset();
}

flushPendingEntries

将entriesToAddOnCommit中暂存的数据写入二级缓存

private void flushPendingEntries() {
  for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
    delegate.putObject(entry.getKey(), entry.getValue());
  }
  for (Object entry : entriesMissedInCache) {
    if (!entriesToAddOnCommit.containsKey(entry)) {
      //没有命中的key写入二级缓存的目的是什么
      delegate.putObject(entry, null);
    }
  }
}

unlockMissedEntries

在真实缓存中把这部分key和对应的value删除,从而保证数据的一致性

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

TransactionalCacheManager通过一个transactionalCaches属性来管理多个TransactionalCache对象,transactionalCaches属性定义如下:

private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();

其中Map的key为二级缓存对象,value为对应的TransactionalCache对象

TransactionalCacheManager和TransactionalCache的关系如下:

在这里插入图片描述

CachingExecutor的代码如下

/**
 * @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);
  }

  @Override
  public Transaction getTransaction() {
    return delegate.getTransaction();
  }
  //关闭执行器
  @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();
  }

  @Override
  public int update(MappedStatement ms, Object parameterObject) throws SQLException {
    //是否刷新缓存
    flushCacheIfRequired(ms);
    return delegate.update(ms, parameterObject);
  }

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

  @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 {
    //调用 MappedStatement#getCache() 方法,获得Cache对象,即当前MappedStatement对象所对应的二级缓存。
    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.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    //不使用二级缓存,则从一级缓存或数据库中查询
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

  @Override
  public List<BatchResult> flushStatements() throws SQLException {
    return delegate.flushStatements();
  }

  @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();
      }
    }
  }

  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.");
        }
      }
    }
  }

  @Override
  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
  }

  @Override
  public boolean isCached(MappedStatement ms, CacheKey key) {
    return delegate.isCached(ms, key);
  }

  @Override
  public void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType) {
    delegate.deferLoad(ms, resultObject, property, key, targetType);
  }

  @Override
  public void clearLocalCache() {
    //清空一级缓存
    delegate.clearLocalCache();
  }
  //通过@Options(flushCache = Options.FlushCachePolicy.TRUE) 或 <select flushCache="true"> 方式,开启需要清空缓存
  //此时清空的只是当前事务中查询数据产生的缓存。而真正的清空,是在事务的提交时完成的
  private void flushCacheIfRequired(MappedStatement ms) {
    Cache cache = ms.getCache();
    if (cache != null && ms.isFlushCacheRequired()) {
      tcm.clear(cache);
    }
  }

  @Override
  public void setExecutorWrapper(Executor executor) {
    throw new UnsupportedOperationException("This method should not be called");
  }

}

查询流程如下:
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值