mybatis标签解析
标签结构
我们在mapper的xml文件中,使用动态SQL,那么这些标签<where>、<if>、<set>、<ForEach>、<Choose>、<Trim>
等是怎么解析的呢?我们先看包的结构
包结构中,script -> xmltags下面的类便是这些动态标签对象,我们在看看这些动态的tag的关系,如下
这些动态标签都实现了SqlNode
接口
public interface SqlNode {boolean apply(DynamicContext context);
}
标签处理器
那么mybatis是如何解析这些动态标签的呢?
public class XMLScriptBuilder extends BaseBuilder {private final XNode context;private boolean isDynamic; //是否是动态sql, 根据占位符#{},${}来判断,有就是动态的private final Class<?> parameterType;private final Map<String, NodeHandler> nodeHandlerMap = new HashMap<>();public XMLScriptBuilder(Configuration configuration, XNode context, Class<?> parameterType) {super(configuration);this.context = context;this.parameterType = parameterType;//初始化的标签处理器 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());}
}
我们看看这些标签处理器,都实现了NodeHandler
接口
public class XMLScriptBuilder extends BaseBuilder {//内部类NodeHandler,提供了各种具体的标签Handlerprivate interface NodeHandler {void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);}private class BindHandler implements NodeHandler {}private class TrimHandler implements NodeHandler {}private class WhereHandler implements NodeHandler {}private class SetHandler implements NodeHandler {}private class ForEachHandler implements NodeHandler {}private class IfHandler implements NodeHandler {}private class OtherwiseHandler implements NodeHandler {}private class ChooseHandler implements NodeHandler {}
}
where
标签示例
我们用 where
标签示例,解析的时候根据xml中node的名称where
名称获取对应的Handler,也就是WhereHandler
private class WhereHandler implements NodeHandler {public WhereHandler() {// Prevent Synthetic Access}@Overridepublic void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);//解析where标签,mixedSqlNode 就是where标签里面的内容WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode);targetContents.add(where);}}
我们继续看下这个WhereSqlNode
是怎么处理的
public class WhereSqlNode extends TrimSqlNode {//前缀,目测是删除where条件后面的第一个运算法,where and a = 1 这种情况private static List<String> prefixList = Arrays.asList("AND ","OR ","AND\n", "OR\n", "AND\r", "OR\r", "AND\t", "OR\t");public WhereSqlNode(Configuration configuration, SqlNode contents) {super(configuration, contents, "WHERE", prefixList, null, null);}
}
我们看到是直接走父类TrimSqlNode
去了
public void applyAll() {sqlBuffer = new StringBuilder(sqlBuffer.toString().trim());String trimmedUppercaseSql = sqlBuffer.toString().toUpperCase(Locale.ENGLISH);if (trimmedUppercaseSql.length() > 0) {applyPrefix(sqlBuffer, trimmedUppercaseSql);applySuffix(sqlBuffer, trimmedUppercaseSql);}delegate.appendSql(sqlBuffer.toString());
}private void applyPrefix(StringBuilder sql, String trimmedUppercaseSql) {if (!prefixApplied) {prefixApplied = true;if (prefixesToOverride != null) {//遍历运算符,删除where前缀for (String toRemove : prefixesToOverride) {if (trimmedUppercaseSql.startsWith(toRemove)) {sql.delete(0, toRemove.trim().length());break;}}}if (prefix != null) {sql.insert(0, " ");//首位添加空格sql.insert(0, prefix);//然后添加where}}
}
到此解析完成,其他标签解析过程类似