MyBatis源码通~StatementHandler&ParameterHandler

StatementHandler&ParameterHandler

StatementHandler是连接Mybatis和JDBC之间的桥梁
MyBatis源码通~StatementHandler&ParameterHandler_第1张图片
在执行SQL之前,StatementHandler需要创建合适的 Statement 对象,然后通过ParameterHandler将参数值填充到 Statement 对象中,最后通过 Statement.execute()执行 SQL。SQL 执行完后,通过ResultSetHandler处理结果。

//-☆☆- SimpleExecutor
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      //NOTE☆: 创建StatementHandler(创建时会加载配置的拦截器Interceptor)
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //NOTE☆: 创建Statement(和JDBC开始关联)
      stmt = prepareStatement(handler, ms.getStatementLog());
      //NOTE☆: 调用StatementHandler来真正地执行SQL命令
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

1、 创建RoutingStatementHandler

调用configuration.newStatementHandler创建StatementHandler,实际创建的是RoutingStatementHandler对象,其作用是根据不同的StatementType来进行路由并创建不同的StatementHandler。(相当于一个装饰器模式或者策略模式),Configuration在创建StatementHandler时直接创建RoutingStatementHandler,在其内部用StatementHandler delegate;来记录正在的StatementHandler。后续所有关于StatementHandler的操作都会经过RoutingStatementHandler的相应方法来执行。

1.1、interceptorChain.pluginAll(statementHandler)

加入拦截器Interceptor,会为真正的StatementHandler进行封装,最后返回的是一个代理类(通过工具Plugin.wrap)

见插件Interceptor篇

2、 prepareStatement()

该方法的作用是绑定执行时所需要的实参,即把运行时参数设置到SQL中。

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //NOTE: 获取数据库连接
    Connection connection = getConnection(statementLog);
    //NOTE: 创建Statement
    stmt = handler.prepare(connection, transaction.getTimeout());
    //NOTE: 参数设置
    handler.parameterize(stmt);
    return stmt;
  }

2.1、获取数据库连接

Mybatis自身提供了PooledDataSource 和 UnpooledDataSource两种数据源,会在JdbcTransaction中通过openConnection()方法去获取Connection。(当在实际应用中会用其他的数据源来代替,就看怎么给Mybatis进行配置了)

2.2、创建Statement(以常用的PreparedStatementHandler为例)

MyBatis源码通~StatementHandler&ParameterHandler_第2张图片
PreparedStatementHandler内部依赖于java.sql.PreparedStatement对象来完成数据库的相关操作。

  • **大致执行路径:**Executor -> RoutingStatementHandler -> delegate(真正的StatementHandler) -> prepare方法 -> BaseStatementHandler -> instantiateStatement方法 -> PreparedStatementHandler

2.2.1、BaseStatementHandler

实现StatementHandler接口的抽象类,提供了一些参数绑定的公用方法、构建ParameterHandler和ResultSetHandler、以及针对插入更新删除操作会先生成自增主键。而具体的数据库操作还是在对应的StatementHandler中实现。

protected final Configuration configuration;
  protected final ObjectFactory objectFactory;
  protected final TypeHandlerRegistry typeHandlerRegistry;

  //NOTE: 将结果集映射成结果对象
  protected final ResultSetHandler resultSetHandler;

  //NOTE:为SQL语句绑定实参,也就是使用传入的实参替换SQL语句中的"?"占位符
  protected final ParameterHandler parameterHandler;

  //NOTE: 记录执行SQL的Executor对象
  protected final Executor executor;

  //NOTE: 记录SQL语句对应的MappedStatement和BoundSql
  protected final MappedStatement mappedStatement;
  protected BoundSql boundSql;

  protected final RowBounds rowBounds;

  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();

    //NOTE: <1> 如果 boundSql 为空,一般是写类操作,例如:insert、update、delete ,则先获得自增主键,然后再创建 BoundSql 对象
    if (boundSql == null) { // issue #435, get the key before calculating the statement
      generateKeys(parameterObject);
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }

    this.boundSql = boundSql;
    
    //创建ParameterHandler和ResultSetHandler
    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  }

2.2.2、instantiateStatement:从Connection中创建Statement

  • Connection:MyBatis 提供了两种基于 JDBC 接口的数据源,分别为 PooledDataSource 和 UnpooledDataSource。但在和其他框架整合使用时,Mybatis自身的数据源方式基本不会用到。
//PreparedStatementHandler
return connection.prepareStatement(....)

2.3、ParameterHandler:Statement参数设置

handler.parameterize(stmt),参数设置会使用在BaseStatementHandler中创建的ParameterHandler。

  • Mybatis中只有一个ParameterHandler的实现类DefaultParameterHandler

MyBatis源码通~StatementHandler&ParameterHandler_第3张图片

  • DefaultParameterHandler中核心的字段:
    • typeHandlerRegistry:记录Mybatis中全部的TypeHandler对象,用于参数类型转换(JdbcType -> JavaType 或 JavaType -> JbdcType)。
    • mappedStatement:记录SQL节点相应的配置信息
    • parameterObject:传入的参数对象
    • boundSql:为Statement设置参数,就是根据BoundSql中记录的SQL语句创建的。
public void setParameters(PreparedStatement ps) {
    //NOTE: 获取SQL片段对应的ParameterMapping(与每个#{xxx}占位符都会生成一个ParameterMapping对象)
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          //NOTE: 参数值
          Object value;
          //NOTE: 参数名称
          String propertyName = parameterMapping.getProperty();

          if (boundSql.hasAdditionalParameter(propertyName)){             
          value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
            //NOTE: 若有对应JavaType的TypeHandler,则直接返回parameterObject,因为可以直接通过TypeHandler转化为对应的jdbcType
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            //NOTE: 可能会是对象,然后找对应属性的值,或者是一个Map则找对应key的value
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }

          //NOTE: 根据不同的typeHandler来设置参数值
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            //NOTE: 使用typeHandler 设置对应 #{} 位置的具体参数值
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {
            .....
          }
        }
      }
    }
  }

3、SQL执行:handler.query(statement)

上述已创建Statement并对于其进行实参绑定,接下来开始正在的sql执行。(以创建PreparedStatementPreparedStatementHandler为例)

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    //NOTE: PreparedStatement
    PreparedStatement ps = (PreparedStatement) statement;
    //NOTE: 执行SQL
    ps.execute();
    //NOTE: 用ResultSetHandler处理执行结果
    return resultSetHandler.handleResultSets(ps);
  }

4、结果解析:见ResultSetHandler篇

你可能感兴趣的:(MyBatis源码通,MyBatis源码通)