Mybatis源码系列文章
Mybatis源码解析(四):sql语句及#{}、${}的解析
Mybatis源码解析(五):SqlSession会话的创建
...
// *******创建SqlSource,解析SQL,封装SQL语句(未参数绑定)和入参信息
// 问题:sql占位符如何进行的替换?动态sql如何进行的解析?
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
...
public interface SqlSource {
BoundSql getBoundSql(Object parameterObject);
}
进入createSqlSource创建方法
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
// 初始化了动态SQL标签处理器
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
// 解析动态SQL
return builder.parseScriptNode();
}
进入XMLScriptBuilder对象构造方法
public XMLScriptBuilder(Configuration configuration, XNode context, Class<?> parameterType) {
super(configuration);
this.context = context;
this.parameterType = parameterType;
// 初始化动态SQL中的节点处理器集合
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());
}
public SqlSource parseScriptNode() {
// ****将带有${}号的SQL信息封装到TextSqlNode
// ****将带有#{}号的SQL信息封装到StaticTextSqlNode
// ****将动态SQL标签中的SQL信息分别封装到不同的SqlNode中
MixedSqlNode rootSqlNode = parseDynamicTags(context);
SqlSource sqlSource;
// 如果SQL中包含${}和动态SQL语句,则将SqlNode封装到DynamicSqlSource
if (isDynamic) {
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
// 如果SQL中包含#{},则将SqlNode封装到RawSqlSource中,并指定parameterType
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
parseDynamicTags方法流程图,下面代码结合着流程图看
进入parseDynamicTags解析动态标签方法
protected MixedSqlNode parseDynamicTags(XNode node) {
List<SqlNode> contents = new ArrayList<>();
//获取
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("");
// 将文本内容封装到SqlNode中
TextSqlNode textSqlNode = new TextSqlNode(data);
// SQL语句中带有${}的话,就表示是dynamic的
if (textSqlNode.isDynamic()) {
contents.add(textSqlNode);
isDynamic = true;
} else {
// SQL语句中(除了${}和下面的动态SQL标签),就表示是static的
// StaticTextSqlNode的apply只是进行字符串的追加操作
contents.add(new StaticTextSqlNode(data));
}
//处理元素节点
} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) {
String nodeName = child.getNode().getNodeName();
// 动态SQL标签处理器
NodeHandler handler = nodeHandlerMap.get(nodeName);
if (handler == null) {
throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
}
handler.handleNode(child, contents);
// 动态SQL标签是dynamic的
isDynamic = true;
}
}
return new MixedSqlNode(contents);
}
进入RawSqlSource构造方法
public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
//先调用 getSql(configuration, rootSqlNode)获取sql,再走下面的构造函数
this(configuration, getSql(configuration, rootSqlNode), parameterType);
}
public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
// 解析SQL语句
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
// 获取入参类型
Class<?> clazz = parameterType == null ? Object.class : parameterType;
// 开始解析
sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
}
进入parse解析方法
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
// 创建分词解析器
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
String sql;
if (configuration.isShrinkWhitespacesInSql()) {
sql = parser.parse(removeExtraWhitespaces(originalSql));
} else {
// 解析#{}
sql = parser.parse(originalSql);
}
// 将解析之后的SQL信息,封装到StaticSqlSource对象中
// SQL字符串是带有?号的字符串,?相关的参数信息,封装到ParameterMapping集合中
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}
1)parser.parse解析
builder.append(handler.handleToken(expression.toString()));
public String handleToken(String content) {
parameterMappings.add(buildParameterMapping(content));
return "?";
}
2)查看StaticSqlSource类
查看DynamicSqlSource类