mybatis源码分析(3)-sql参数绑定,结果集映射

mybatis执行sql前需要对参数进行绑定并对sql中#{}占位符进行替换,sql执行之后需要对结果集进行映射成JavaBean。

sql参数绑定

分析

User user = userDao.selectUserByName(username);

@Select("select * from user where 1=1 and name = #{name} limit 1")
User selectUserDoByName(@Param("name")String name);

private final SortedMap params;
 public Object convertArgsToSqlCommandParam(Object[] args) {
      final int paramCount = params.size();
      //参数为空时
      if (args == null || paramCount == 0) {
        return null;
      }
     //当没有@Param注解并且参数为1个时 
     else if (!hasNamedParameters && paramCount == 1) {
        return args[params.keySet().iterator().next().intValue()];
      }
     else {
       /**
       * 入参args[0]为"test",params的key,value分别为0,"name",这样param
       * 就能把"name","test"对应上
       */
        final Map param = new ParamMap();
        int i = 0;
        //其中params是SortedMap类型
        for (Map.Entry entry : params.entrySet()) {
          param.put(entry.getValue(), args[entry.getKey().intValue()]);
          final String genericParamName = "param" + String.valueOf(i + 1);
          if (!param.containsKey(genericParamName)) {
            param.put(genericParamName, args[entry.getKey()]);
          }
          i++;
        }
        return param;
      }
    }

@Select("select id, create_date as createDate, modify_date as modifyDate, name, password, salt from user where 1=1 and name = #{name} limit 1")
UserDo selectUserDoByName(String test, String name);

此sql会报错,因为这种写法参数绑定时没有"name"的参数。可以加个@Param注解,或者将#{name}改成#{1}或#{param2}

,DefaultSqlSession中调用executor的wrapCollection方法

 public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

private Object wrapCollection(final Object object) {
    if (object instanceof Collection) {
      StrictMap map = new StrictMap();
      map.put("collection", object);
      if (object instanceof List) {
        map.put("list", object);
      }
      return map;
    } else if (object != null && object.getClass().isArray()) {
      StrictMap map = new StrictMap();
      map.put("array", object);
      return map;
    }
    return object;
  }


MapperParamMap和StrictMap都继承了HashMap,只是将super.containsKey(key)为false的时候抛出了一个异常

SqlSourceBuilder.parse方法中,GenericTokenParser的parse方法将#{xx}替换为 ?,然后构造一个StaticSqlSource。

mybatis初始化时调用SqlSourceBuilder.parse

 public SqlSource parse(String originalSql, Class parameterType, Map additionalParameters) {
    ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
    GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
    String sql = parser.parse(originalSql);
    return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
  }

在进行sql查询之前,会对#{}参数进行替换。SimpleExecutor的doQuery方法。

 public  List 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);
      //sql执行之前进行#参数替换
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection);
    handler.parameterize(stmt);
    return stmt;
  }

PreparedStatementHandler.parameterize对sql#参数替换

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

DefaultParameterHandler.setParameters对sql中?占位符进行替换

public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List 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();
          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.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);
          }
        }
      }
    }

拿出核心代码,\color{red}{此代码表示取出来的, 如果Object是JavaBean或者复杂对象则通过反射取值}">

MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);

复杂对象时,

public Object getValue(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      //获取复杂对象
      MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
      if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
        return null;
      } else {
        return metaValue.getValue(prop.getChildren());
      }
    } else {
      return objectWrapper.get(prop);
    }
  }

返回结构集映射

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

    final List multipleResults = new ArrayList();

    int resultSetCount = 0;
    /** 调用getFirstResultSet方法获取ResultSet,同时获取数据库的      
    * MetaData数据,包括数据表列名、列的类型、类序号等,这些信       
    * 息都存储在ResultSetWrapper类中了
    */
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List 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.getResulSets();
    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);
  }

调用handleResultSet方法来来进行结果集的封装

结果集映射中间流程

  • getRowValue方法中的applyAutomaticMappings()方法是自动映射功能,getRowValue方法中的applyPropertyMappings()方法是人工干预设置的处理,比如加了typeHandler之类的
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;
      }
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
      foundValues = lazyLoader.size() > 0 || foundValues;
      rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;
    }
    return rowValue;
  }

private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
    List autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
    boolean foundValues = false;
    if (autoMapping.size() > 0) {
      //几个列名调用几次
      for (UnMappedColumAutoMapping mapping : autoMapping) {
        //获取当前列对应的值
        final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
        // issue #377, call setter on nulls
        if (value != null || configuration.isCallSettersOnNulls()) {
          if (value != null || !mapping.primitive) {
            metaObject.setValue(mapping.property, value);
          }
          foundValues = true;
        }
      }
    }
    return foundValues;
  }

private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
      throws SQLException {
    final List mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
    boolean foundValues = false;
    final List propertyMappings = resultMap.getPropertyResultMappings();
    for (ResultMapping propertyMapping : propertyMappings) {
      String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      if (propertyMapping.getNestedResultMapId() != null) {
        // the user added a column attribute to a nested result map, ignore it
        column = null;
      }
      if (propertyMapping.isCompositeResult()
          || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
          || propertyMapping.getResultSet() != null) {
        Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
        // issue #541 make property optional
        final String property = propertyMapping.getProperty();
        if (property == null) {
          continue;
        } else if (value == DEFERED) {
          foundValues = true;
          continue;
        }
        if (value != null) {
          foundValues = true;
        }
        if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
          // gcode issue #377, call setter on nulls (value is not 'found')
          metaObject.setValue(property, value);
        }
      }
    }
    return foundValues;
  }
  • 如果用注解的方式开发,比如在查询上面加,这样就可以对列进行映射,走的是上面的applyPropertyMappings流程
@Results(@Result(column = "id", property = "id", jdbcType = JdbcType.BIGINT, javaType = String.class))
@Select(.....)

 public void setValue(String name, Object value) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
      if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
        if (value == null && prop.getChildren() != null) {
          // don't instantiate child path if value is null
          return;
        } else {
          metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
        }
      }
      metaValue.setValue(prop.getChildren(), value);
    }
//调用objectWrapper.set方法,核心
else {
      objectWrapper.set(prop, value);
    }
  }

objectWrapper类三个实现类


objectWrapper类三个实现类

如果是BeanWrapper则,最后会走

//调用set方法
public void set(PropertyTokenizer prop, Object value) {
    if (prop.getIndex() != null) {
      Object collection = resolveCollection(prop, object);
      setCollectionValue(prop, collection, value);
    } else {
      setBeanProperty(prop, object, value);
    }
  }

//调用setBeanProperty方法
private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
    try {
      Invoker method = metaClass.getSetInvoker(prop.getName());
      Object[] params = {value};
      try {
        method.invoke(object, params);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    } catch (Throwable t) {
      throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t);
    }
  }

最终通过反射设置结果集。

  1. Mybatis 源码分析(2)—— 参数处理
  2. MyBatis源码分析-SQL语句执行的完整流程

你可能感兴趣的:(mybatis源码分析(3)-sql参数绑定,结果集映射)