美文网首页
myabtis源码解析四(mybatis中statement对象

myabtis源码解析四(mybatis中statement对象

作者: 为梦想前进 | 来源:发表于2019-10-17 15:00 被阅读0次

上一期,我们分析了sql语句的创建过程,接下来,咱们进入到下一个特别重要的对象创建,statement对象的创建
StatementHandler 是一个非常核心接口,不仅要创建合适statement对象,再将参数放到statement对象中,执行sql,处理结果集.可以说这些步骤都是在statementHandle中
实现的,接下来咱们跟着源码一起来分析,还是以查询为例,接着上期的selectOne分析

@Override
  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();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
      //创建路由statement
      StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
      //应用插件到StatementHandler上
      statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
      return statementHandler;
    }

     public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
         // 根据 StatementType 创建不同的 StatementHandler
        switch (ms.getStatementType()) {
          case STATEMENT:
            delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          case PREPARED:
            delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          case CALLABLE:
            delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
          default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }

      }

再来看一下statement的uml图


image.png

接下来,我们需要设置参数到sql中了,JDBC 提供了三种 Statement 接口,分别是 Statement、PreparedStatement 和 CallableStatement。他们的uml图为:


image.png

statement:提供执行的sql语句
PreparedStatement:对输入参数执行动态替换,也就是将之前的?替换为具体的参数
CallableStatement:用于保存存储过程输出的结果

我们来来分析PreparedStatement

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    //获取连接
    Connection connection = getConnection(statementLog);
    //创建 Statement
    stmt = handler.prepare(connection, transaction.getTimeout());
    //设置参数
    handler.parameterize(stmt);
    return stmt;
  }
  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      //创建statement
      statement = instantiateStatement(connection);
      //设置超时时间
      setStatementTimeout(statement, transactionTimeout);
      //设置fetchSize
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }

 @Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
     // 根据条件调用不同的 prepareStatement 方法创建 PreparedStatement
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
      String[] keyColumnNames = mappedStatement.getKeyColumns();
      if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql, keyColumnNames);
      }
    } else if (mappedStatement.getResultSetType() != null) {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
      return connection.prepareStatement(sql);
    }
  }

statement创建就是这样,重点看下参数是怎样设置进去的

handler.parameterize(stmt)

@Override
  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }

  @Override
    public void setParameters(PreparedStatement ps) {
      ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
      //从boundSql中获取参数映射的集合
      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) {
            Object value;
            // 获取属性名
            String propertyName = parameterMapping.getProperty();
             // 检测 BoundSql 的 additionalParameters 是否包含 propertyName
            if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
              value = boundSql.getAdditionalParameter(propertyName);
            } else if (parameterObject == null) {
              value = null;
              // 检测运行时参数是否有相应的类型解析器
            } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
              value = parameterObject;
            } else {
              // 为参数对象创建元信息对象
              MetaObject metaObject = configuration.newMetaObject(parameterObject);
              value = metaObject.getValue(propertyName);
            }
            TypeHandler typeHandler = parameterMapping.getTypeHandler();
            JdbcType jdbcType = parameterMapping.getJdbcType();
            if (value == null && jdbcType == null) {
              jdbcType = configuration.getJdbcTypeForNull();
            }
            try {
               //typeHandler对ParameterHandler 设置参数
              typeHandler.setParameter(ps, i + 1, value, jdbcType);
            } catch (TypeException e) {
              throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
            } catch (SQLException e) {
              throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
            }
          }
        }
      }
    }

我们来执行一条根据主键查询的sql语句,然后debug运行,看看参数如何设置进去的

    <!-- 根据PrimaryKey获取对象-->
    <select id="selectByPrimaryKey" resultMap="base_result_map" >
        select <include refid="base_column_list" /> from `ryx_account` where `id`=#{id}
    </select>

image.png

每一个?都会在parameterMappings集合中生成一个对象属性,执行完之后,一条正正的sql就形成了


image.png

当我们执行完sql后就要开始处理查询结果了结果集的处理工作由结果集处理器 ResultSetHandler 执行。ResultSetHandler 是一个接口,它只有一个实现类 DefaultResultSetHandler。结果集的处理入口方法是 handleResultSets,下面来看一下该方法的实现。

 @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleResultSets(ps);
  }

 @Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<Object>();

    int resultSetCount = 0;
    //获取第一个结果集
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
       // 处理结果集
      handleResultSet(rsw, resultMap, multipleResults, null);
      // 获取下一个结果集
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }

private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
   //获取结果集
    ResultSet rs = stmt.getResultSet();
    while (rs == null) {
      // move forward to get the first resultset in case the driver
      // doesn't return the resultset as the first result (HSQLDB 2.1)
      if (stmt.getMoreResults()) {
        rs = stmt.getResultSet();
      } else {
        if (stmt.getUpdateCount() == -1) {
          // no more results. Must be no resultset
          break;
        }
      }
    }
    return rs != null ? new ResultSetWrapper(rs, configuration) : null;
  }

//处理结果集方法
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
      if (parentMapping != null) {
        //处理存储过程的逻辑,多结果集
        handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
      } else {
          //这里有个细节注意下,我们可以实现resultHandler接口,处理我们自己定义的结果集类型
        if (resultHandler == null) {
          //构建默认的结果集处理对象
          DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
          //处理行值方法
          handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
          multipleResults.add(defaultResultHandler.getResultList());
        } else {
          // 处理结果集的行数据
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      // issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());
    }
  }


//处理行值方法
//处理行值方法
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) {
      ensureNoRowBounds();
      checkResultHandler();
      //处理结果集映射
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
      //处理简单结果映射
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  }

这里注意啊一下,resultHandle是一个接口,我们可以实现自己的类,用来自定义结果集,map,list等等


image.png
我们这里已简单结果映射分析为主,
  private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
        throws SQLException {
      DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
      // 根据 RowBounds 定位到指定行记录
      skipRows(rsw.getResultSet(), rowBounds);
      while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
         // 获取经过鉴别器处理后的 ResultMap
        ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
        // 从 resultSet 中获取结果
        Object rowValue = getRowValue(rsw, discriminatedResultMap);
        // 存储结果
        storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
      }
    }

private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
    if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
      if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
        rs.absolute(rowBounds.getOffset());
      }
    } else {
      for (int i = 0; i < rowBounds.getOffset(); i++) {
        //根据循环去找下一个值,如果offest的值很大,效率就会很低,所以mybatis自带的分页插件效率很低哦
        rs.next();
      }
    }
  }

接下来分析getRowValue方法(获取行值)
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    // 创建结果对象
    Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
    if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
        final MetaObject metaObject = configuration.newMetaObject(rowValue);
        boolean foundValues = this.useConstructorMappings;

        // 是否应该自动映射结果集
        if (shouldApplyAutomaticMappings(resultMap, false)) {
            // 进行自动映射
            foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
        }
        // 根据resultMap节点中配置的映射关系进行映射
        foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
        foundValues = lazyLoader.size() > 0 || foundValues;
        rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
    }
    return rowValue;
}

这个方法一共有三段逻辑,分别是
1: 创建结果对象
2:是否应该自动映射结果集
3:根据resultMap节点中配置的映射关系进行映射
我们来一一分析下

1:创建结果对象
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    //重置之前的映射结果
    this.useConstructorMappings = false; // reset previous mapping result
    final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
    final List<Object> constructorArgs = new ArrayList<Object>();
    //调用重载方法创建结果对象
    Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
    if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
      final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
      for (ResultMapping propertyMapping : propertyMappings) {
        // issue gcode #109 && issue #149
        if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
          resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
          break;
        }
      }
    }
    this.useConstructorMappings = (resultObject != null && !constructorArgTypes.isEmpty()); // set current mapping result
    return resultObject;
  }

2:是否应该自动映射结果集,这个就不分析了,伙伴们可以自行分析下

3:根据resultMap节点中配置的映射关系进行映射
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
    // 获取 UnMappedColumnAutoMapping 列表
       List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
       boolean foundValues = false;
       if (!autoMapping.isEmpty()) {
           for (UnMappedColumnAutoMapping mapping : autoMapping) {
               // 通过 TypeHandler 从结果集中获取指定列的数据
               final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
               if (value != null) {
                   foundValues = true;
               }
               if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
                   // 通过元信息对象设置 value 到实体类对象的指定字段上
                   metaObject.setValue(mapping.property, value);
               }
           }
       }
    return foundValues;
  }


private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {

    final String mapKey = resultMap.getId() + ":" + columnPrefix;
    // 从缓存中获取 UnMappedColumnAutoMapping 列表
    List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
    // 缓存未命中
    if (autoMapping == null) {
        autoMapping = new ArrayList<UnMappedColumnAutoMapping>();
        // 从 ResultSetWrapper 中获取未配置在 <resultMap> 中的列名
        final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
        for (String columnName : unmappedColumnNames) {
            String propertyName = columnName;
            if (columnPrefix != null && !columnPrefix.isEmpty()) {
                if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
                    // 获取不包含列名前缀的属性名
                    propertyName = columnName.substring(columnPrefix.length());
                } else {
                    continue;
                }
            }
            // 将下划线形式的列名转成驼峰式,比如 AUTHOR_NAME -> authorName
            final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
            if (property != null && metaObject.hasSetter(property)) {
                // 检测当前属性是否存在于 resultMap 中
                if (resultMap.getMappedProperties().contains(property)) {
                    continue;
                }
                // 获取属性的类型
                final Class<?> propertyType = metaObject.getSetterType(property);
                if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
                    // 获取类型处理器
                    final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
                    // 封装上面获取到的信息autoMapping集合中
                    autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
                } else {
                    configuration.getAutoMappingUnknownColumnBehavior()
                        .doAction(mappedStatement, columnName, property, propertyType);
                }
            } else {
                 //若 property 为空,或实体类中无 property 属性,此时无法完成
                 //列名与实体类属性建立映射关系。默认什么都不做
                configuration.getAutoMappingUnknownColumnBehavior()
                    .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
            }
        }
        // 写入缓存
        autoMappingsCache.put(mapKey, autoMapping);
    }
    return autoMapping;
}
总结下以上步骤
从 ResultSetWrapper 中获取未配置在 <resultMap> 中的列名
遍历获取到的列名列表
若列名包含列名前缀,则移除列名前缀,得到属性名
将下划线形式的列名转成驼峰式
获取属性类型
获取类型处理器
创建UnMappedColumnAutoMapping对象

我们主要看一下第一个步骤
 public List<String> getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
    //获取未映射的列名
    List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    if (unMappedColumnNames == null) {
      //加载映射和未映射的列名
      loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
      unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    }
    return unMappedColumnNames;
  }
 private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
    List<String> mappedColumnNames = new ArrayList<String>();
    List<String> unmappedColumnNames = new ArrayList<String>();
    final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
    final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
    for (String columnName : columnNames) {
      final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
      if (mappedColumns.contains(upperColumnName)) {
        mappedColumnNames.add(upperColumnName);
      } else {
        unmappedColumnNames.add(columnName);
      }
    }
    mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
    unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
  }

接下来分析一下 MyBatis 是如何将结果集中的数据填充到已映射的实体类字段中的
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject,ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {

    // 获取映射的列名
    final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
    boolean foundValues = false;
    // 获取 ResultMapping
    final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
    for (ResultMapping propertyMapping : propertyMappings) {
        // 拼接列名前缀,得到完整列名
        String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
        if (propertyMapping.getNestedResultMapId() != null) {
            column = null;
        }
        /*
         * 下面的 if 分支由三个或条件组合而成,三个条件的含义如下:
         *   条件一:检测 column 是否为 {prop1=col1, prop2=col2} 形式,该
         *          种形式的 column 一般用于关联查询
         *   条件二:检测当前列名是否被包含在已映射的列名集合中,若包含则可进行数据集映射操作
         *   条件三:多结果集相关,暂不分析
         */
        if (propertyMapping.isCompositeResult()
            || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
            || propertyMapping.getResultSet() != null) {

            // 从结果集中获取指定列的数据
            Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);

            final String property = propertyMapping.getProperty();
            if (property == null) {
                continue;

            // 若获取到的值为 DEFERED,则延迟加载该值
            } else if (value == DEFERED) {
                foundValues = true;
                continue;
            }
            if (value != null) {
                foundValues = true;
            }
            if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property)
                                  .isPrimitive())) {

                // 将获取到的值设置到实体类对象中
                metaObject.setValue(property, value);
            }
        }
    }
    return foundValues;
}

private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping,ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {

    if (propertyMapping.getNestedQueryId() != null) {
        // 获取关联查询结果,下一节分析
        return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
    } else if (propertyMapping.getResultSet() != null) {
        addPendingChildRelation(rs, metaResultObject, propertyMapping);
        return DEFERED;
    } else {
        final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
        // 拼接前缀
        final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
        // 从 ResultSet 中获取指定列的值
        return typeHandler.getResult(rs, column);
    }
}

相关文章

网友评论

      本文标题:myabtis源码解析四(mybatis中statement对象

      本文链接:https://www.haomeiwen.com/subject/wvdymctx.html