Mybaits-Executor

SqlSessionFactory sqlSessionFactory = 
    new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

  从以上代码来看,SqlSession是Mybatis提供操作数据库的API,但是从SqlSession源代码来看真正执行SQL的是Executor组件。Executor接口中定义了操作shujuk的增删改查方法,其中query和queryCursor方法用于执行查询类SQL,update方法用于执行插入、删除和修改类SQL。
在这里插入图片描述
  如上图所示,Executor接口有BaseExecutor和CachingExecutor两个直接实现类,然后SimpleExecutor、ReuseExecutor和SimpleExecutor三个类又继承自BaseExecutor抽象类。根据源代码和UML来看Executor组件使用了模板方法设计模式。

Executor

public interface Executor {

  int update(MappedStatement ms, Object parameter) throws SQLException;
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
  <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
  List<BatchResult> flushStatements() throws SQLException;
  void commit(boolean required) throws SQLException;
  void rollback(boolean required) throws SQLException;
  CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
  boolean isCached(MappedStatement ms, CacheKey key);
  void clearLocalCache();
  void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
  Transaction getTransaction();
  void close(boolean forceRollback);
  boolean isClosed();
  void setExecutorWrapper(Executor executor);
}

  Executor接口定义了执行查询SQL、执行更新SQL、提交、回滚和关闭连接等操作数据库和事务的方法。

BaseExecutor

  以SqlSession接口DefaultSqlSession实现类的selectList方法为切入口,首先从configuration配置类中获取当前SQL对应的MappedStatement,然后调用executor的query方法来执行SQL。

private <E> List<E> selectList(
    String statement, Object parameter, 
    RowBounds rowBounds, ResultHandler handler) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
}

  BaseExecutor抽象类实现了Executor接口中定义的方法的执行流程及通用的处理逻辑,但是其中核心的处理逻辑交给子类来实现,是典型的模板方法模式。

  @Override
  public <E> List<E> query(
      MappedStatement ms, Object parameter, 
      RowBounds rowBounds, ResultHandler resultHandler, 
      CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

以query()方法源代码为例,BaseExecutor抽象类实现逻辑如下:

  1. 如果当前Executor处于关闭状态,就直接抛出ExecutorException;
  2. 判断是否需要清理查询缓存;
  3. 根据当前查询SQL从缓存中获取结果,如果缓存中存在结果就交给handleLocallyCachedOutputParameters进行结果处理;
  4. 如果缓存中不存在,则交给queryFromDatabase方法从数据库中获取数据;
    1. 调用doQuery抽象方法,在子类中实现真正的查询逻辑
    2. 更新处理缓存
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
}

Executor实现类

  Mybatis提供了SimpleExecutor、ResueExecutor和BatchExecutor三种Executor实现,这些Executor都继承至BaseExecutor抽象类,并实现了其中定义的抽象方法,以完成数据库的操作。
  SimpleExecutor顾名思义就是提供最基础功能的Executor,它能够完成基本的增删改查;ResueExecutor缓存了JDBC的Statement对象,以便提高执行效率;BatchExecutor会将通过同一个Mapper执行的update、insert和delete操作转换为批量执行。
  Mybatis还提供了一个带有缓存功能的CachingExecutor,CachingExecutor直接实现了Executor接口,并不像SimpleExecutor、ResueExecutor和BatchExecutor那样继承自BaseExecutor抽象类,所以我们可以将CachingExecutor当做另一条线的Executor。

@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) {
          // delegate为真正执行逻辑的executor
          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);
  }

  从CachingExecutor类中query()方法的源代码来看,CachingExecutor在执行查询逻辑之前做了一系列的缓存操作。在从缓存中没有获取到数据的时候,才会通过调用真正的Executor来执行对应的查询操作,由此可见,CachingExecutor使用了装饰器模式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乐只乐之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值