Mybatis__Executor源码跟踪解析

讲到Mybaits就不得不提它的执行器Executor,我们知道Mybaits是ORM框架(关系映射),那么它的mapper语句就是通过Executor执行的。

Executor是一个顶层接口,它又分为两大实现类:BaseExecutor、CachingExecutor;
BaseExecutor又分为三大子类SimpleExecutor、BatchExecutor、ReuseExecutor;
在这里插入图片描述

Mybaits的Executor可以在xml配置文件中进行配置,默认使用的SimpleExecutor

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

1、Executor
(顶层接口、父接口)
在这里插入图片描述在这里插入图片描述

2.BaseExecutor
(抽象类,实现了Executor接口,实现了执行器的基本功能,采用模板方法的设计模式)
源码有点多,看部分重要的源码

2.1先来看属性和构造
在这里插入图片描述

PerpetualCache
在这里插入图片描述

2.2.事务相关处理
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

2.3. update方法
在这里插入图片描述

2.4.doUpdate
在这里插入图片描述
2.4.query
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

2.5.createCacheKey
在这里插入图片描述在这里插入图片描述

2.6.抽象方法用于给子类去实现
在这里插入图片描述
获取数据库连接
在这里插入图片描述在这里插入图片描述

2.7.Configuration
BaseExecutor的实现类是在Configuration中创建出来的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.SimpleExecutor
(默认的执行器,最简单的执行器,根据对应的sql直接执行即可,不会做一些额外的操作;拼接完SQL之后,直接交给 StatementHandler去执行。每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。(可以是Statement或PrepareStatement对象)

3.1doUpdate
在这里插入图片描述

3.2doQuery
在这里插入图片描述

3.3prepareStatement
在这里插入图片描述

3.4.prepare
在这里插入图片描述
在这里插入图片描述

3.5parameterize
在这里插入图片描述

CallableStatementHandler中的parameterize
在这里插入图片描述

ParameterHandler中的parameterize
在这里插入图片描述

SimpleStatementHandler中的parameterize
在这里插入图片描述

4.BatchExecutor(重点、难点)
执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理的;可以是Statement或PrepareStatement对象.
在这里插入图片描述

4.1doUpdate
在这里插入图片描述 在这里插入图片描述

// 缓存多个Statement对象,每个Statement都是addBatch()后,等待执行
private final List<Statement> statementList = new ArrayList<Statement>();
// 对应的结果集(主要保存了update结果的count数量)
private final List<BatchResult> batchResultList = new ArrayList<BatchResult>();
// 当前保存的sql,即上次执行的sql
private String currentSql;

/**
   * 更新
   */
  @Override
  public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
	//获取Configuration对象,获得配置信息
    final Configuration configuration = ms.getConfiguration();
    //获取StatementHandler对象
    final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
    //获取BoundSql对象
    final BoundSql boundSql = handler.getBoundSql();
    //获得Sql语句
    final String sql = boundSql.getSql();
    final Statement stmt;
    /**
     * 如果sql等于currentSql同时MappedStatement与currentStatement相同, 就是同一条SQL,但是参数可能不同,这样就不需要重复创建PrepareStatement
     * 
     * 可以减少网络交互次次数,通过源码可以发现批处理中最佳时间就是同样的sql要一起执行,不要存在不同sql间隔这样的场景出现
     */
    if (sql.equals(currentSql) && ms.equals(currentStatement)) {
      int last = statementList.size() - 1;
      //获取最后一次创建statement
      stmt = statementList.get(last);
      //设置事务超时时间
      applyTransactionTimeout(stmt);
     handler.parameterize(stmt);//fix Issues 322
      //获取对应的批量结果
      BatchResult batchResult = batchResultList.get(last);
      //将参数对象添加到参数列表中
      batchResult.addParameterObject(parameterObject);
    } else {//和上一次创建的SQL不同,则需要重新创建PrepareStatement
      //获取数据库连接	
      Connection connection = getConnection(ms.getStatementLog());
      //创建一个Statement对象
      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);
    //添加到批处理,最终是调用jdbc的批处理操作
    handler.batch(stmt);
    //返回默认值
    return BATCH_UPDATE_RETURN_VALUE;
  }

4.3.doQuery
在这里插入图片描述

/**
   * 查询
   */
  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, 
		  ResultHandler resultHandler, BoundSql boundSql)
      throws SQLException {
    Statement stmt = null;
    try {
      //刷新Statements,默认是设置为false,不进行事务回滚
      flushStatements();
      //获取配置信息
      Configuration configuration = ms.getConfiguration();
      //获取Statement处理器对象
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, 
    		  rowBounds, resultHandler, boundSql);
      //获取数据库连接
      Connection connection = getConnection(ms.getStatementLog());
      //获取Statement对象
      stmt = handler.prepare(connection, transaction.getTimeout());
      handler.parameterize(stmt);
      return handler.<E>query(stmt, resultHandler);
    } finally {
      //关闭Statement
      closeStatement(stmt);
    }
  }

4.4. doFlushStatements

/**
   * 所调用的flushStatements其实就是调用了doFlushStatements
   */
  @Override
  public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
    try {
      List<BatchResult> results = new ArrayList<BatchResult>();
      //默认isRollback为false,如果true,进行了事务回滚返回空列表
      if (isRollback) {
        return Collections.emptyList();
      }
      for (int i = 0, n = statementList.size(); i < n; i++) {//遍历所有satement
        Statement stmt = statementList.get(i);
        applyTransactionTimeout(stmt);
        //获取对应的结果对象
        BatchResult batchResult = batchResultList.get(i);
        try {
        //stmt.executeBatch执行批处理,并将更新条数保存到执行结果中;
          batchResult.setUpdateCounts(stmt.executeBatch());
        //获取结果对应到mappedStatement
          MappedStatement ms = batchResult.getMappedStatement();
        //获取参数列表
          List<Object> parameterObjects = batchResult.getParameterObjects();
          /**
           * KeyGenerator用于生成数据库主键或将主键重置到pojo中
           * 
           * KeyGenerator接口定义了2个函数:
           * 
           * //执行insert之前
		   * 1. void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
		   * 
		   * //insert之后
		   * 2. void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
		   * 
		   * NoKeyGenerator:没有执行任何操作,并且是Mybatis的默认实现
		   * 
		   * Jdbc3KeyGenerator:适用于可以自动生成主键的sql(自增等),由于Statement.execute执行后返回的是操作行数,并不会返回主键,当配置了
		   * <insert id="insert" useGeneratedKeys="true" keyProperty="id">
		   * 后,该函数会自动将id赋值到keyProperty对应的javabean属性中
		   * 
           * SelectKeyGenerator:通过自定义sql手动获取主键值,有2种配置,before和after
           * before既是在insert之前设置到pojo中作为参数一起insert到db
           * after即为在insert之后,通过自定义sql获取并设置到pojo中
           * 
           * <selectKey resultType="java.lang.Integer" keyProperty="id" order="BEFORE">  
           * 	select max(id) from TB_USER  
           * </selectKey>
           * 
           */
          KeyGenerator keyGenerator = ms.getKeyGenerator();
          if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {
            Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;
            jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);
          } else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { //issue #141
            for (Object parameter : parameterObjects) {
              keyGenerator.processAfter(this, ms, stmt, parameter);
            }
          }
          // Close statement to close cursor #1109
          //关闭Staement
          closeStatement(stmt);
        } catch (BatchUpdateException e) {
          StringBuilder message = new StringBuilder();
          message.append(batchResult.getMappedStatement().getId())
              .append(" (batch index #")
              .append(i + 1)
              .append(")")
              .append(" failed.");
          if (i > 0) {
            message.append(" ")
                .append(i)
                .append(" prior sub executor(s) completed successfully, but will be rolled back.");
          }
          throw new BatchExecutorException(message.toString(), e, results, batchResult);
        }
        results.add(batchResult);
      }
      return results;
    } finally {
      for (Statement stmt : statementList) {//遍历一个个的关闭Statement
        closeStatement(stmt);
      }
      currentSql = null;
      statementList.clear();
      batchResultList.clear();
    }
  }

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

/**
   * 更新
   */
  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
	//获取配置信息
    Configuration configuration = ms.getConfiguration();
    //从配置信息里创建一个新的StatementHandler处理器对象
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
    //获取Statement对象
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    //处理器执行更新
    return handler.update(stmt);
  }

5.2.ReuseExecutor

/**
   * 查询
   */
  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
	//获取配置信息
	Configuration configuration = ms.getConfiguration();
	//从配置信息里创建一个新的StatementHandler处理器对象
	StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
	//获取Statement对象
	Statement stmt = prepareStatement(handler, ms.getStatementLog());
	//处理器执行查询
	return handler.<E>query(stmt, resultHandler);
  }

doFlushStatements

@Override
  public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
    for (Statement stmt : statementMap.values()) {//遍历statementMap 关闭Statement
      closeStatement(stmt);
    }
    statementMap.clear();//清除statementMap
    //返回空列表
    return Collections.emptyList();
  }

prepareStatement
在这里插入图片描述

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    BoundSql boundSql = handler.getBoundSql();
    String sql = boundSql.getSql();
    if (hasStatementFor(sql)) {//如果缓存了该SQL,则返回其Statement对象
      stmt = getStatement(sql);
      applyTransactionTimeout(stmt);
    } else {// 如果没有缓存该SQL,则创建SQL的Statement,并加入缓存
      Connection connection = getConnection(statementLog);
      stmt = handler.prepare(connection, transaction.getTimeout());
      putStatement(sql, stmt);
    }
    handler.parameterize(stmt);
    return stmt;
  }

在这里插入图片描述

/**
   * 是否缓存了sql
   * @param sql
   * @return
   */
  private boolean hasStatementFor(String sql) {
    try {
      return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed();
    } catch (SQLException e) {
      return false;
    }
  }

  /**
   * 返回指定sql的 Statement
   * @param s
   * @return
   */
  private Statement getStatement(String s) {
    return statementMap.get(s);
  }

  /**
   * 添加SQL和Statement
   * @param sql
   * @param stmt
   */
  private void putStatement(String sql, Statement stmt) {
    statementMap.put(sql, stmt);
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

偷偷学习被我发现

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

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

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

打赏作者

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

抵扣说明:

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

余额充值