• 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使用了装饰器模式。

  • 相关阅读:
    浅浅的 Cmake
    Java学习之this关键字
    动态内存管理
    成考本科学位英语考试有固定合格分数线吗?
    关于受理南山区2022年度“领航人才” 租房补贴申请的通告
    主流的CPU架构
    4.使用CFileDialog打开文件对话框,获得文件路径 -windows编程
    求组合数(递推法)
    使用MySQL和SQL Server生成最近七天的日期
    【Web安全】SQL各类注入与绕过
  • 原文地址:https://blog.csdn.net/Jas000/article/details/127037137