Mybatis源码的理解一之:解读动态sql

我们的项目中通常写很多sql,而这种屡见不鲜的占位符却有很多人不知道它底层的实现,最近和一个朋友聊到这事,一时兴起,所以就撸了一会源码,希望借助我的理解,助你也搞清楚这块的知识!

废话不多说,直接上测试用例:

很简单的一串代码,但是内部逻辑是有点复杂

@Test
    public void testMybatis(){
        final Map<String,String> mapper = new HashMap<String, String>();
        mapper.put("author", "威威");
        mapper.put("code", "8888");

        //初始化一个处理程序
        TokenHandler handler = content -> mapper.get(content);
        GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
        System.out.println("看看这是啥:  " + parser.parse("作者:#{author},您的工号是:#{code}"));
    }

根据上图可以看得出,替换赋值是是用map中的value,而占位符里面的正是map中的key,这里我们要稍微思考下,究竟是怎么解析出我的key?

mybatis中有动态Sql源类:DynamicSqlSource

 @Override
  public BoundSql getBoundSql(Object parameterObject) {
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    rootSqlNode.apply(context);
    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);
    context.getBindings().forEach(boundSql::setAdditionalParameter);
    return boundSql;
  }

,跟进去里面的parse

  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;
    if (configuration.isShrinkWhitespacesInSql()) {
      sql = parser.parse(removeExtraWhitespaces(originalSql));
    } else {
      sql = parser.parse(originalSql);
    }
    return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
  }

 这里开始解析占位符#{ }

下面直接看真正解析的代码:

public String parse(String text) {
    if (text == null || text.isEmpty()) {
      return "";
    }
    // 寻找#{的索引位置
    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;
    do {
      if (start > 0 && src[start - 1] == '\\') {
        // 这个#{被转义。删除并继续
        builder.append(src, offset, start - offset - 1).append(openToken);
        offset = start + openToken.length();
      } else {
        // 接着搜索 }
        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] == '\\') {
            // 这个}被转义,删除并继续
            expression.append(src, offset, end - offset - 1).append(closeToken);
            offset = end + closeToken.length();
            end = text.indexOf(closeToken, offset);
          } else {
            expression.append(src, offset, end - offset);
            break;
          }
        }
        if (end == -1) {
          // 没有找到 }
          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);
    } while (start > -1);
    if (offset < src.length) {
      builder.append(src, offset, src.length - offset);
    }
    return builder.toString();
  }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值