概述
我们在mapper.xml中写的这种sql并不能执行,这就要求mybatis将我们编写的sql进行解析,变成符合w3c标准的sql,让statement可以执行,这种脚本解析很多场景都会使用,比如我们工作中使用的流程编排,规则解析,freemark框架等都会存在这样的技术
总体架构
- BoundSql :这个类我们不陌生,在前面分析的时候经常遇到,这个类中提供了satement 运行sql所需要的所有数据
- SqlSource 非常简单的一个接口,就一个获取BoudSql方法
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;
}
SqlNode 语法树解析
脚本组成
eg:
<select id="getSqlNode" resultType="com.wfg.entity.ActivityEntity">
select id , name from activity
<where>
<if test="list != null">
and id in
<foreach collection="list" separator="," close=")" open="(" item="item" index="index">
#{item}
</foreach>
<if test="name != null">
and name = #{name}
</if>
</if>
</where>
</select>
源代码:
org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseDynamicTags
protected 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));
}
//动态结点 eg:<if> <where> 等等
} 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);
}
每个点点等处理器中handleNode 方法会调用parseDynamicTags 形成一个递归操作;
解析流程图:
private void initNodeHandlerMap() {
nodeHandlerMap.put("trim", new TrimHandler());
nodeHandlerMap.put("where", new WhereHandler());
nodeHandlerMap.put("set", new SetHandler());
nodeHandlerMap.put("foreach", new ForEachHandler());
nodeHandlerMap.put("if", new IfHandler());
nodeHandlerMap.put("choose", new ChooseHandler());
nodeHandlerMap.put("when", new IfHandler());
nodeHandlerMap.put("otherwise", new OtherwiseHandler());
nodeHandlerMap.put("bind", new BindHandler());
}
提前初始化一些处理器;