花里胡哨系列之Mybatis源码---参数解析(五)

一.基本介绍

本节分析的主要内容是:Mybatis如何将传入的参数填充到Sql语句当中。举个例子:

映射文件:
<mapper namespace="org.apache.ibatis.mytest.MyDao">


 <insert id="insertAuthor" parameterType="org.apache.ibatis.domain.blog.Author">
  insert into Author (id,username,password,email,bio)
  values (#{id},#{username},#{password},#{email},#{bio})
 </insert>

  <select id="selectAll" parameterType="java.lang.String" resultType="java.util.HashMap">
     select * from Author where id = #{id} and username = #{username}
  </select>

  <update id="updateAuthorIfNecessary" parameterType="org.apache.ibatis.domain.blog.Author">
    update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
    where id=#{id}
  </update>

</mapper>

接口:

public interface MyDao {

  void insertAuthor(Author author);

  void updateAuthorIfNecessary(Author author);

  Map<String, Object> selectAll(@Param("id") String id, @Param("username")String username);
}

测试代码
@Test
public void update(){
  try (SqlSession session = sqlMapper.openSession()) {
    MyDao mapper = session.getMapper(MyDao.class);
    Author expected = new Author(500, "cbegin", "******", null, null, null);
    mapper.updateAuthorIfNecessary(expected);
  }
}

上述mapper.updateAuthorIfNecessary(expected) 最终会调用到映射文件中的 id为updateAuthorIfNecessary的update标签,

sql语句最终解析为:update Author set  username = ? ,password = ?, email = ? where id = ? 

看看如何将 Author 对象填充到sql语句中对于的参数位置。

二.准备阶段

     通过MyDao mapper = session.getMapper(MyDao.class) 这种方式最终得到的结果是一个 代理对象,MapperProxy

MapperProxy:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    } else if (method.isDefault()) {
      if (privateLookupInMethod == null) {//jdk1.8 接口默认方法
        return invokeDefaultMethodJava8(proxy, method, args);
      } else {
        return invokeDefaultMethodJava9(proxy, method, args);
      }
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
  // 从缓存中获取 MapperMethod 对象,若缓存未命中,则创建 MapperMethod 对象
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  // 调用 execute 方法执行 SQL
  return mapperMethod.execute(sqlSession, args);
}

MapperMethod:

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
   创建 SqlCommand 对象,该对象包含一些和 SQL 相关的信息
  this.command = new SqlCommand(config, mapperInterface, method);
  // 创建 MethodSignature 对象
  this.method = new MethodSignature(config, mapperInterface, method);
}

MethodSignature:

public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
  //获取返回类型
  Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
  if (resolvedReturnType instanceof Class<?>) {
    this.returnType = (Class<?>) resolvedReturnType;
  } else if (resolvedReturnType instanceof ParameterizedType) {
    this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
  } else {
    this.returnType = method.getReturnType();
  }
  this.returnsVoid = void.class.equals(this.returnType);
  this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
  this.returnsCursor = Cursor.class.equals(this.returnType);
  this.returnsOptional = Optional.class.equals(this.returnType);
  // 解析 @MapKey 注解,获取注解内容
  this.mapKey = getMapKey(method);
  this.returnsMap = this.mapKey != null;
  //解析RowBounds参数 位置
  this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
  //解析 ResultHandler 位置
  this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
  //解析参数列表,重点关注
  this.paramNameResolver = new ParamNameResolver(configuration, method);
}

ParamNameResolver:

public ParamNameResolver(Configuration config, Method method) {
  //获取参数类型列表
  final Class<?>[] paramTypes = method.getParameterTypes();
  //获取参数注解
  final Annotation[][] paramAnnotations = method.getParameterAnnotations();
  final SortedMap<Integer, String> map = new TreeMap<>();
  int paramCount = paramAnnotations.length;
  // get names from @Param annotations
  for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
    // 检测当前的参数类型是否为 RowBounds 或 ResultHandler
    if (isSpecialParameter(paramTypes[paramIndex])) {
      // skip special parameters
      continue;
    }
    String name = null;
    for (Annotation annotation : paramAnnotations[paramIndex]) {
      if (annotation instanceof Param) {
        hasParamAnnotation = true;
        // 获取 @Param 注解内容
        name = ((Param) annotation).value();
        break;
      }
    }
    if (name == null) {
      // @Param was not specified.
      // 检测是否设置了 useActualParamName 全局配置,默认开启
      if (config.isUseActualParamName()) {
        // 通过反射获取参数名称。此种方式要求 JDK 版本为 1.8+,
        // 且要求编译时加入 -parameters 参数,否则获取到的参数名
        // 仍然是 arg0, arg1, ..., argN
        name = getActualParamName(method, paramIndex);
      }
      if (name == null) {
        // use the parameter index as the name ("0", "1", ...)
        // gcode issue #71
        /*
         * 使用 map.size() 返回值作为名称,思考一下为什么不这样写:
         * name = String.valueOf(paramIndex);
         * 因为如果参数列表中包含 RowBounds 或 ResultHandler,这两个
         * 参数会被忽略掉,这样将导致名称不连续。
         *
         * 比如参数列表 (int p1, int p2, RowBounds rb, int p3)
         * - 期望得到名称列表为 ["0", "1", "2"]
         * - 实际得到名称列表为 ["0", "1", "3"]
         */
        name = String.valueOf(map.size());
      }
    }
    //参数下标 -- 》 参数名,后续根据该下标获取真正的参数对象
    map.put(paramIndex, name);
  }
  //转成sortedMap,只读
  names = Collections.unmodifiableSortedMap(map);
}

      可以看到ParamNameResolver里面保存了 调用方法 参数索引位置和参数名的映射。

      如: void  getInfoById(String id), 没有 @Param注解,

      映射关系为:0 -- > arg0

             void getInfoById(@Param("id") String id)

      映射关系为:0 -- > id

      获取到了方法参数索引和参数名称的映射关系,接下来要结合具体入参值,获取到参数名称 --->参数值  的映射关系了

MapperMethod:
public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  switch (command.getType()) {
    case INSERT: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
      break;
    }
    case UPDATE: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
      break;
    }
    case DELETE: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
      break;
    }
    case SELECT:
      if (method.returnsVoid() && method.hasResultHandler()) {
        // 如果方法返回值为 void,但参数列表中包含 ResultHandler,表明
        // 使用者想通过 ResultHandler 的方式获取查询结果,而非通过返回值
        // 获取结果
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        // 执行查询操作,并返回多个结果
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        // 执行查询操作,并将结果封装在 Map 中返回
        result = executeForMap(sqlSession, args);
      } else if (method.returnsCursor()) {
        // 执行查询操作,并返回一个 Cursor 对象
        result = executeForCursor(sqlSession, args);
      } else {
        // 执行查询操作,并返回一个结果
        // 获取参数名对应的参数值 映射关系
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
        if (method.returnsOptional()
            && (result == null || !method.getReturnType().equals(result.getClass()))) {
          result = Optional.ofNullable(result);
        }
      }
      break;
    case FLUSH:
      // 执行刷新操作
      result = sqlSession.flushStatements();
      break;
    default:
      throw new BindingException("Unknown execution method for: " + command.getName());
  }
  // 如果方法的返回值为基本类型,而返回值却为 null,此种情况下应抛出异常
  if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
    throw new BindingException("Mapper method '" + command.getName()
        + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
  }
  return result;
}
public Object convertArgsToSqlCommandParam(Object[] args) {
  return paramNameResolver.getNamedParams(args);
}
ParamNameResolver:
public Object getNamedParams(Object[] args) {
  final int paramCount = names.size();
  if (args == null || paramCount == 0) {
    return null;
  } else if (!hasParamAnnotation && paramCount == 1) {
    /*
     * 如果方法参数列表无 @Param 注解,且仅有一个非特别参数,则返回该
     * 参数的值。比如如下方法:
     * List findList(RowBounds rb, String name)
     * names 如下:
     * names = {1 : "arg1"}
     * 此种情况下,返回 args[names.firstKey()],即 args[1]
     */
    return args[names.firstKey()];
  } else {
    final Map<String, Object> param = new ParamMap<>();
    int i = 0;
    for (Map.Entry<Integer, String> entry : names.entrySet()) {
      // 添加 <参数名, 参数值> 键值对到 param 中
      param.put(entry.getValue(), args[entry.getKey()]);
      // add generic param names (param1, param2, ...)
      final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
      // ensure not to overwrite parameter named with @Param
      // 检测 names 中是否包含 genericParamName,什么情况下会包含?
      // 答案如下:
      // 使用者显式将参数名称配置为 param1,即 @Param("param1")
      if (!names.containsValue(genericParamName)) {
        param.put(genericParamName, args[entry.getKey()]);
      }
      i++;
    }
    return param;
  }
}

     在ParamNameResolver的getNamedParams,传入Object[] args,也就是入参,将会根据 names,即参数索引 -- 参数名 映射关系,创建 参数名--- 参数值 映射关系 (一个参数,且无@Param注解除外)

     举例:void getInfoById(String id)     一个参数,且无Param注解

       参数索引 -- 参数名: 0 ---> arg0

       不创造参数名--- 参数值 的映射关系,直接返回 id值

               void getInfoById(@Param("id") String id, @Param("age") String age ) 

       参数索引 -- 参数名: 0 ---> id

                                         1 ---> age

       参数名--- 参数值        id ----> agrs[0]

                                         age -----> args[1]

三. 解析sql中的参数

      构造好了  参数名--- >参数值 映射关系后,需要将其填充到 sql语句中,即 select * from a where id = #{id}, 将 id替换成具体值。

       以<update>标签执行sql过程为例

DefaultSqlSession:
public int update(String statement, Object parameter) {
  try {
    //parameter 是已经封装过的入参
    dirty = true;
    MappedStatement ms = configuration.getMappedStatement(statement);
    return executor.update(ms, wrapCollection(parameter));
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}
BaseExecutor:
public int update(MappedStatement ms, Object parameter) throws SQLException {
  ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  clearLocalCache();//清除缓存
  //模板方法
  return doUpdate(ms, parameter);
}
SimpleExecutor:
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
  Statement stmt = null;
  try {
    Configuration configuration = ms.getConfiguration();
    //会解析 sql,将 #{a} 替换成 ? 
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
    //获取连接 ,填充参数
    stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.update(stmt);
  } finally {
    closeStatement(stmt);
  }
}
Configuration:
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  //创建具有路由功能StatementHandler
  StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
  //将插件应用在StatementHandler
  statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
  return statementHandler;
}

RoutingStatementHandler:

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

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

}

PreparedStatementHandler:

public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
BaseStatementHandler:
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
    //获取 KeyGenerator,执行 processBefore逻辑
    generateKeys(parameterObject);
    //根据传入参数解析动态 sql
    boundSql = mappedStatement.getBoundSql(parameterObject);
  }

  this.boundSql = boundSql;

  this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
  this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}

      重点关注  mappedStatement.getBoundSql(parameterObject), 根据入参解析动态sql

MappedStatement:
public BoundSql getBoundSql(Object parameterObject) {
  //如果是DynamicSqlSource,需要处理?,拼接字符串, 对于RawSqlSource,StaticSqlSource
  //在 mapper.xml文件解析阶段已经解析好了
  BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  if (parameterMappings == null || parameterMappings.isEmpty()) {
    // 创建新的 BoundSql,这里的 parameterMap 是 ParameterMap 类型。
    // 由<ParameterMap> 节点进行配置,该节点已经废弃,不推荐使用。
    // 默认情况下,parameterMap.getParameterMappings() 返回空集合
    boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
  }

  // check for nested result maps in parameter mappings (issue #30)
  for (ParameterMapping pm : boundSql.getParameterMappings()) {
    String rmId = pm.getResultMapId();
    if (rmId != null) {
      ResultMap rm = configuration.getResultMap(rmId);
      if (rm != null) {
        hasNestedResultMaps |= rm.hasNestedResultMaps();
      }
    }
  }

  return boundSql;
}

以动态sql为例:DynamicSqlSource

@Override
public BoundSql getBoundSql(Object parameterObject) {
  DynamicContext context = new DynamicContext(configuration, parameterObject);
  // 解析 SQL 片段,并将解析结果存储到 DynamicContext 中,会将条件和传入参数结合,如test的判断条件
  rootSqlNode.apply(context);
  SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
  Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
  // 构建 StaticSqlSource,在此过程中将 sql 语句中的占位符 #{} 替换为问号 ?,
  // 并为每个占位符构建相应的 ParameterMapping
  SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
  BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
  // 将 DynamicContext 的 ContextMap 中的内容拷贝到 BoundSql 中,additionalParameter,额外参数
  context.getBindings().forEach(boundSql::setAdditionalParameter);
  return boundSql;
}
SqlSourceBuilder:
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
  ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
  GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
  String sql = parser.parse(originalSql);//将 #{}替换成?,并且构造parameterMappings,入参结构,有序
  return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}

GenericTokenParser :

public String parse(String text) {
  if (text == null || text.isEmpty()) {
    return "";
  }
  // search open token
  int start = text.indexOf(openToken);
  if (start == -1) {
    return text;
  }
  char[] src = text.toCharArray();
  int offset = 0;
  final StringBuilder builder = new StringBuilder();
  StringBuilder expression = null;
  while (start > -1) {
    if (start > 0 && src[start - 1] == '\\') {
      // this open token is escaped. remove the backslash and continue.
      builder.append(src, offset, start - offset - 1).append(openToken);
      offset = start + openToken.length();
    } else {
      // found open token. let's search close token.
      if (expression == null) {
        expression = new StringBuilder();
      } else {
        expression.setLength(0);
      }
      builder.append(src, offset, start - offset);
      offset = start + openToken.length();
      int end = text.indexOf(closeToken, offset);
      while (end > -1) {
        if (end > offset && src[end - 1] == '\\') {
          // this close token is escaped. remove the backslash and continue.
          expression.append(src, offset, end - offset - 1).append(closeToken);
          offset = end + closeToken.length();
          end = text.indexOf(closeToken, offset);
        } else {//获取真实表达式, 如${abc} ,expression = "abc"
          expression.append(src, offset, end - offset);
          break;
        }
      }
      if (end == -1) {
        // close token was not found.
        builder.append(src, start, src.length - start);
        offset = src.length;
      } else {//这一步将表达式 转为?,或者真实值,看具体实现类,同时保存 参数相关信息
        builder.append(handler.handleToken(expression.toString()));
        offset = end + closeToken.length();
      }
    }
    start = text.indexOf(openToken, offset);
  }
  if (offset < src.length) {
    builder.append(src, offset, src.length - offset);
  }
  return builder.toString();
}
ParameterMappingTokenHandler:
@Override
public String handleToken(String content) {
  //根据 表达式构造ParameterMapping, 
  parameterMappings.add(buildParameterMapping(content));
  return "?";
}
/*
 * 将 #{xxx} 占位符中的内容解析成 ParameterMapping,举例说明一下。如下:
 *
 * #{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
 *
 * 上面占位符中的内容最终会被解析成如下的结果:
 *
 * {
 * "property": "age",
 * "typeHandler": "MyTypeHandler",
 * "jdbcType": "NUMERIC",
 * "javaType": "int"
 * }
 *
 */
private ParameterMapping buildParameterMapping(String content) {
  Map<String, String> propertiesMap = parseParameterMapping(content);
  String property = propertiesMap.get("property");
  Class<?> propertyType;
  if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params
    propertyType = metaParameters.getGetterType(property);
  } else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
    propertyType = parameterType;
  } else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
    propertyType = java.sql.ResultSet.class;
  } else if (property == null || Map.class.isAssignableFrom(parameterType)) {
    propertyType = Object.class;
  } else {
    // 代码逻辑走到此分支中,表明 parameterType 是一个自定义的类,
    // 比如 Article,此时为该类创建一个元信息对象
    MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
    if (metaClass.hasGetter(property)) {
      propertyType = metaClass.getGetterType(property);
    } else {
      propertyType = Object.class;
    }
  }
  ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
  // 将 propertyType 赋值给 javaType
  Class<?> javaType = propertyType;
  String typeHandlerAlias = null;
  for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
    String name = entry.getKey();
    String value = entry.getValue();
    if ("javaType".equals(name)) {
      // 如果用户明确配置了 javaType,则以用户的配置为准
      javaType = resolveClass(value);
      builder.javaType(javaType);
    } else if ("jdbcType".equals(name)) {
      builder.jdbcType(resolveJdbcType(value));
    } else if ("mode".equals(name)) {
      builder.mode(resolveParameterMode(value));
    } else if ("numericScale".equals(name)) {
      builder.numericScale(Integer.valueOf(value));
    } else if ("resultMap".equals(name)) {
      builder.resultMapId(value);
    } else if ("typeHandler".equals(name)) {
      typeHandlerAlias = value;
    } else if ("jdbcTypeName".equals(name)) {
      builder.jdbcTypeName(value);
    } else if ("property".equals(name)) {
      // Do Nothing
    } else if ("expression".equals(name)) {
      throw new BuilderException("Expression based parameters are not supported yet");
    } else {
      throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}.  Valid properties are " + PARAMETER_PROPERTIES);
    }
  }
  if (typeHandlerAlias != null) {
    builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
  }
  return builder.build();
}

      看看 ParameterMapping 的字段

private Configuration configuration;
//参数名称
private String property;
private ParameterMode mode;
private Class<?> javaType = Object.class;
private JdbcType jdbcType;
private Integer numericScale;
private TypeHandler<?> typeHandler;
private String resultMapId;
private String jdbcTypeName;
private String expression;

    最重要的是 property,代表 #{user}中的 user, 后续根据 property 从之前 参数名称和参数值映射关系集合 获取具体值。

四.填充sql参数

  SimpleExecutor:

public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
  Statement stmt = null;
  try {
    Configuration configuration = ms.getConfiguration();
    //会解析 sql,将 #{a} 替换成 ?
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
    //填充参数
    stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.update(stmt);
  } finally {
    closeStatement(stmt);
  }
}
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;
}
PreparedStatementHandler:
public void parameterize(Statement statement) throws SQLException {
  parameterHandler.setParameters((PreparedStatement) statement);
}
DefaultParameterHandler:
public void setParameters(PreparedStatement ps) {
  ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
  // 从 BoundSql 中获取 ParameterMapping 列表,每个 ParameterMapping
  // 与原始 SQL 中的 #{xxx} 占位符一一对应
  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) {//输入参数,ParameterMode.IN
        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())) {
          // 若运行时参数的类型有相应的类型处理器 TypeHandler,则将
          // parameterObject 设为当前属性的值。
          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 = JdbcType.OTHER
          jdbcType = configuration.getJdbcTypeForNull();
        }
        try {
          //参数位置有序
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        } catch (TypeException | SQLException e) {
          throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
        }
      }
    }
  }
}

 看看    value = metaObject.getValue(propertyName);

 

public Object getValue(String name) {
  PropertyTokenizer prop = new PropertyTokenizer(name);
  if (prop.hasNext()) {
    //获取 #{user.id}中 user的值
    MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
    if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
      return null;
    } else {
      //获取 user中的 id值,id属性是 user的children
      return metaValue.getValue(prop.getChildren());
    }
  } else {//直接获取属性值, 通过反射 执行 get方法 或者 直接用 map.get(),看包装对象objectWrapper的类型
    return objectWrapper.get(prop);
  }
}

        通过遍历 ParameterMapping ,取 propertyName, 在之前 构造好的传入参数中寻找对应的值,最后通过  TypeHandler 设置到PreparedStatement 中

 BaseTypeHandler:

public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
  if (parameter == null) {
    if (jdbcType == null) {
      throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
    }
    try {
      ps.setNull(i, jdbcType.TYPE_CODE);
    } catch (SQLException e) {
      throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "
            + "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "
            + "Cause: " + e, e);
    }
  } else {
    try {
      setNonNullParameter(ps, i, parameter, jdbcType);
    } catch (Exception e) {
      throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . "
            + "Try setting a different JdbcType for this parameter or a different configuration property. "
            + "Cause: " + e, e);
    }
  }
}
CharacterTypeHandler:
public void setNonNullParameter(PreparedStatement ps, int i, Character parameter, JdbcType jdbcType) throws SQLException {
  ps.setString(i, parameter.toString());
}

     参数填充到此结束,其中有很多细节没有细讲,但是流程已经给出来了,可以按照这个流程自行调试。

四.总结

     参数填充,其实就是将输入参数整理好,将 sql中引用的参数整理好,然后整合到sql中,其中sql的解析过程,各种动态节点的解析结合入参需要自己多调试。下一章准备分析Mybatis的缓存机制,如一级缓存,二级缓存。

     结语:守得云开见月明!

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值