zoukankan      html  css  js  c++  java
  • MyBatis学习笔记(二) Executor

    一、概述

    当我们打开一个SqlSession的时候,我们就完成了操作数据库的第一步,那MyBatis是如何执行Sql的呢?其实MyBatis的增删改查都是通过Executor执行的,Executor和SqlSession绑定在一起,由Configuration类的newExecutor方法创建。

    二、Executor类图

    首先,顶层接口是Executor,有两个实现类,分别是BaseExecutor和CachingExecutor,CachingExecutor用于二级缓存,而BaseExecutor则用于一级缓存及基础的操作,BaseExecutor是一个抽象类,又有三个实现,分别是SimpleExecutor,BatchExecutor,ReuseExecutor,而具体使用哪一个Executor则是可以在mybatis-config.xml中进行配置的,配置方式如下:

    <settings>
        <!--SIMPLE、REUSE、BATCH-->
        <setting name="defaultExecutorType" value="REUSE"/>
    </settings>

    如果没有配置Executor,默认情况下是SimpleExecutor。

    三、各个Executor介绍

    1.BaseExecutor

    相当于一个基础,实现了Executor的方法,但是只是做一些准备工作,比如查询的CacheKey定义等,以及公共方法的定义,比如close、commit、rollback方法等,而具体的执行则是定义了抽象方法doUpdate、doQuery,这些将由BaseExecutor的子类实现,如下

    BaseExecutor这里采用了模板方法模式

      protected abstract int doUpdate(MappedStatement ms, Object parameter)
          throws SQLException;
    
      protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
          throws SQLException;
    
      protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
          throws SQLException;
    
      protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
          throws SQLException;

    1.1 SimpleExecutor

    最简单的执行器,根据对应的sql直接执行,每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。(可以是Statement或PrepareStatement对象)

    1.2 BatchExecutor

    执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理的;BatchExecutor相当于维护了多个桶,每个桶里都装了很多属于自己的SQL,就像苹果蓝里装了很多苹果,番茄蓝里装了很多番茄,最后,再统一倒进仓库。(可以是Statement或PrepareStatement对象)

    通常需要注意的是批量更新操作,由于内部有缓存的实现,使用完成后记得调用flushStatements来清除缓存。

      @Override
      public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
        final Configuration configuration = ms.getConfiguration();
        final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
        final BoundSql boundSql = handler.getBoundSql();
        final String sql = boundSql.getSql();
        final Statement stmt;
        if (sql.equals(currentSql) && ms.equals(currentStatement)) {
          int last = statementList.size() - 1;
          stmt = statementList.get(last);
          applyTransactionTimeout(stmt);
         handler.parameterize(stmt);//fix Issues 322
          BatchResult batchResult = batchResultList.get(last);
          batchResult.addParameterObject(parameterObject);
        } else {
          Connection connection = getConnection(ms.getStatementLog());
          stmt = handler.prepare(connection, transaction.getTimeout());
          handler.parameterize(stmt);    //fix Issues 322
          currentSql = sql;
          currentStatement = ms;
          statementList.add(stmt);
          batchResultList.add(new BatchResult(ms, sql, parameterObject));
        }
      // handler.parameterize(stmt);
        handler.batch(stmt);
        return BATCH_UPDATE_RETURN_VALUE;
      }

    1.3 ReuseExecutor

    可重用的执行器,重用的对象是Statement,也就是说该执行器会缓存同一个sql的Statement,省去Statement的重新创建,优化性能。内部的实现是通过一个HashMap来维护Statement对象的。由于当前Map只在该session中有效,所以使用完成后记得调用flushStatements来清除Map。
    private final Map<String, Statement> statementMap = new HashMap<String, Statement>();

    2.CachingExecutor

    先从缓存中获取查询结果,存在就返回,不存在,再委托给Executor delegate去数据库取,delegate可以是上面任一的SimpleExecutor、ReuseExecutor、BatchExecutor。

    这几个Executor的生命周期都是局限于SqlSession范围内。

      public CachingExecutor(Executor delegate) {
        this.delegate = delegate;
        delegate.setExecutorWrapper(this);
      }
    
    ......
    
      @Override
      public int update(MappedStatement ms, Object parameterObject) throws SQLException {
        flushCacheIfRequired(ms);
        return delegate.update(ms, parameterObject);
      }
    
      @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> 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, 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<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);
      }
    
    ......

    四、Executor初始化

    1.先调用startManagedSession

      public void startManagedSession() {
        this.localSqlSession.set(openSession());
      }

    2.调用startManagedSession方法来使SqlSessionManager内部维护的localSqlSession变量生效,这一步操作中会涉及到对Executor的获取,代码如下:

     @Override
      public SqlSession openSession() {
        return sqlSessionFactory.openSession();
      }
    
    
     @Override
      public SqlSession openSession() {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
      }
    
      private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
          final Environment environment = configuration.getEnvironment();
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
          //这里根据不同的executortype获取不同的Executor 
          final Executor executor = configuration.newExecutor(tx, execType);
          return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
          closeTransaction(tx); // may have fetched a connection so lets call close()
          throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }

    3.最后就是文章开头所说的newExecutor方法创建返回不同的Executor 

      public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Executor executor;
        if (ExecutorType.BATCH == executorType) {
          executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
          executor = new ReuseExecutor(this, transaction);
        } else {
          executor = new SimpleExecutor(this, transaction);
        }
        if (cacheEnabled) {
          executor = new CachingExecutor(executor);
        }
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
      }

    参考文章:

    https://www.jianshu.com/p/53cc886067b1

    https://blog.csdn.net/cleargreen/article/details/80614362

  • 相关阅读:
    【以前的空间】《用单调性优化动态规划》
    【以前的空间】倍增
    【以前的空间】BIT的两个小小运用
    【以前的空间】bzoj1009 [HNOI2008]GT考试
    【以前的空间】斜率优化的一点点总结
    【以前的空间】bzoj 1227 [SDOI2009]虔诚的墓主人
    【以前的空间】bzoj 1072 [SCOI2007]排列perm
    【以前的空间】bzoj 1052 [HAOI2007]覆盖问题
    【以前的空间】bzoj [ZJOI2007]棋盘制作
    Notepad++使用技巧
  • 原文地址:https://www.cnblogs.com/hpuiotcl/p/10721089.html
Copyright © 2011-2022 走看看