写在前面: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);
}
可以看到其中一个条件就是是否有 {} ,那么可以看到它会在这里创建一个 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,然后进行替换,因为会带着类型信息,所以不用担心注入咯
网友评论