mybatis逻辑标签
mybatis 有 9 大标签,如下图
我们只针对其中 select|insert|update|delete 的子标签部分进行探索
简介
首先要上一道菜,来介绍我们本节分析的内容
<select id="selectAll" resultType="com.aya.mapper.Blog" >
select * from blog
<if test="order=='id'">
ORDER BY id
</if>
</select>
本节以最简单的 if 子标签来作为例子,来理清 子标签的加载和执行流程
加载
选自 XMLScriptBuilder
protected MixedSqlNode parseDynamicTags(XNode node) {
List<SqlNode> contents = new ArrayList<SqlNode>();
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);
}
这段代码是mybatis
加载本例select
标签的核心部分
来源
xml 来源:
settings=>mapper=>解析mapper文件=>select 标签解析
代码来源:
new SqlSessionFactoryBuilder().build(inputStream);
工作
- 将子节点分为两个块,静态块和节点块
- 遍历每个块,这里分为静态块和节点块两种处理方式
静态块
select * from blog
节点块
<if test="order=='id'">
ORDER BY id
</if>
静态块处理
最终变成:
contents.add(new StaticTextSqlNode(“select * from blog”));
节点块处理
handler.handleNode(child, contents);
这里就是比较重要的地方了,对于动态块,mybatis 是如何处理的呢?
我们这里nodeName是if ,那么就相当于 nodeHandlerMap.get(“if”).handleNode(child, contents);
接下来就去找到 nodeHandlerMap 是如何初始化的吧!!
在XMLScriptBuilder的构造中调用了函数 initNodeHandlerMap ,初始化所有条件节点的处理方式
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());
}
按照本例,我们去 IfHandler.handlerNode 去看看做了什么
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
String test = nodeToHandle.getStringAttribute("test");
IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
targetContents.add(ifSqlNode);
}
- parseDynamicTags 解析内容
- 将 IfSqlNode 设置内容和条件
- 添加 IfSqlNode
加载小结
这里得出结论,在加载过程中,添加了一个静态块,一个动态块
1. contents.add(new StaticTextSqlNode(data));
2. contents.add(new IfSqlNode(mixedSqlNode, test));
执行
选自 MixedSqlNode, 遍历 SqlNode 拼接为 sql 语句
@Override
public boolean apply(DynamicContext context) {
for (SqlNode sqlNode : contents) {
sqlNode.apply(context);
}
return true;
}
来源
代码来源:
sqlSession.selectList("com.aya.mapper.BlogMapper.selectAll", new HashMap<String,String>(){
{
put("order","id");
}
});
工作
- 执行 StaticTextSqlNode.apply, sql 语句为:
select * from blog
- 执行 IfSqlNode.apply 成功,执行子节点块 StaticTextSqlNode.apply
select * from blog ORDER BY id
此时,sql 被动态构建完成,接下来由执行器去执行
总结
本文以一个简单的例子来阐述 子标签
的加载和执行.
- 在 settings 加载时加载为 XXHandler ,然后将 XXHandler 转换为 XXSqlNode,添加到content里面
- 在 SqlSession 执行的时候,执行 XXSqlNode 的 apply
标签处理器表:
标签 | 处理器 | 节点 |
trim | TrimHandler | TrimSqlNode |
where | WhereHandler | WhereSqlNode extends TrimSqlNode |
set | SetHandler | SetSqlNode extends TrimSqlNode |
foreach | ForEachHandler | ForEachSqlNode |
if | IfHandler | IfSqlNode |
choose | ChooseHandler | ChooseSqlNode |
when | IfHandler | IfSqlNode |
otherwise | OtherwiseHandler | MixedSqlNode |
bind | BindHandler | VarDeclSqlNode |