MyBatis源码分析

以下分析在mybatis3.5.4版本下

MyBatis对sql语句解析处理

1处理sql语句的类

org.apache.ibatis.scripting.xmltags.XMLLanguageDriver

1)处理xml语句映射方法

  @Override
  public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
    XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
    return builder.parseScriptNode();
  }

1.1) builder.parseScriptNode方法

 public SqlSource parseScriptNode() {
 	//获取sql语句带占位填充符的(如select * from user where id=#{id})
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
    SqlSource sqlSource;
    if (isDynamic) {
      //动态sql
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }

1.2 org.apache.ibatis.builder.SqlSourceBuilder类下的parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters)方法把sql语句中的#{id}占位填充符替换为?

public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
    ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
    GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
	/**
	* 替换前:select * from user where id = #{id} or id=#{pid}
	* 替换后:select * from user where id = ? or id = ?
	* 执行sql语句参数添加时处理在debug
	* 看org.apache.ibatis.binding.MapperMethod 的  public Object execute(SqlSession     sqlSession, Object[] args)方法
	* 具体解析在
	*/
    String sql = parser.parse(originalSql);
    return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
  }
  1. 处理注解中sql语句的方法
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
    // issue #3
    if (script.startsWith("<script>")) {
      XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver());
      return createSqlSource(configuration, parser.evalNode("/script"), parameterType);
    } else {
      // issue #127
      script = PropertyParser.parse(script, configuration.getVariables());
      TextSqlNode textSqlNode = new TextSqlNode(script);
      if (textSqlNode.isDynamic()) {
         //动态sql
        return new DynamicSqlSource(configuration, textSqlNode);
      } else {
         //原始sql
        return new RawSqlSource(configuration, script, parameterType);
      }
    }
  }

2 执行sql解析#{}占位符参数

org.apache.ibatis.reflection.ParamNameResolver 进行参数名称解析

  /*
  * 构造函数
  * @param:config 配置上下文
  * @param:method 方法 用来获取方法中的参数与@param注解
  */
  public ParamNameResolver(Configuration config, Method method) {
    //获取方法中所有参数
    final Class<?>[] paramTypes = method.getParameterTypes();
    //获取方法参数中所有注解
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
    //创建排序的map结构保证 参数与参数注解对应
    final SortedMap<Integer, String> map = new TreeMap<>();
    int paramCount = paramAnnotations.length;
    // get names from @Param annotations
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
      //如果是特殊参数跳过
      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("id") 获取后name=id
          name = ((Param) annotation).value();
          break;
        }
      }
      //如果没有@param按照实际参数名称
      //如 findById(Integer id,Integer pid); 获取name依次为 id,pid
      if (name == null) {
        // @Param was not specified.
        if (config.isUseActualParamName()) {
          name = getActualParamName(method, paramIndex);
        }
        if (name == null) {
          // use the parameter index as the name ("0", "1", ...)
          // gcode issue #71
          name = String.valueOf(map.size());
        }
      }
      map.put(paramIndex, name);
    }
    //添加到属性中以后使用
    names = Collections.unmodifiableSortedMap(map);

方法中所使用的names在构造函数时候已经初始化了

  public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    if (args == null || paramCount == 0) {
      return null;
      //如果没有@param注解  只有一个参数 直接返回第一个参数
    } else if (!hasParamAnnotation && paramCount == 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.put(entry.getValue(), args[entry.getKey()]);
        // add generic param names (param1, param2, ...)
        final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      /**
      * 最终获取的param为
      * pid:43
      * id:42
      * param1:42
      * param2:43
      */
      return param;
    }
  }

执行堆栈

执行堆栈

1.org.apache.ibatis.executor.SimpleExecutor类中的方法
1.1 doQuery方法中StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);把所有的封装进去
1.2 prepareStatement把封装的处理成sql的Statement

 @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.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
  }

如有错误欢迎指正,纯粹个人理解,大神勿喷,谢谢!

原文地址:https://www.cnblogs.com/idcode/p/14551414.html