面试高频:mybatis 的 $ 和 # 是有啥区别

279 篇文章 1 订阅
162 篇文章 1 订阅

写在前面:2020年面试必备的Java后端进阶面试题总结了一份复习指南在Github上,内容详细,图文并茂,有需要学习的朋友可以Star一下!
GitHub地址:https://github.com/abel-max/Java-Study-Note/tree/master

这个问题也是面试中常被问到的,就抽空来了解下这个,跳过一大段前面初始化的逻辑,对于一条 select * from t1 where id = #{id} 这样的 sql,在初始化扫描 mapper 的xml文件的时候会根据是否是 dynamic 来判断生成 DynamicSqlSource 还是 RawSqlSource,这里它是一条 RawSqlSource,

在这里做了替换,将 #{} 替换成了 ?

面试高频:mybatis 的 $ 和 # 是有啥区别

前面说的是否 dynamic 就是在这里进行判断

面试高频:mybatis 的 $ 和 # 是有啥区别

// org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseScriptNode
public SqlSource parseScriptNode() {    MixedSqlNode rootSqlNode = parseDynamicTags(context);    SqlSource sqlSource;    if (isDynamic) {
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);    } else {
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);    }    return sqlSource;
  }// org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseDynamicTagsprotected MixedSqlNode parseDynamicTags(XNode node) {    List<SqlNode> contents = new ArrayList<>();    NodeList children = node.getNode().getChildNodes();    for (int i = 0; i < children.getLength(); i++) {
      XNode child = node.newXNode(children.item(i));      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
        String data = child.getStringBody("");
        TextSqlNode textSqlNode = new TextSqlNode(data);        if (textSqlNode.isDynamic()) {
          contents.add(textSqlNode);          isDynamic = true;
        } else {
          contents.add(new StaticTextSqlNode(data));        }      } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
        String nodeName = child.getNode().getNodeName();        NodeHandler handler = nodeHandlerMap.get(nodeName);        if (handler == null) {
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
        }        handler.handleNode(child, contents);        isDynamic = true;
      }    }    return new MixedSqlNode(contents);
  }// org.apache.ibatis.scripting.xmltags.TextSqlNode#isDynamic  public boolean isDynamic() {    DynamicCheckerTokenParser checker = new DynamicCheckerTokenParser();    GenericTokenParser parser = createParser(checker);    parser.parse(text);    return checker.isDynamic();
  }    private GenericTokenParser createParser(TokenHandler handler) {    return new GenericTokenParser("${", "}", handler);
  }

可以看到其中一个条件就是是否有 ${} 这种占位符,假如说上面的 sql 换成 ${} ,那么可以看到它会在这里创建一个 dynamicSqlSource,

// org.apache.ibatis.scripting.xmltags.DynamicSqlSource
public class DynamicSqlSource implements SqlSource {  private final Configuration configuration;  private final SqlNode rootSqlNode;  public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {    this.configuration = configuration;    this.rootSqlNode = rootSqlNode;  }  @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;
  }
}

这里眼尖的同学可能就可以看出来了,RawSqlSource 在初始化的时候已经经过了 parse,把 #{} 替换成了 ? 占位符,但是 DynamicSqlSource 并没有

面试高频:mybatis 的 $ 和 # 是有啥区别

再看这个图,我们发现在这的时候还没有进行替换

然后往里跟

面试高频:mybatis 的 $ 和 # 是有啥区别

好像是这里了

面试高频:mybatis 的 $ 和 # 是有啥区别

这里 rootSqlNode.apply 其实是一个对原来 sql 的解析结果的一个循环调用,不同类型的标签会构成不同的 node,像这里就是一个 textSqlNode

面试高频:mybatis 的 $ 和 # 是有啥区别

可以发现到这我们的 sql 已经被替换了,而且是直接作为 string 类型替换的,所以可以明白了这个问题所在,就是注入,不过细心的同学发现其实这里是有个

面试高频:mybatis 的 $ 和 # 是有啥区别

理论上还是可以做过滤的,不过好像现在没用起来。

我们前面可以发现对于 #{} 是在启动扫描 mapper的 xml 文件就替换成了 ? ,然后是在什么时候变成实际的值的呢

面试高频:mybatis 的 $ 和 # 是有啥区别

发现到这的时候还是没有替换,其实说白了也就是 prepareStatement 那一套,

面试高频:mybatis 的 $ 和 # 是有啥区别

在这里进行替换,会拿到 org.apache.ibatis.mapping.ParameterMapping,然后进行替换,因为会带着类型信息,所以不用担心注入咯

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值