Mybatis框架中的foreach标签解析
如果是foreachNode则会预填充很多的参数及SQL语句以及展开的参数信息。foreach标签的注册:
当出现foreach标签的时候则调用对应的处理函数:
private void initNodeHandlerMap() {
nodeHandlerMap.put("if", new IfHandler());
nodeHandlerMap.put("foreach", new ForeachHandler());
}
处理函数初始化foreach节点配置属性:
@Override
public void handleNode(XNode var1, List<SqlNode> var2) {
MixedSqlNode mixedSqlNode = new MixedSqlNode(parseDynamicTags(var1));
String collection = var1.getStringAttribute("collection");
String item = var1.getStringAttribute("item");
String index = var1.getStringAttribute("index");
String open = var1.getStringAttribute("open");
String close = var1.getStringAttribute("close");
String separator = var1.getStringAttribute("separator");
ForEachSqlNode foreachSqlNode = new ForEachSqlNode(XMLScriptBuilder.this.configuration,mixedSqlNode, collection, index, item, open, close, separator);
var2.add(foreachSqlNode);
}
这样就完成foreach标签的初始化,当系统执行的时候就需要组装SQL语句信息,这里还是apply方法,其中参数为传入的上下文信息。执行的原理就是:
- 通过ognl计算器获取遍历的集合对象;
- 根据open,close配置属性在sql中填充值;
- 在解析参数过程中最重要的就是对填充的每个item如何赋值?这里通过将配置的index和item都传入到DynamicContext中,这里的DynamicContext作用类似于符号表,用于保存执行上下文的变量配置信息,其中item生成的临时变量格式为: "__frch_" + item + "_" + I,index值为map中的key或者遍历的次数。
- 系统通过这些上下文最终执行foreach标签中的对象,产生sql语句,执行完成后移除这些临时生成的上下文信息。
在第三步中,为了方便将产生的临时变量替换为用户定义的值,这里进行了替换:
public void appendSql(String sql) {
GenericTokenParser parser = new GenericTokenParser("#{", "}", (content) -> {
String newContent = content.replaceFirst("^\\s*" + this.item + "(?![^.,:\\s])", ForEachSqlNode.itemizeItem(this.item, this.index));
if (this.itemIndex != null && newContent.equals(content)) {
newContent = content.replaceFirst("^\\s*" + this.itemIndex + "(?![^.,:\\s])", ForEachSqlNode.itemizeItem(this.itemIndex, this.index));
}
return "#{" + newContent + "}";
});
this.delegate.appendSql(parser.parse(sql));
}
因为用户上下文(符号表)更新了,所以这里相应进行了处理。
@Override
public boolean apply(DynamicContext context) {
Map<String, Object> bindings = context.bindingsAsMap();
Iterable<?> iterable = ForEachSqlNode.this.evaluator.evaluateIterable(collectionExpression, bindings);
if(!iterable.iterator().hasNext()) {
return true;
} else {
boolean first = true;
this.applyOpen(context);
int i = 0;
for(Object o:iterable) {
if(!first && this.separator != null) {
context = new PrefixedContext(context, this.separator);
} else {
context = new PrefixedContext(context, "");
}
int uniqueNumber = context.getUniqueNumber();
if(o instanceof Map.Entry) {
Map.Entry<Object, Object> entry = (Map.Entry)o;
//这里将index和item都添加到符号表中
this.applyIndex(context, entry.getKey(), uniqueNumber);
this.applyItem(context, entry.getValue(), uniqueNumber);
} else {
//这里将index和item都添加到符号表中
this.applyIndex(context, i, uniqueNumber);
this.applyItem(context, o, uniqueNumber);
}
this.contents.apply(new FilteredDynamicContext(this.configuration, context, this.index, this.item, uniqueNumber));
if(first) {
first = !((PrefixedContext)context).isPrefixApplied();
}
context = ((PrefixedContext) context).delegate;
++i;
}
this.applyClose(context);
context.getBindings().remove(this.item);
context.getBindings().remove(this.index);
return true;
}
}