Mybatis-StatementHandler源码解析

Mybatis3.5.1源码分析

  1. Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源码解析
  2. Mybatis-Configuration源码解析
  3. Mybatis-事务对象源码解析
  4. Mybatis-数据源源码解析
  5. Mybatis缓存策略源码解析
  6. Mybatis-DatabaseIdProvider源码解析
  7. Mybatis-TypeHandler源码解析
  8. Mybatis-Reflector源码解析
  9. Mybatis-ObjectFactory,ObjectWrapperFactory源码分析
  10. Mybatis-Mapper各类标签封装类源码解析
  11. Mybatis-XMLMapperBuilder,XMLStatmentBuilder源码分析
  12. Mybatis-MapperAnnotationBuilder源码分析
  13. [Mybatis-MetaObject,MetaClass源码解析]https://www.jianshu.com/p/f51fa552f30a)
  14. Mybatis-LanguageDriver源码解析
  15. Mybatis-SqlSource源码解析
  16. Mybatis-SqlNode源码解析
  17. Mybatis-KeyGenerator源码解析
  18. Mybatis-Executor源码解析
  19. Mybatis-ParameterHandler源码解析
  20. Mybatis-StatementHandler源码解析
  21. Mybatis-DefaultResultSetHandler(一)源码解析
  22. Mybatis-DefaultResultSetHandler(二)源码解析
  23. Mybatis-ResultHandler,Cursor,RowBounds 源码分析
  24. Mybatis-MapperProxy源码解析
  25. Mybatis-SqlSession源码解析
  26. Mybatis-Interceptor源码解析

StatementHandler

/**
 *    Copyright 2009-2016 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor.statement;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.session.ResultHandler;

/**
 * StatementHandler负责处理Mybatis与JDBC之间Statement的交互,而JDBC中的Statement,就是负责与数据库进行交互的对象。
 * <p>
 *     参考博客:
 * <ul>
 *     <li><a href='https://www.jianshu.com/p/39270777d3f6'>https://www.jianshu.com/p/39270777d3f6</a></li>
 * </ul>     <a href='https://www.jianshu.com/p/19686af69b0d'>https://www.jianshu.com/p/19686af69b0d</a>
 * </p>
 *
 * @author Clinton Begin
 */
public interface StatementHandler {

  /**
   * 根据MappedStatement对象配置的StatementType,去创建Statement对象或则PreparedStatment或则CallableStatment
   * @param connection 数据库连接对象
   * @param transactionTimeout 事务超时时间
   */
  Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;

  /**
   * 主要针对PreparedStatment或则CallableStatment关联预编译SQL语句中占位符进行修改
   * @param statement 执行SQL 语句并返回它所生成结果的对象
   */
  void parameterize(Statement statement)
      throws SQLException;

  /**
   * 将 {@code statement} 添加到批处理队列
   * @param statement 执行SQL 语句并返回它所生成结果的对象
   */
  void batch(Statement statement)
      throws SQLException;

  /**
   * 用于通知Statement将[insert,update,delete]推送到数据库
   * @param statement 执行SQL 语句并返回它所生成结果的对象
   * @return 更新记录数
   */
  int update(Statement statement)
      throws SQLException;

  /**
   * 用于通知Statement将[select]推送到数据库并返回对应查询结果
   * @param statement 执行 SQL 语句并返回它所生成结果的对象
   * @param resultHandler 结果处理器
   * @param <E> 结果对象类型
   * @return 结果对象集合
   */
  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException;

  /**
   * 用于通知Statement将[select]推送到数据库并返回对应游标结果
   * @param statement 执行 SQL 语句并返回它所生成结果的对象
   * @param <E> 结果对象类型
   * @return 结果对象游标对象
   * @throws SQLException
   */
  <E> Cursor<E> queryCursor(Statement statement)
      throws SQLException;

  /**
   * 获取参数映射与可执行SQL封装类对象
   */
  BoundSql getBoundSql();

  /**
   * 获取参数处理器
   */
  ParameterHandler getParameterHandler();

}

RoutingStatementHandler

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor.statement;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

/**
 * 一个具体实现类,这个类中并没有对Statement对象具体使用,还是根据得到Excutor类型,决定创建何种类型StatementHandler对象。在MyBatis工作时,使用的StatementHandler接口对象实际上就是RoutingStatementHandler对象
 * @author Clinton Begin
 */
public class RoutingStatementHandler implements StatementHandler {

  /**
   * 真正操作的StatementHandler
   */
  private final StatementHandler delegate;

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

    //根据StatementType去新建对应的StatmentHandler对象,
    //ms.getStatementType默认是PREPARED
    switch (ms.getStatementType()) {
      case STATEMENT:
        //简单的Statement处理器,Statement:用于执行静态 SQL 语句并返回它所生成结果的对象
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        //PreparedStatement对象处理器,PreparedStatement: 预编译的 SQL 语句的对象
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        //CallableStatement对象处理器,CallableStatmement:用于执行 SQL 存储过程的接口实例
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        //没有配置StatementType的ms还怎么执行,肯定要抛出异常啦
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }

  /**
   * 创建Statement对象或则PreparedStatment或则CallableStatment
   * @param connection 数据库连接对象
   * @param transactionTimeout 事务超时时间
   */
  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    //交由delegate处理
    return delegate.prepare(connection, transactionTimeout);
  }

  /**
   * 主要针对PreparedStatment或则CallableStatment关联预编译SQL语句中占位符进行修改
   * @param statement 执行SQL 语句并返回它所生成结果的对象
   */
  @Override
  public void parameterize(Statement statement) throws SQLException {
    //交由delegate处理
    delegate.parameterize(statement);
  }

  /**
   * 将 {@code statement} 添加到批处理队列
   * @param statement 执行SQL 语句并返回它所生成结果的对象
   */
  @Override
  public void batch(Statement statement) throws SQLException {
    //交由delegate处理
    delegate.batch(statement);
  }

  /**
   * 用于通知Statement将[insert,update,delete]推送到数据库
   * @param statement 执行SQL 语句并返回它所生成结果的对象
   * @return 更新记录数
   */
  @Override
  public int update(Statement statement) throws SQLException {
    //交由delegate处理
    return delegate.update(statement);
  }

  /**
   * 用于通知Statement将[select]推送到数据库并返回对应查询结果
   * @param statement 执行 SQL 语句并返回它所生成结果的对象
   * @param resultHandler 结果处理器
   * @param <E> 结果对象类型
   * @return 结果对象集合
   */
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    //交由delegate处理
    return delegate.query(statement, resultHandler);
  }

  /**
   * 用于通知Statement将[select]推送到数据库并返回对应游标结果
   * @param statement 执行 SQL 语句并返回它所生成结果的对象
   * @param <E> 结果对象类型
   * @return 结果对象游标对象
   * @throws SQLException
   */
  @Override
  public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
    //交由delegate处理
    return delegate.queryCursor(statement);
  }

  /**
   * 获取参数映射与可执行SQL封装类对象
   */
  @Override
  public BoundSql getBoundSql() {
    //交由delegate处理
    return delegate.getBoundSql();
  }

  /**
   * 获取参数处理器
   */
  @Override
  public ParameterHandler getParameterHandler() {
    //交由delegate处理
    return delegate.getParameterHandler();
  }
}

BaseStatementHandler

/**
 *    Copyright 2009-2016 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor.statement;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;

/**
 * 用于简化StatementHandler接口开发的难度,是适配器模式的体现,他有三个实现类:SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler。在RoutingStatementHandler创建时,就根据接受的Excutor来创建这三个类型对象。
 * @author Clinton Begin
 */
public abstract class BaseStatementHandler implements StatementHandler {

  /**
   * mybatis全局配置信息
   */
  protected final Configuration configuration;
  /**
   * 对象工厂
   */
  protected final ObjectFactory objectFactory;
  /**
   * 类型处理注册器,从mybatis全局配置信息中获取
   */
  protected final TypeHandlerRegistry typeHandlerRegistry;
  /**
   * 结果集处理器
   */
  protected final ResultSetHandler resultSetHandler;

  /**
   * 参数处理器
   */
  protected final ParameterHandler parameterHandler;

  /**
   * 执行器
   */
  protected final Executor executor;
  /**
   *  Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
   */
  protected final MappedStatement mappedStatement;
  /**
   *  Mybatis的分页对象
   */
  protected final RowBounds rowBounds;
  /**
   * 参数映射与可执行SQL封装类对象
   */
  protected BoundSql boundSql;

  /**
   *
   * @param executor 执行器
   * @param mappedStatement Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
   * @param parameterObject 参数对象
   * @param rowBounds Mybatis的分页对象
   * @param resultHandler 结果处理器
   * @param boundSql 参数映射与可执行SQL封装类对象
   */
  protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    this.configuration = mappedStatement.getConfiguration();
    this.executor = executor;
    this.mappedStatement = mappedStatement;
    this.rowBounds = rowBounds;

    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();

    if (boundSql == null) { // issue #435, get the key before calculating the statement
      //调用mappedStatement配置的KeyGenerator,在executor执行boundSQL对象之前,生成Key赋值到parameter中
      generateKeys(parameterObject);
      //构建boundSQL
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }

    this.boundSql = boundSql;
    //构建参数处理器,默认情况下,构建的是DefaultParameteHandler实例
    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    //构建结果集处理器,构建出来的是DefaultResultSetHandler实例
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  }

  /**
   * 获取参数映射与可执行SQL封装类对象
   */
  @Override
  public BoundSql getBoundSql() {
    return boundSql;
  }

  /**
   * 获取参数处理器
   */
  @Override
  public ParameterHandler getParameterHandler() {
    return parameterHandler;
  }

  /**
   * 创建Statement对象或则PreparedStatment或则CallableStatment
   * @param connection 数据库连接对象
   * @param transactionTimeout 事务超时时间
   * @return 构建出来的Statement实例,已经配置了queryTimeout和fetchSize
   */
  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    //设置错误日志上下文
    ErrorContext.instance().sql(boundSql.getSql());
    //定义一个Statement
    Statement statement = null;
    try {
      //实例化Statement
      statement = instantiateStatement(connection);
      //设置Statement超时时长
      setStatementTimeout(statement, transactionTimeout);
      //设置FetchSize:
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      //关闭Statment对象
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      //关闭Statment对象
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }

  /**
   * 实例化Statement对象
   * @param connection 数据库连接
   * @return Statement对象
   */
  protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

  /**
   * 设置Statement超时时长
   * @param stmt 执行SQL 语句并返回它所生成结果的对象。
   * @param transactionTimeout 事务超时时长
   */
  protected void setStatementTimeout(Statement stmt, Integer transactionTimeout) throws SQLException {
    //定义查询超时时长变量名
    Integer queryTimeout = null;
    //如果有配置超时时长
    if (mappedStatement.getTimeout() != null) {
      //应用配置的超时时长
      queryTimeout = mappedStatement.getTimeout();

      //如果由配置全局的Statement超时时长
    } else if (configuration.getDefaultStatementTimeout() != null) {
      //应用全局配置的Statement超时时长
      queryTimeout = configuration.getDefaultStatementTimeout();
    }
    //如果queryTime不为null
    if (queryTimeout != null) {
      //将queryTime设置到Statement的
      stmt.setQueryTimeout(queryTimeout);
    }
    //应用事务超时。事务超时大于查询时间,则不做更改;否则将查询时间修改成事务时间
    StatementUtil.applyTransactionTimeout(stmt, queryTimeout, transactionTimeout);
  }

  /**
   *
   * 设置FetchSize:
   * <p>
   *     这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为 unset(依赖驱动)。
   * </p>
   * <br/>
   * <p>
   *     假设fetchSize为5,查询总记录数为100,每批5次返回给你,最后结果还是总记录数,只是提高点查询的速度而已
   * </p>
   * <br/>
   * <p>
   *     MySQL不支持fetchSize,默认为一次性取出所有数据。所以容易导致OOM,
   *     如果是Oracle的话就是默认取出fetchSize条数据。
   *     裸露JDBC防止OOM可以调用statement的enableStreamingResults方法,
   *     MyBatis应该在&lt;select fetchSize="-2147483648"&gt;。
   * </p>
   * @param stmt 执行SQL 语句并返回它所生成结果的对象。
   */
  protected void setFetchSize(Statement stmt) throws SQLException {
    //获取select标签的fetchSize属性
    Integer fetchSize = mappedStatement.getFetchSize();
    //如有设置fetchSize
    if (fetchSize != null) {
      //设置fetchSize到stmt
      stmt.setFetchSize(fetchSize);
      return;
    }
    //获取全局配置的FetchSize
    Integer defaultFetchSize = configuration.getDefaultFetchSize();
    //如有设置fetchSize
    if (defaultFetchSize != null) {
      //设置fetchSize到stmt
      stmt.setFetchSize(defaultFetchSize);
    }
  }

  /**
   * 关闭Statment对象
   * @param statement 执行SQL 语句并返回它所生成结果的对象
   */
  protected void closeStatement(Statement statement) {
    try {
      //如果statment不为null
      if (statement != null) {
        //关闭statment
        statement.close();
      }
    } catch (SQLException e) {
      //ignore 忽略关闭时的异常
    }
  }

  /**
   * 生成Key
   * @param parameter 参数对象
   */
  protected void generateKeys(Object parameter) {
    //获取配置的Key生成器
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    //保存当前的上下文
    ErrorContext.instance().store();
    //在executor执行boundSQL对象之前,生成Key赋值到parameter中
    keyGenerator.processBefore(executor, mappedStatement, null, parameter);
    //恢复当前上下文
    ErrorContext.instance().recall();
  }

}

CallableStatementHandler

/**
 *    Copyright 2009-2018 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor.statement;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.mapping.ResultSetType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.JdbcType;

/**
 * CallableStatement对象处理器,CallableStatmement:用于执行 SQL 存储过程的接口实例
 * @author Clinton Begin
 */
public class CallableStatementHandler extends BaseStatementHandler {

  /**
   *
   * @param executor 执行器
   * @param mappedStatement  Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
   * @param parameter 参数对象
   * @param rowBounds Mybatis的分页对象
   * @param resultHandler 结果处理器
   * @param boundSql 参数映射与可执行SQL封装类对象
   */
  public CallableStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
  }

  /**
   * 用于通知Statement将[insert,update,delete]推送到数据库
   * @param statement CallableStatement对象
   * @return 更新记录数
   * @throws SQLException
   */
  @Override
  public int update(Statement statement) throws SQLException {
    //将statement强转成CallableStatement对象
    CallableStatement cs = (CallableStatement) statement;
    //执行SQL
    cs.execute();
    //获取更新记录数
    int rows = cs.getUpdateCount();
    //获取参数对象
    Object parameterObject = boundSql.getParameterObject();
    //获取Key生成器
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    //执行Key生成器的业务逻辑,将结果赋值到参数对象对应属性中
    keyGenerator.processAfter(executor, mappedStatement, cs, parameterObject);
    /**
     * 处理存储过程结果映射,将结果对象赋值到参数对象的属性中(这里的属性是指:参数属性映射配置里配置属性的ParameterType
     * 为ParameterMode.OUT或者ParameterMode.INOUT的属性)
     */
    resultSetHandler.handleOutputParameters(cs);
    return rows;
  }

  /**
   * 将 statement 添加到批处理队列
   * @param statement 执行SQL 语句并返回它所生成结果的对象
   * @throws SQLException
   */
  @Override
  public void batch(Statement statement) throws SQLException {
    //将statement强转成CallableStatement对象
    CallableStatement cs = (CallableStatement) statement;
    //添加到批量队列中
    cs.addBatch();
  }

  /**
   * 用于通知Statement将[select]推送到数据库并返回对应查询结果
   * @param statement CallableStatement对象
   * @param resultHandler 结果处理器
   * @param <E> 结果对象类型
   * @return 结果对象集合
   */
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    //将statement强转成CallableStatement对象
    CallableStatement cs = (CallableStatement) statement;
    //执行SQL
    cs.execute();
    //处理cs返回所有结果集,返回结果对象集合
    List<E> resultList = resultSetHandler.handleResultSets(cs);
    /**
     * 处理存储过程结果映射,将结果对象赋值到参数对象的属性中(这里的属性是指:参数属性映射配置里配置属性的ParameterType
     * 为ParameterMode.OUT或者ParameterMode.INOUT的属性)
     */
    resultSetHandler.handleOutputParameters(cs);
    return resultList;
  }

  @Override
  public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
    //将statement强转成CallableStatement对象
    CallableStatement cs = (CallableStatement) statement;
    //执行SQL
    cs.execute();
    //处理cs返回所有结果集,返回结果对象游标
    Cursor<E> resultList = resultSetHandler.handleCursorResultSets(cs);
    /**
     * 处理存储过程结果映射,将结果对象赋值到参数对象的属性中(这里的属性是指:参数属性映射配置里配置属性的ParameterType
     * 为ParameterMode.OUT或者ParameterMode.INOUT的属性)
     */
    resultSetHandler.handleOutputParameters(cs);
    return resultList;
  }

  /**
   * 实例化Statement对象
   * @param connection 数据库连接
   * @return
   * @throws SQLException
   */
  @Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    //获取可执行SQL
    String sql = boundSql.getSql();
    //mappedStatement对象配置的结果集类型为依赖驱动
    if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
      //创建一个 CallableStatement 对象来调用数据库存储过程。
      return connection.prepareCall(sql);
    } else {
      /**
       * ResultSetType.FORWARD_ONLY:结果集的游标只能向下滚动
       * ResultSetType.SCROLL_INSENSITIVE:结果集的游标可以上下移动,当数据库变化时,当前结果集不变
       * ResultSetType.SCROLL_SENSITIVE:返回可滚动的结果集,当数据库变化时,当前结果集同步改变
       *
       * ResultSet.CONCUR_READ_ONLY:不能用结果集更新数据库中的表。
       * ResultSet.CONCUR_UPDATABLE:能用结果集更新数据库中的表
       */
      //创建一个 CallableStatement 对象,该对象将生成具有给定类型和并发性的 ResultSet 对象。
      return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
  }

  /**
   * 主要针对PreparedStatment或则CallableStatment关联预编译SQL语句中占位符进行修改
   * @param statement CallableStatement对象
   */
  @Override
  public void parameterize(Statement statement) throws SQLException {
    //注册输出参数到statement
    registerOutputParameters((CallableStatement) statement);
    //设置参数值到statement
    parameterHandler.setParameters((CallableStatement) statement);
  }

  /**
   * 注册输出参数到 {@code cs}
   * @param cs 用于执行 SQL 存储过程的接口实例
   */
  private void registerOutputParameters(CallableStatement cs) throws SQLException {
    //获取参数映射集合
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    //遍历参数映射集合
    for (int i = 0, n = parameterMappings.size(); i < n; i++) {
      //获取参数映射
      ParameterMapping parameterMapping = parameterMappings.get(i);
      //如果参数映射配置的模式为 输出参数 或者为 输入输出参数
      if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {
        //如果参数映射配置jdbc类型为null
        if (null == parameterMapping.getJdbcType()) {
          //抛出异常
          throw new ExecutorException("The JDBC Type must be specified for output parameter.  Parameter: " + parameterMapping.getProperty());
        } else {
          //如果有配置小数点后保留的位数 且 配置的jdbc类型为JdbcType.NUMERIC 或者是 JdbcType.DECIMAL
          if (parameterMapping.getNumericScale() != null && (parameterMapping.getJdbcType() == JdbcType.NUMERIC || parameterMapping.getJdbcType() == JdbcType.DECIMAL)) {
            // 按顺序位置 parameterIndex 将参数注册,设置JDBC类型为JdbcType.NUMERIC 或者 JdbcType.DECIMAL,并设置其小数点后保留的位数
            cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getNumericScale());
          } else {
            //如果没有配置jdbc类型名
            if (parameterMapping.getJdbcTypeName() == null) {
              // 按顺序位置 parameterIndex 将参数注册,设置jdbc类型为参数映射所配置的jdbc类型
              cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE);
            } else {
              // 按顺序位置 parameterIndex 将参数注册,设置jdbc类型为参数映射所配置的jdbc类型,并设置jdbc类型名
              cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getJdbcTypeName());
            }
          }
        }
      }
    }
  }

}

PreparedStatementHandler

/**
 *    Copyright 2009-2018 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor.statement;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultSetType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

/**
 * PreparedStatement对象处理器,PreparedStatement: 预编译的 SQL 语句的对象
 * @author Clinton Begin
 */
public class PreparedStatementHandler extends BaseStatementHandler {

  /**
   *
   * @param executor 执行器
   * @param mappedStatement  Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
   * @param parameter 参数对象
   * @param rowBounds Mybatis分页对象
   * @param resultHandler 结果处理器
   * @param boundSql 参数映射与可执行SQL封装类对象
   */
  public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
  }

  /**
   * 用于通知Statement将[insert,update,delete]推送到数据库
   * @param statement PreparedStatement对象
   * @return 更新记录数
   * @throws SQLException
   */
  @Override
  public int update(Statement statement) throws SQLException {
    //将statement强转成PreparedStatment对象
    PreparedStatement ps = (PreparedStatement) statement;
    //执行SQL
    ps.execute();
    //获取更新记录数
    int rows = ps.getUpdateCount();
    //获取参数对象
    Object parameterObject = boundSql.getParameterObject();
    //获取Key生成器
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    //执行Key生成器的业务逻辑,将结果赋值到参数对象对应属性中
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    return rows;
  }

  /**
   * 将{@code statment} 添加到批量队列中
   * <p>
   *     参考文档:<a href='https://www.cnblogs.com/liqiu/p/3825544.html'>https://www.cnblogs.com/liqiu/p/3825544.html</a>
   * </p>
   * @param statement PreparedStatement对象
   * @throws SQLException
   */
  @Override
  public void batch(Statement statement) throws SQLException {
    //将statement强转成PreparedStatement对象
    PreparedStatement ps = (PreparedStatement) statement;
    //添加到批量队列中
    ps.addBatch();
  }

  /**
   * 用于通知Statement将[select]推送到数据库并返回对应查询结果
   * @param statement PreparedStatement对象
   * @param resultHandler 结果处理器
   * @param <E> 结果对象类型
   * @return 结果对象集合
   */
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    //将statement强转成PreparedStatement对象
    PreparedStatement ps = (PreparedStatement) statement;
    //执行SQL
    ps.execute();
    // 处理 ps 返回所有结果集,返回结果对象集合
    return resultSetHandler.handleResultSets(ps);
  }

  /**
   * 用于通知Statement将[select]推送到数据库并返回对应游标结果
   * @param statement PreparedStatement对象
   * @param <E> 结果对象类型
   * @return 结果对象游标
   */
  @Override
  public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
    //将statement强转成PreparedStatement对象
    PreparedStatement ps = (PreparedStatement) statement;
    //执行SQL
    ps.execute();
    //处理stmt返回所有结果集,返回结果对象游标
    return resultSetHandler.handleCursorResultSets(ps);
  }

  /**
   * 实例化Statement对象
   * @param connection 数据库连接
   * @return Statement对象
   * @throws SQLException
   */
  @Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    //获取可执行的SQL
    String sql = boundSql.getSql();
    //如果Key生成器是Jdbc3KeyGenerator实例
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
      //获取配置的列名
      String[] keyColumnNames = mappedStatement.getKeyColumns();
      //如果配置的列名为null
      if (keyColumnNames == null) {
        /**
         * PreparedStatement.RETURN_GENERATED_KEYS:使用的是INSERT语句时,可以取出新插入数据行中自动增长的列的值
         * PreparedStatement.NO_GENERATED_KEYS:不会取出新插入数据行中自动增长的列的值
         */
        //创建一个默认 PreparedStatement 对象,该对象能获取自动生成的键
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        //创建一个能返回由keyColumnNames指定的自动生成键的默认 PreparedStatement 对象
        return connection.prepareStatement(sql, keyColumnNames);
      }
      //mappedStatement对象配置的结果集类型为依赖驱动
    } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
      //创建一个 PreparedStatement 对象来将参数化的 SQL 语句发送到数据库。
      return connection.prepareStatement(sql);
    } else {
      /**
       * ResultSetType.FORWARD_ONLY:结果集的游标只能向下滚动
       * ResultSetType.SCROLL_INSENSITIVE:结果集的游标可以上下移动,当数据库变化时,当前结果集不变
       * ResultSetType.SCROLL_SENSITIVE:返回可滚动的结果集,当数据库变化时,当前结果集同步改变
       *
       * ResultSet.CONCUR_READ_ONLY:不能用结果集更新数据库中的表。
       * ResultSet.CONCUR_UPDATABLE:能用结果集更新数据库中的表
       */
      //创建一个 PreparedStatement 对象,该对象将生成具有给定类型和并发性的 ResultSet 对象。
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
  }

  /**
   * 主要针对PreparedStatment或则CallableStatment关联预编译SQL语句中占位符进行修改
   * @param statement PreparedStatement对象
   */
  @Override
  public void parameterize(Statement statement) throws SQLException {
    //将参数对象赋值到statement中
    parameterHandler.setParameters((PreparedStatement) statement);
  }

}

SimpleStatementHandler

/**
 *    Copyright 2009-2018 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor.statement;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultSetType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

/**
 * 简单的Statement处理器,Statement:用于执行静态 SQL 语句并返回它所生成结果的对象
 * @author Clinton Begin
 */
public class SimpleStatementHandler extends BaseStatementHandler {

  /**
   *
   * @param executor 执行器
   * @param mappedStatement Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
   * @param parameter 参数对象
   * @param rowBounds Mybatis的分页对象
   * @param resultHandler 结果处理器
   * @param boundSql 参数映射与可执行SQL封装类对象
   */
  public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
  }

  @Override
  public int update(Statement statement) throws SQLException {
    //获取SQL脚本
    String sql = boundSql.getSql();
    //获取参数对象
    Object parameterObject = boundSql.getParameterObject();
    //获取Key生成器
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    //已更新的函数
    int rows;
    if (keyGenerator instanceof Jdbc3KeyGenerator) {
      //PreparedStatement.RETURN_GENERATED_KEYS:获取刚刚插入自增ID值
      //执行SQL
      statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
      //获取更新记录数
      rows = statement.getUpdateCount();
      //Keys生成器将更新之后主键信息赋值到参数对象对应的属性中
      keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
    } else if (keyGenerator instanceof SelectKeyGenerator) {
      //执行SQL
      statement.execute(sql);
      //获取更新记录数
      rows = statement.getUpdateCount();
      //Keys生成器执行DML标签中的selectKey标签的SQL脚本,并将结果赋值到参数对象对应的属性中
      keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
    } else {
      //执行SQL
      statement.execute(sql);
      //获取更新记录数
      rows = statement.getUpdateCount();
    }
    return rows;
  }

  /**
   * 批处理,只是获取boundSql的可执行SQL,然后添加到 {@code statment}
   * @param statement 执行 SQL 语句并返回它所生成结果的对象
   */
  @Override
  public void batch(Statement statement) throws SQLException {
    //获取可执行SQL
    String sql = boundSql.getSql();
    //添加批处理队列中
    statement.addBatch(sql);
  }

  /**
   * 用于通知Statement将[select]推送到数据库并返回对应查询结果
   * @param statement 执行 SQL 语句并返回它所生成结果的对象
   * @param resultHandler 结果处理器
   * @param <E> 结果对象类型
   * @return
   */
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    //获取可执行SQL
    String sql = boundSql.getSql();
    //执行SQL
    statement.execute(sql);
    //处理 statement 中的所有结果集,返回结果对象集合
    return resultSetHandler.handleResultSets(statement);
  }

  /**
   * 用于通知Statement将[select]推送到数据库并返回对应游标结果
   * @param statement 执行 SQL 语句并返回它所生成结果的对象
   * @param <E> 结果对象类型
   * @return 结果对象游标对象
   */
  @Override
  public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
    //获取可执行SQL
    String sql = boundSql.getSql();
    //执行SQL
    statement.execute(sql);
    //处理statement 中的所有结果集,返回结果对象游标
    return resultSetHandler.handleCursorResultSets(statement);
  }

  /**
   * 实例化Statment对象
   * @param connection 数据库连接
   * @return 执行 SQL 语句并返回它所生成结果的对象
   */
  @Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    //ResultSetType.DEFAULT:表示依赖驱动进行创建Statement实例
    if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
      //创建一个 Statement 对象来将 SQL 语句发送到数据库
      return connection.createStatement();
    } else {
      /**
       * ResultSetType.FORWARD_ONLY:结果集的游标只能向下滚动
       * ResultSetType.SCROLL_INSENSITIVE:结果集的游标可以上下移动,当数据库变化时,当前结果集不变
       * ResultSetType.SCROLL_SENSITIVE:返回可滚动的结果集,当数据库变化时,当前结果集同步改变
       *
       * ResultSet.CONCUR_READ_ONLY:不能用结果集更新数据库中的表。
       * ResultSet.CONCUR_UPDATABLE:能用结果集更新数据库中的表
       */
      //创建一个 Statement 对象,该对象将生成具有给定类型和并发性的 ResultSet 对象。
      return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
  }

  @Override
  public void parameterize(Statement statement) {
    // N/A N/A是Not Applicable 的缩写,表示“不适用”的意思
  }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容