Mybatis源码解析之--谈谈${}

一百度${}, #{} 网上有好多答案,不过还是自己跟过源码才能理解原理

一 概述

  其实${} 没什么特别的,也可以从入参为POJO里获取属性值进行替换。需要注意的是它通过ognl表达式把POJO做变更,然后从里面获取属性值,进行纯粹的字符串拼接。

  所以需要注意,比如一张表有一个列是VARCHAR,如果在POJO里 setName("name") 是不够的,应该是setName("'name'"),加上单引号。

  还有就是$ #是可以同时用的。我们就来看看源码吧

二 源码分析

  现在我们有如下的sql语句

  <insert id="insertDollar" parameterType="me.gacl.domain.User" >
    insert into t_user
    values (${userId}, #{userName}, ${userBirthday}, 
      ${userSalary})
  </insert>

  如果代码这样写是没有问题的

        User user = (User) ac.getBean("user");
        user.setUserId("'11'");
        user.setUserName("白虎神皇xdp");
        user.setUserBirthday("'123'");
        user.setUserSalary(10000D);
        user.setTableName("t_user");
        userService.insertDollar(user);

  首先要说明的是,如果一个sql里包含了$,mybatis在解析的时候是当作  DynamicSqlSource 的

  之前分析StatementHandler的时候我们知道,执行前要根据入参确定最终的sql语句也就是获取boundSql

  我们看  DynamicSqlSource.getBoundSql 

public BoundSql getBoundSql(Object parameterObject) {
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    rootSqlNode.apply(context);//经过了apply之后 insert into t_user values ('11', #{userName}, '123', 10000.0)
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
      boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
    }
    return boundSql;
  }

继续分析apply,对于$,每一个sqlNode都是  TextSqlNode 

@Override
  public boolean apply(DynamicContext context) {
    GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter));
    context.appendSql(parser.parse(text));
    return true;
  }
  
  private GenericTokenParser createParser(TokenHandler handler) {
    return new GenericTokenParser("${", "}", handler);
  }

  parse方法之前在分析#的时候,是分析过的,就是根据输入的开始和结束符进行字符串的解析,很纯粹的字符串操作,这里面要注意的是 handler.handleToken(content) 

public String parse(String text) {
    StringBuilder builder = new StringBuilder();
    if (text != null && text.length() > 0) {
      char[] src = text.toCharArray();
      int offset = 0;
      int start = text.indexOf(openToken, offset);
      while (start > -1) {
        if (start > 0 && src[start - 1] == '\\') {
          // the variable is escaped. remove the backslash.
          builder.append(src, offset, start - offset - 1).append(openToken);
          offset = start + openToken.length();
        } else {
          int end = text.indexOf(closeToken, start);
          if (end == -1) {
            builder.append(src, offset, src.length - offset);
            offset = src.length;
          } else {
            builder.append(src, offset, start - offset);
            offset = start + openToken.length();
            String content = new String(src, offset, end - offset);
            builder.append(handler.handleToken(content));
            offset = end + closeToken.length();
          }
        }
        start = text.indexOf(openToken, offset);
      }
      if (offset < src.length) {
        builder.append(src, offset, src.length - offset);
      }
    }
    return builder.toString();
  }

   BindingTokenParser.handleToken 

@Override
    public String handleToken(String content) {
      Object parameter = context.getBindings().get("_parameter");
      if (parameter == null) {
        context.getBindings().put("value", null);
      } else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
        context.getBindings().put("value", parameter);
      }
      Object value = OgnlCache.getValue(content, context.getBindings());//通过ognl从对象里取值
      String srtValue = (value == null ? "" : String.valueOf(value)); // issue #274 return "" instead of "null"
      checkInjection(srtValue);
      return srtValue; //注意这里是String
    }

  到此关于$的源码就分析完了,特别注意# $是可以共存的,先解析$符号部分,然后再解析#部分。

posted on 2020-11-26 20:24  MaXianZhe  阅读(197)  评论(0编辑  收藏  举报

导航