简要
关于mybatis中的动态标签(常用的foreach、if、choose等),都会有对应的类去解析;SqlNode是顶级解析接口,各动态标签实现该接口的apply方法完成各自解析操作。foreach标签对应的解析实现类是foreachSqlNode.
foreach标签解析的过程就是对foreach标签中的各个属性(collection、index、item等)进行解析处理的过程,每个属性都有对应的解析处理逻辑.sql解析完成之后执行对应的查询或删除等数据操作,然后封装结果集.本文仅对foreach标签解析进行说明.
执行demo
dao接口:
List<News> findNews(@Param("ids") List<Integer> ids);
xml配置文件:
<select id="findNews" resultType="com.it.txm.demo.News">
select id,title from find_news
where id in
(
<foreach collection="ids" item="item" separator=",">
#{item}
</foreach>
)
</select>
测试demo:
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(777);
list1.add(797);
List<News> list = newsMapper.findNews(list1);
System.out.println(list);
执行流程分析
debug调试进入获取BoundSql(即解析sql)
MixedSqlNode可以认为是多个动态标签的组合处理者,会循环处理每个标签.源码:
public class MixedSqlNode implements SqlNode {
private final List<SqlNode> contents;
// 创建MixedSqlNode对象时会将实现sqlNode实现类集合添加到MixedSqlNode中
public MixedSqlNode(List<SqlNode> contents) {
this.contents = contents;
}
// MixedSqlNode对apply方法的实现处理就是执行对各个标签的apply实现逻辑.
@Override
public boolean apply(DynamicContext context) {
contents.forEach(node -> node.apply(context));
return true;
}
}
具体到demo中,mixedSqlNode
有两个sqlNode接口的实现类:
StaticTextSqlNode
、ForEachSqlNode
;
StaticTextSqlNode
实现apply做的处理是对sql的拼接,源码如下:
public class StaticTextSqlNode implements SqlNode {
private final String text;
public StaticTextSqlNode(String text) {
this.text = text;
}
// 动态上下文对象进行拼接sql
@Override
public boolean apply(DynamicContext context) {
context.appendSql(text);
return true;
}
// 最终是利用sqlBuilder进行参数拼接
public void appendSql(String sql) {
sqlBuilder.add(sql);
}
}
具体到案例中StaticTextSqlNode
实际解析的就是下面标注的两行sql:
下面主要说ForEachSqlNode
,源码如下:
public boolean apply(DynamicContext context) {
// 从动态上下文中获取映射绑定信息(这里只用到参数映射信息.)
Map<String, Object> bindings = context.getBindings();
// 解析foreach标签中的collection属性.返回迭代器类型参数,实际就是解析的collection标签中集合的具体参数值:777 797
final Iterable<?> iterable = evaluator.evaluateIterable(collectionExpression, bindings);
if (!iterable.iterator().hasNext()) {
return true;
}
boolean first = true;
// 解析foreach标签中的open属性.具体解析处理方式就是sql拼接,由于比较简单,这里不再展开.
applyOpen(context);
int i = 0;
// 遍历迭代器中的实参.
for (Object o : iterable) {
DynamicContext oldContext = context{
// 此处处理foreach标签中的separator分隔符属性,new PrefixedContext()实际上执行的是DynamicContext中设置分割属性.如果 不为空则设置为foreach标签中实际使用的分割符为空则设置空字符串,执行demo中使用的是逗号.
if (first || separator == null) {
context = new PrefixedContext(context, "");
} else {
context = new PrefixedContext(context, separator);
}
int uniqueNumber = context.getUniqueNumber();
// Issue #709
if (o instanceof Map.Entry) {
@SuppressWarnings("unchecked")
Map.Entry<Object, Object> mapEntry = (Map.Entry<Object, Object>) o;
applyIndex(context, mapEntry.getKey(), uniqueNumber);
applyItem(context, mapEntry.getValue(), uniqueNumber);
} else {
// applyIndex处理的是foreach标签中index属性,下面会展开说
applyIndex(context, i, uniqueNumber);
// applyItem主要作用是解析foreach标签中的item属性并进行赋值,下面会展开说明
applyItem(context, o, uniqueNumber);
}
// 将foreach标签中解析完成的单个参数进行拼接
contents.apply(new FilteredDynamicContext(configuration, context, index, item, uniqueNumber));
if (first) {
first = !((PrefixedContext) context).isPrefixApplied();
}
context = oldContext;
i++;
}
// 执行foreach标签中close属性对应处理,实际是执行参数拼接.
applyClose(context);
context.getBindings().remove(item);
context.getBindings().remove(index);
return true;
}
简单说一下DynamicContext,可以理解为是动态参数的上下文对象,里面map形式的方法参数、用于sql拼接的sqlBuilderd.
applyIndex主要作用是处理foreach标签中的index属性,按照map形式往DynamicContext动态上下文中存入对象信息,最终封装数:
context.bind中执行bindings.put(name, value);
private void applyIndex(DynamicContext context, Object o, int i) {
// 对于foreach标签中index属性设置的情况下执行以下逻辑.
if (index != null) {
// 按照index为key,实际参数o的形式组装map存入DynamicContext动态sql上下文中.
context.bind(index, o);
// itemizeItem作用参数拼装,foreach中解析之后的格式默认:__frch_ + item + "_" + i,然后按照key-value形式存入DynamicContext动态sql上下文中.
context.bind(itemizeItem(index, i), o);
}
}
applyItem执行原理同applyIndex,foreach标签中item属性不为空时执行:按照key-value形式往DynamicContext动态上下文中存入对象信息.
private void applyItem(DynamicContext context, Object o, int i) {
if (item != null) {
context.bind(item, o);
context.bind(itemizeItem(item, i), o);
}
}
执行完成之后DynamicContext中的sql拼接对象中组装的查询sql如下:
至此foreach标签解析分析完成.
欢迎小伙伴评论区留言,共同交流共同进步!
原文地址:https://blog.csdn.net/weixin_43401380/article/details/121585356
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。