前面了解到mybatis解析完resultMap标签的内容后,对sql进行解析,来看一下sql的解析过程。
private void sqlElement(List list) throws Exception {
if (configuration.getDatabaseId() != null) {
sqlElement(list, configuration.getDatabaseId());
}
sqlElement(list, null);
}
private void sqlElement(List list, String requiredDatabaseId) throws Exception {
for (XNode context : list) {
String databaseId = context.getStringAttribute("databaseId");
String id = context.getStringAttribute("id");
//命名空间 + sql标签中的id
id = builderAssistant.applyCurrentNamespace(id, false);
//解析数据库id
if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
//将sql节点的内容添加到自己属性的Map中
sqlFragments.put(id, context);
}
}
}
解析节点的过程分为,判断数据库id是否匹配,获取相对应属性,替换
MappedStatement对象的作用,为sql语句绑定参数,执行sql语句,将结果集映射成对象
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
//数据库id进行比对是否一致
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
//获取insert update delete select 标签中其他的属性值 代码略
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
//递归将标签变成sql语句,删除sql标签,并且将${}这类的参数赋上真实值
includeParser.applyIncludes(context.getNode());
//判断主键自动生成器是使用哪种,
//mysql,sql server是可以自增的,oracler是使用序列
processSelectKeyNodes(id, parameterTypeClass, langDriver);
//SqlSource其实就是sql语句,这里将对sql语句进行解析,如果是动态的则使用动态的解析
//静态的则直接赋真实值,删除标签
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
String resultSets = context.getStringAttribute("resultSets");
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
//命名空间 + 增删改查标签的id
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
//如果有,则直接取出这个主键自增生成器
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
//没有,则根据数据库的类型,进行判断使用哪种主键自增生成器
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
//将MappedStatement添加到Configuration类的属性中
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
// 创建SqlSource过程,先创建XMLScriptBuilder
public SqlSource parseScriptNode() {
//判断是否含有动态的sql,占位符,动态sql相关节点
MixedSqlNode rootSqlNode = parseDynamicTags(context);
SqlSource sqlSource = null;
//判断是否是动态sql语句
if (isDynamic) {
//创建sqlSource
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
protected MixedSqlNode parseDynamicTags(XNode node) {
List contents = new ArrayList();
NodeList children = node.getNode().getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
//创建XNode节点,这一步将${}能变成常量的都变成常量,所以 ${}这种会有sql注入的问题
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("");
TextSqlNode textSqlNode = new TextSqlNode(data);
//还存在${}这种,那么就是动态sql了
if (textSqlNode.isDynamic()) {
contents.add(textSqlNode);
isDynamic = true;
} else {
contents.add(new StaticTextSqlNode(data));
}
}
//...
}
return new MixedSqlNode(contents);
}
上述代码结束后,MappedStatement对象最终会放入Configuration类的一个map中,id就是MappedStatement对象的id,MappedStatement对象的id是什么?进一步查看会发现是命名空间 + 增删改查标签的id,也就是说,如果你的两个增删改查的方法名字是一样的,但是参数类型不一样,对于java来说,这是多态,mybatis来说,没有多态,有一个方法会被覆盖。
public MappedStatement addMappedStatement(
String id,
SqlSource sqlSource,
StatementType statementType,
SqlCommandType sqlCommandType,
Integer fetchSize,
Integer timeout,
String parameterMap,
Class> parameterType,
String resultMap,
Class> resultType,
ResultSetType resultSetType,
boolean flushCache,
boolean useCache,
boolean resultOrdered,
KeyGenerator keyGenerator,
String keyProperty,
String keyColumn,
String databaseId,
LanguageDriver lang,
String resultSets) {
if (unresolvedCacheRef) {
throw new IncompleteElementException("Cache-ref not yet resolved");
}
//将id变成命名空间 + id
id = applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
//...略
configuration.addMappedStatement(statement);
return statement;
}
mybatis的mapper文件的sql解析部分大致就完成了,mybatis先进行数据库id进行判断是否正确,是否一致,然后解析sql语句,对sql语句进行解析,将