Mybatis系列七:XMLStatementBuilder创建MappedStatement

Mybatis系列七:XMLStatementBuilder创建MappedStatement

概述

《Mybatis系列四:XMLMapperBuilder解析XL配置文件》一文【1.3.6 解析select|insert|update|delete元素】章节中,解析select|insert|update|delete元素时XMLStatementBuilder创建MappedStatement。
解析流程大致如下:
(1) 获取SQL节点各属性值
(2) 使用XMLIncludeTransformer解析include节点
(3) 解析selectKey节点,创建MappedStatement实例,保存到Configuration#mappedStatements中;创建SelectKeyGenerator,保存到Configuration#keyGenerators中
(4) 默认使用XMLLanguageDriver解析SQL节点得到SqlSource,使用各种类型的NodeHandler、SqlNode解析SQL节点。SqlSource有两种:
DynamicSqlSource SQL语句有${param0}(文本替换,可能是一段SQL)或者使用了if/where(运行时决定是否拼接SQL片段)等节点需要运行时才能解析出要执行的SQL。
RawSqlSource 使用了#{param1}占位符或者没有使用,就是一个完整SQL,不需要运行时解析得到SQL。
(5) 获取KeyGenerator,解析selectKey节点(如果有)时已经注册,如果没有默认INSERT语句使用
Jdbc3KeyGenerator.INSTANCE或NoKeyGenerator.INSTANCE,取决于是否使用key生成器以及当前是否时INSERT语句。
(6) 创建MappedStatement,保存到Configuration#mappedStatements中

一、XMLStatementBuilder

1.1 是否匹配当前databaseId

// Configuration配置了databaseId,statement(select|insert|update|delete)没指定databaseId则解析,指定了databaseId但是不匹配忽略。
// Configuration未配置databaseId,statement(select|insert|update|delete)指定了databaseId则忽略。
private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
if (requiredDatabaseId != null) {
if (!requiredDatabaseId.equals(databaseId)) {
return false;
}
} else {
if (databaseId != null) {
return false;
}
// 如果前一个语句的databaseId不为null,则跳过此语句
id = builderAssistant.applyCurrentNamespace(id, false);
if (this.configuration.hasStatement(id, false)) {
MappedStatement previous = this.configuration.getMappedStatement(id, false); // issue #2
if (previous.getDatabaseId() != null) {
return false;
}
}
}
return true;
}

1.2 解析select|insert|update|delete元素 parseStatementNode

select a.id, **** ** ** ** ** ** ** ** ** as resource_type from table_name a **** ** select uuid()** **** insert into table_name (id, name, email, phone) values (#{id},#{name},#{email},#{phone})

public void parseStatementNode() {
String id = context.getStringAttribute(“id”);
String databaseId = context.getStringAttribute(“databaseId”);

if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}

Integer fetchSize = context.getIntAttribute(“fetchSize”);
Integer timeout = context.getIntAttribute(“timeout”);
String parameterMap = context.getStringAttribute(“parameterMap”);
String parameterType = context.getStringAttribute(“parameterType”);
Class<?> parameterTypeClass = resolveClass(parameterType);
String resultMap = context.getStringAttribute(“resultMap”);
String resultType = context.getStringAttribute(“resultType”);
String lang = context.getStringAttribute(“lang”);
// 默认是XMLLanguageDriver
LanguageDriver langDriver = getLanguageDriver(lang);

Class<?> resultTypeClass = resolveClass(resultType);
String resultSetType = context.getStringAttribute(“resultSetType”);
StatementType statementType = StatementType.valueOf(context.getStringAttribute(“statementType”, StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

// 根据节点名称得到SqlCommandType
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));

// SELECT语句默认不刷新缓存使用缓存,其它操作默认刷新缓存不用缓存
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute(“flushCache”, !isSelect);
boolean useCache = context.getBooleanAttribute(“useCache”, isSelect);
boolean resultOrdered = context.getBooleanAttribute(“resultOrdered”, false);

// XMLIncludeTransformer解析
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());

// 解析include节点后,再解析selectKey节点,解析完成后移除节点
processSelectKeyNodes(id, parameterTypeClass, langDriver);

// 解析SQL(之前:已解析并删除了和)
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
String resultSets = context.getStringAttribute(“resultSets”);
String keyProperty = context.getStringAttribute(“keyProperty”);
String keyColumn = context.getStringAttribute(“keyColumn”);

// 获取KeyGenerator,解析selectKey节点(如果有)时已经注册,如果没有默认INSERT语句使用
Jdbc3KeyGenerator.INSTANCE或NoKeyGenerator.INSTANCE,取决于是否使用key生成器以及当前是否时INSERT语句。
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
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#mappedStatements中
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}

1.3 处理selectKey节点

private void processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver) {
List selectKeyNodes = context.evalNodes(“selectKey”);
if (configuration.getDatabaseId() != null) {
parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, configuration.getDatabaseId());
}
parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, null);
** removeSelectKeyNodes(selectKeyNodes);**
}

1.4 解析selectKey节点

private void parseSelectKeyNodes(String parentId, List list, Class<?> parameterTypeClass, LanguageDriver langDriver, String skRequiredDatabaseId) {
for (XNode nodeToHandle : list) {
String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
String databaseId = nodeToHandle.getStringAttribute(“databaseId”);
if (databaseIdMatchesCurrent(id, databaseId, skRequiredDatabaseId)) {
parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, langDriver, databaseId);
}
}
}

1.5 解析selectKey节点

private void parseSelectKeyNode(String id, XNode nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver, String databaseId) {
String resultType = nodeToHandle.getStringAttribute(“resultType”);
Class<?> resultTypeClass = resolveClass(resultType);
StatementType statementType = StatementType.valueOf(nodeToHandle.getStringAttribute(“statementType”, StatementType.PREPARED.toString()));
String keyProperty = nodeToHandle.getStringAttribute(“keyProperty”);
String keyColumn = nodeToHandle.getStringAttribute(“keyColumn”);
// 执行时机
boolean executeBefore = “BEFORE”.equals(nodeToHandle.getStringAttribute(“order”, “AFTER”));

//defaults
boolean useCache = false;
boolean resultOrdered = false;
KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
Integer fetchSize = null;
Integer timeout = null;
boolean flushCache = false;
String parameterMap = null;
String resultMap = null;
ResultSetType resultSetTypeEnum = null;

SqlSource sqlSource = langDriver.createSqlSource(configuration, nodeToHandle, parameterTypeClass);
SqlCommandType sqlCommandType = SqlCommandType.SELECT;

builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, null);

id = builderAssistant.applyCurrentNamespace(id, false);

// 创建MappedStatement实例,保存到Configuration#mappedStatements中
MappedStatement keyStatement = configuration.getMappedStatement(id, false);
// 解析selectKey元素封装为SelectKeyGenerator,保存到Configuration#keyGenerators中
configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore));
}

1.6 移除selectKey节点

private void removeSelectKeyNodes(List selectKeyNodes) {
for (XNode nodeToHandle : selectKeyNodes) {
nodeToHandle.getParent().getNode().removeChild(nodeToHandle.getNode());
}
}

二、解析include元素 XMLIncludeTransformer

2.1 解析include元素

public void applyIncludes(Node source) {
Properties variablesContext = new Properties();
Properties configurationVariables = configuration.getVariables();
if (configurationVariables != null) {
variablesContext.putAll(configurationVariables);
}
applyIncludes(source, variablesContext, false);
}

2.2 递归解析include元素

private void applyIncludes(Node source, final Properties variablesContext, boolean included) {
if (source.getNodeName().equals(“include”)) {
// 获取引用的SQL片段
Node toInclude = findSqlFragment(getStringAttribute(source, “refid”), variablesContext);
// 获取include的子元素property
Properties toIncludeContext = getVariablesContext(source, variablesContext);
applyIncludes(toInclude, toIncludeContext, true);
if (toInclude.getOwnerDocument() != source.getOwnerDocument()) {
toInclude = source.getOwnerDocument().importNode(toInclude, true);
}
source.getParentNode().replaceChild(toInclude, source);
while (toInclude.hasChildNodes()) {
toInclude.getParentNode().insertBefore(toInclude.getFirstChild(), toInclude);
}
toInclude.getParentNode().removeChild(toInclude);
} else if (source.getNodeType() == Node.ELEMENT_NODE) {
if (included && !variablesContext.isEmpty()) {
// replace variables in attribute values
NamedNodeMap attributes = source.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node attr = attributes.item(i);
attr.setNodeValue(PropertyParser.parse(attr.getNodeValue(), variablesContext));
}
}
NodeList children = source.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
applyIncludes(children.item(i), variablesContext, included);
}
} else if (included && source.getNodeType() == Node.TEXT_NODE
&& !variablesContext.isEmpty()) {
// 替换占位符
source.setNodeValue(PropertyParser.parse(source.getNodeValue(), variablesContext));
}
}

2.3 查找SQL片段

private Node findSqlFragment(String refid, Properties variables) {
// includ元素refid属性可能有占位符
refid = PropertyParser.parse(refid, variables);
// 使用当前命名空间的SQL片段需添加命名空间前缀,可能使用了其它命名空间或者当前命名空间的SQL片段
refid = builderAssistant.applyCurrentNamespace(refid, true);
try {
XNode nodeToInclude = configuration.getSqlFragments().get(refid);
return nodeToInclude.getNode().cloneNode(true);
} catch (IllegalArgumentException e) {
throw new IncompleteElementException(“Could not find SQL statement to include with refid '” + refid + “’”, e);
}
}

2.4 include子节点属性值替换占位符

private Properties getVariablesContext(Node node, Properties inheritedVariablesContext) {
Map<String, String> declaredProperties = null;
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node n = children.item(i);
if (n.getNodeType() == Node.ELEMENT_NODE) {
String name = getStringAttribute(n, “name”);
// value属性值可能使用了占位符
String value = PropertyParser.parse(getStringAttribute(n, “value”), inheritedVariablesContext);
if (declaredProperties == null) {
declaredProperties = new HashMap<String, String>();
}

   //  重复属性定义抛异常
   if (declaredProperties.put(name, value) != null) {
     throw new BuilderException("Variable " + name + " defined twice in the same include definition");
   }
 }

}
if (declaredProperties == null) {
return inheritedVariablesContext;
} else {
Properties newProperties = new Properties();
newProperties.putAll(inheritedVariablesContext);
newProperties.putAll(declaredProperties);
return newProperties;
}
}

三、SQL语句XML脚本语言 XMLLanguageDriver

3.1 创建SqlSource

public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
return builder.parseScriptNode();
}

四、解析SQL语句 XMLScriptBuilder

4.1 构造器

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());
}

4.2 解析SQL语句

public SqlSource parseScriptNode() {
MixedSqlNode rootSqlNode = parseDynamicTags(context);
SqlSource sqlSource = null;
// SQL语句有${param0}(文本替换,可能是一段SQL)或者使用了if/where(动态拼接SQL)等节点需要运行时才能解析出完整SQL
if (isDynamic) {
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
// 使用了#{param1}占位符或者没有使用,就是一个完整SQL,不需要运行时解析
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}

4.3 解析动态标签

protected MixedSqlNode parseDynamicTags(XNode node) {
List 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("");
TextSqlNode textSqlNode = new TextSqlNode(data);
// SQL语句中使用了${param0},直接文本替换
if (textSqlNode.isDynamic()) {
contents.add(textSqlNode);
isDynamic = true;
} else {
contents.add(new StaticTextSqlNode(data));
}
} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
String nodeName = child.getNode().getNodeName();
// 使用4.1初始化时注册的节点处理器处理节点
NodeHandler handler = nodeHandlerMap.get(nodeName);
if (handler == null) {
throw new BuilderException(“Unknown element <” + nodeName + “> in SQL statement.”);
}
handler.handleNode(child, contents);
isDynamic = true;
}
}
return new MixedSqlNode(contents);
}

4.4 SQL节点处理器 NodeHandler

4.4.1 绑定变量 BindHandler

使用**《MyBatis动态sql之bind标签》**中的例子

**** select * from user where uname like #{paran_uname}

private class BindHandler implements NodeHandler {
public BindHandler() {
// Prevent Synthetic Access
}

@Override
public void handleNode(XNode nodeToHandle, List targetContents) {
final String name = nodeToHandle.getStringAttribute(“name”);
final String expression = nodeToHandle.getStringAttribute(“value”);
final VarDeclSqlNode node = new VarDeclSqlNode(name, expression);
targetContents.add(node);
}
}

4.4.2 添加删除前后缀 TrimHandler

private class TrimHandler implements NodeHandler {
public TrimHandler() {
// Prevent Synthetic Access
}

@Override
public void handleNode(XNode nodeToHandle, List targetContents) {
// 解析
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
String prefix = nodeToHandle.getStringAttribute(“prefix”);
String prefixOverrides = nodeToHandle.getStringAttribute(“prefixOverrides”);
String suffix = nodeToHandle.getStringAttribute(“suffix”);
String suffixOverrides = nodeToHandle.getStringAttribute(“suffixOverrides”);
TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
targetContents.add(trim);
}
}

4.4.3 WhereHandler

WhereSqlNode扩展TrimSqlNode, 默认添加where前缀,默认移除"AND ","OR ",“AND\n”, “OR\n”, “AND\r”, “OR\r”, “AND\t”, "OR\t"前缀。
private class WhereHandler implements NodeHandler {
public WhereHandler() {
// Prevent Synthetic Access
}

@Override
public void handleNode(XNode nodeToHandle, List targetContents) {
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode);
targetContents.add(where);
}
}

4.4.4 SetHandler

SetSqlNode扩展TrimSqlNode, 默认添加set前缀,默认移除","后缀。
private class SetHandler implements NodeHandler {
public SetHandler() {
// Prevent Synthetic Access
}

@Override
public void handleNode(XNode nodeToHandle, List targetContents) {
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
SetSqlNode set = new SetSqlNode(configuration, mixedSqlNode);
targetContents.add(set);
}
}

4.4.5 ForEachHandler

private class ForEachHandler implements NodeHandler {
public ForEachHandler() {
// Prevent Synthetic Access
}

@Override
public void handleNode(XNode nodeToHandle, List targetContents) {
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
String collection = nodeToHandle.getStringAttribute(“collection”);
String item = nodeToHandle.getStringAttribute(“item”);
String index = nodeToHandle.getStringAttribute(“index”);
String open = nodeToHandle.getStringAttribute(“open”);
String close = nodeToHandle.getStringAttribute(“close”);
String separator = nodeToHandle.getStringAttribute(“separator”);
ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, mixedSqlNode, collection, index, item, open, close, separator);
targetContents.add(forEachSqlNode);
}
}

4.4.6 IfHandler

private class IfHandler implements NodeHandler {
public IfHandler() {
// Prevent Synthetic Access
}

@Override
public void handleNode(XNode nodeToHandle, List targetContents) {
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
String test = nodeToHandle.getStringAttribute(“test”);
IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
targetContents.add(ifSqlNode);
}
}

4.4.7 OtherwiseHandler

private class OtherwiseHandler implements NodeHandler {
public OtherwiseHandler() {
// Prevent Synthetic Access
}

@Override
public void handleNode(XNode nodeToHandle, List targetContents) {
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
targetContents.add(mixedSqlNode);
}
}

4.4.8 ChooseHandler

private class ChooseHandler implements NodeHandler {
public ChooseHandler() {
// Prevent Synthetic Access
}

@Override
public void handleNode(XNode nodeToHandle, List targetContents) {
List whenSqlNodes = new ArrayList();
List otherwiseSqlNodes = new ArrayList();
// 解析出when、otherwis SQL节点
handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes);
// 获取otherwis SQL节点,只能有一个
SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes);
ChooseSqlNode chooseSqlNode = new ChooseSqlNode(whenSqlNodes, defaultSqlNode);
targetContents.add(chooseSqlNode);
}

private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List ifSqlNodes, List defaultSqlNodes) {
List children = chooseSqlNode.getChildren();
for (XNode child : children) {
String nodeName = child.getNode().getNodeName();
NodeHandler handler = nodeHandlerMap.get(nodeName);
if (handler instanceof IfHandler) {
handler.handleNode(child, ifSqlNodes);
} else if (handler instanceof OtherwiseHandler) {
handler.handleNode(child, defaultSqlNodes);
}
}
}

// 获取otherwis SQL节点,只能有一个
private SqlNode getDefaultSqlNode(List defaultSqlNodes) {
SqlNode defaultSqlNode = null;
if (defaultSqlNodes.size() == 1) {
defaultSqlNode = defaultSqlNodes.get(0);
} else if (defaultSqlNodes.size() > 1) {
throw new BuilderException(“Too many default (otherwise) elements in choose statement.”);
}
return defaultSqlNode;
}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值