1.ForEachSqlNode
mybatis的foreach标签可以将列表、数组中的元素拼接起来,中间可以指定分隔符separator
<select id="getByUserId" resultMap="BaseMap"> select <include refid="BaseFields"></include> from user <where> user_id in <foreach collection="userIdList" item="userId" open="(" separator="," close=")"> #{userId} </foreach> </where> </select>
上面这段select sql代码使用了foreach标签,传入了一个userIdList的列表,首先会转化为一个ForeachSqlNode对象,经过处理后foreach标签里面的代码会解析成 (假设userIdList=[101,102,103])
(#{__frch_userId_0}, #{__frch_userId_1},#{__frch_userId_2})
, 后续预处理值替换后就会变成 (101,102,103)
下面是具体的ForEachSqlNode的解析过程源码:
public class ForEachSqlNode implements SqlNode { public static final String ITEM_PREFIX = "__frch_"; // 表达式值获取器 private final ExpressionEvaluator evaluator; // collection userIdList private final String collectionExpression; private final SqlNode contents; // open值 ( private final String open; // close值 ) private final String close; // separator分隔符值 , private final String separator; // item值 userId private final String item; // index值 null private final String index; // mybatis配置信息 private final Configuration configuration; public ForEachSqlNode(Configuration configuration, SqlNode contents, String collectionExpression, String index, String item, String open, String close, String separator) { this.evaluator = new ExpressionEvaluator(); this.collectionExpression = collectionExpression; this.contents = contents; this.open = open; this.close = close; this.separator = separator; this.index = index; this.item = item; this.configuration = configuration; } @Override public boolean apply(DynamicContext context) { Map<String, Object> bindings = context.getBindings(); // 迭代器 final Iterable<?> iterable = evaluator.evaluateIterable(collectionExpression, bindings); if (!iterable.iterator().hasNext()) { return true; } boolean first = true; // 添加open值 applyOpen(context); int i = 0; for (Object o : iterable) { DynamicContext oldContext = context; // 第一次循环first为true,使用new PrefixedContext(context, "")构建context,因为第一个元素之前不用添加分隔符 // 第一次循环完毕后first为false,使用new PrefixedContext(context, separator)构建,之后先添加分隔符再添加sql值 if (first || separator == null) { context = new PrefixedContext(context, ""); } else { context = new PrefixedContext(context, separator); }