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

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

  • 相关阅读:
    SpringMVC
    Spring mvc 下Ajax获取JSON对象问题 406错误
    Docker国内镜像源
    获取redis cluster主从关系
    终端登录超时限制暂时解除
    vim全选,全部复制,全部删除
    [转]Redis集群搭建
    Jenkins持续集成01—Jenkins服务搭建和部署
    ELK重难点总结和整体优化配置
    ELK 经典用法—企业自定义日志收集切割和mysql模块
  • 原文地址:https://www.cnblogs.com/idcode/p/14551414.html
Copyright © 2020-2023  润新知