Mybatis源码分析(三)
上回我们分析了Mybatis解析核心配置文件的代码,我们继续上次的分析,开始mapper映射文件的解析,话不多说咱们上代码!!!!!!
Mapper映射文件的前奏
上次我们说到调用mapperElement(root.evalNode("mappers"))
,解析核心配置文件中的“mappers”标签
/**
* 解析<mappers>标签
* @param parent mappers标签对应的XNode对象
* @throws Exception
*/
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
// 获取<mappers>标签的子标签
for (XNode child : parent.getChildren()) {
// <package>子标签
if ("package".equals(child.getName())) {
// 获取mapper接口和mapper映射文件对应的package包名
String mapperPackage = child.getStringAttribute("name");
// 将包下所有的mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂
configuration.addMappers(mapperPackage);
} else {// <mapper>子标签
// 获取<mapper>子标签的resource属性
String resource = child.getStringAttribute("resource");
// 获取<mapper>子标签的url属性
String url = child.getStringAttribute("url");
// 获取<mapper>子标签的class属性
String mapperClass = child.getStringAttribute("class");
// 它们是互斥的
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
// 专门用来解析mapper映射文件
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// 通过XMLMapperBuilder解析mapper映射文件
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
// 通过XMLMapperBuilder解析mapper映射文件
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
// 将指定mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
我们这里的Mybatis核心配置文件是通过package(包扫描的方式)配置mapper映射文件
<mappers>
<package name="com.li.mapper"/>
</mappers>
所以我们这里会走到这个if判断中;
在这里通过configuration的addMappers()方法将该包下的所有的mapper接口以及他的代理对象存储到一个map集合中,key为mapper接口类型,value为代理对象工厂;
调用configuration.addMappers(mapperPackage)
方法
// configuration 中的addMappers方法
public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
}
// MapperRegistry 的addMappers 方法
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
}
// 调用上面的方法addMappers()
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
// 根据package名称,加载该包下Mapper接口文件(不是映射文件)
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
// 获取加载的Mapper接口
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
for (Class<?> mapperClass : mapperSet) {
// 将Mapper接口添加到MapperRegistry中
addMapper(mapperClass);
}
}
这里将扫描包,加载接口类,将Mapper接口添加MapperRegistry中,调用MapperRegistry中addMapper方法;
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
// 如果Map集合中已经有该mapper接口的映射,就不需要再存储了
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 将mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂
knownMappers.put(type, new MapperProxyFactory<T>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
// 用来解析注解方式的mapper接口
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
// 解析注解方式的mapper接口
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
将mapper接口的class作为key,对应的代理工厂作为value保存在MapperRegistry中的knownMappers内;
创建MapperAnnotationBuilder ,顾名思义该类用来解析注解配置的Mapper接口,在解析注解配置的Mapper之前,也解析了package方式配置的mapper映射文件,调用MapperAnnotationBuilder的parse()方法;
public void parse() {
// 获取mapper接口的全路径
String resource = type.toString();
// 是否解析过该mapper接口
if (!configuration.isResourceLoaded(resource)) {
// 先解析mapper映射文件
loadXmlResource(); //做主要分析
// 设置解析标识
configuration.addLoadedResource(resource);
// Mapper构建者助手
assistant.setCurrentNamespace(type.getName());
// 解析CacheNamespace注解
parseCache();
// 解析CacheNamespaceRef注解
parseCacheRef();
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
// 每个mapper接口中的方法,都解析成MappedStatement对象
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
因为我们这里主要分析Mybatis映射文件方式。所以注解不做过多分析,我们来看loadXmlResource()
方法;
private void loadXmlResource() {
// Spring may not know the real resource name so we check a flag
// to prevent loading again a resource twice
// this flag is set at XMLMapperBuilder#bindMapperForNamespace
// 是否解析过该mapper映射文件
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
// 获取映射文件的全路径
String xmlResource = type.getName().replace('.', '/') + ".xml";
// #1347
InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
if (inputStream == null) {
// Search XML mapper that is not in the module but in the classpath.
try {
inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
} catch (IOException e2) {
// ignore, resource is not required
}
}
if (inputStream != null) {
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
xmlParser.parse();
}
}
}
获取到mapper映射文件的全路径后加载映射文件,创建XMLMapperBuilder类,调用parse()方法解析mapper映射文件;
开始解析Mapper映射文件
public void parse() {
// mapper映射文件是否已经加载过
if (!configuration.isResourceLoaded(resource)) {
// 从映射文件中的<mapper>根标签开始解析,直到完整的解析完毕
configurationElement(parser.evalNode("/mapper"));
// 标记已经解析
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
-
调用
configurationElement(parser.evalNode("/mapper"))
开始解析mapper映射文件/** * 解析映射文件 * @param context 映射文件根节点<mapper>对应的XNode */ private void configurationElement(XNode context) { try { // 获取<mapper>标签的namespace值,也就是命名空间 String namespace = context.getStringAttribute("namespace"); // 命名空间不能为空 if (namespace == null || namespace.equals("")) { throw new BuilderException("Mapper's namespace cannot be empty"); } // 设置当前的命名空间为namespace的值 builderAssistant.setCurrentNamespace(namespace); // 解析<cache-ref>子标签 cacheRefElement(context.evalNode("cache-ref")); // 解析<cache>子标签 cacheElement(context.evalNode("cache")); // 解析<parameterMap>子标签 parameterMapElement(context.evalNodes("/mapper/parameterMap")); // 解析<resultMap>子标签 resultMapElements(context.evalNodes("/mapper/resultMap")); // 解析<sql>子标签,也就是SQL片段 sqlElement(context.evalNodes("/mapper/sql")); // 解析<select>\<insert>\<update>\<delete>子标签 buildStatementFromContext(context.evalNodes("select|insert|update|delete")); //做主要分析 } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e); } }
在这个方法中我们可以看出解析mapper映射文件时和解析mybatis核心配置文件一样,也是一层一层的去解析,我们在这里是主要分析解析<select><insert><update><delete>子标签
-
解析<select><insert><update><delete>子标签
// private void buildStatementFromContext(List<XNode> list) { if (configuration.getDatabaseId() != null) { buildStatementFromContext(list, configuration.getDatabaseId()); } // 构建MappedStatement buildStatementFromContext(list, null); } private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) { for (XNode context : list) { // MappedStatement解析器 final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try { // 解析select等4个标签,创建MappedStatement对象 statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } }
这里创建了一个XMLStatementBuilder用来构建MappedStatement,在mybatis中一个MappedStatement就代表了一个<select><insert><update><delete>其中一个标签对应的SQL节点。
-
调用
statementParser.parseStatementNode()
开始解析<select><insert><update><delete>标签/** * 解析<select>\<insert>\<update>\<delete>子标签 */ public void parseStatementNode() { // 获取statement的id属性(特别关键的值) 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"); // 别名处理,获取入参对应的Java类型 Class<?> parameterTypeClass = resolveClass(parameterType); // 获取ResultMap String resultMap = context.getStringAttribute("resultMap"); // 获取结果映射类型 String resultType = context.getStringAttribute("resultType"); String lang = context.getStringAttribute("lang"); LanguageDriver langDriver = getLanguageDriver(lang); // 别名处理,获取返回值对应的Java类型 Class<?> resultTypeClass = resolveClass(resultType); String resultSetType = context.getStringAttribute("resultSetType"); // 设置默认StatementType为Prepared,该参数指定了后面的JDBC处理时,采用哪种Statement StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); String nodeName = context.getNode().getNodeName(); // 解析SQL命令类型是什么?确定操作是CRUD中的哪一种 SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); //是否查询语句 boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); boolean useCache = context.getBooleanAttribute("useCache", isSelect); boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); // Include Fragments before parsing // <include>标签解析 XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); // Parse selectKey after includes and remove them. // 解析<selectKey>标签 processSelectKeyNodes(id, parameterTypeClass, langDriver); // Parse the SQL (pre: <selectKey> and <include> were parsed and removed) // 创建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; 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对象 builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); }
在这个方法里,对<select><insert><update><delete>标签进行解析,获取到了标签中配置的值,比如parameterType(入参类型)、resultType(出参类型)等做了获取,最重要的是创建SqlSource,解析SQL,封装SQL语句(未参数绑定)和入参信息并且通过构建者助手创建MappedStatement对象;
-
创建SqlSource、创建MappedStatement对象
-
创建SqlSource
@Override public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) { // 初始化了动态SQL标签处理器 XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType); // 解析动态SQL return builder.parseScriptNode(); } //解析动态标签 public SqlSource parseScriptNode() { // 解析select\insert\ update\delete标签中的SQL语句,最终将解析到的SqlNode封装到MixedSqlNode中的List集合中 // ****将带有${}号的SQL信息封装到TextSqlNode // ****将带有#{}号的SQL信息封装到StaticTextSqlNode // ****将动态SQL标签中的SQL信息分别封装到不同的SqlNode中 MixedSqlNode rootSqlNode = parseDynamicTags(context); SqlSource sqlSource = null; // 如果SQL中包含${}和动态SQL语句,则将SqlNode封装到DynamicSqlSource if (isDynamic) { sqlSource = new DynamicSqlSource(configuration, rootSqlNode); } else { // 如果SQL中包含#{},则将SqlNode封装到RawSqlSource中,并指定parameterType sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType); } return sqlSource; } // protected MixedSqlNode parseDynamicTags(XNode node) { List<SqlNode> contents = new ArrayList<>(); //获取<select>\<insert>等4个标签的子节点,子节点包括元素节点和文本节点 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) { // issue #628 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); }
通过循环标签内子节点,将每个节点进行分类封装,最终封装成一个MixedSqlNode(混合的sqlNode);
这边进行一个简单的讲解:
-
SqlNode:是一个接口,有一个apply的方法,将解析完的语句append到结果中;
实现类:
-
MixedSqlNode:是一个混合的SqlNode,用来封装sql解析的上下文;
-
StaticTextSqlNode:最简单的SqlNode,功能仅仅就是将自身记录的text拼接到context上下文中;
-
TextSqlNode:
- 表示包含 “${}” 占位符的动态SQL节点;
TextSqlNode.apply()
方法会使用GenericTokenParser解析“${}”占位符,并直接替换成传入的实际参数值;- 实际参数值获取逻辑在内部
BindingTokenParser.handleToken
中;
-
IfSqlNode:if标签平常用的最多,在处理对应节点逻辑时,其主要工作就是通过Ognl表达式和传入的参数进行判断,看传入的参数值是否有满足if test里的表达式,满足将SQL片段合并到context上下文中。若不满足test则过滤掉这一部分SQL片段,不添加到context中;
-
WhereSqlNode:<where /> 标签的 SqlNode 实现类,继承至TrimSqlNode。WhereSqlNode会传入
prefix=WHERE
和 prefixesToOverride=["AND ","OR ",“AND\n”, “OR\n”, “AND\r”, “OR\r”, “AND\t”, “OR\t”]`; -
SetSqlNode:<set /> 标签的 SqlNode 实现类,继承至TrimSqlNode。SetSqlNode会传入
prefix=WHERE
和prefixesToOverride=[“,”]
; -
TrimSqlNode:<trim /> 标签的 SqlNode 实现类,继承至TrimSqlNode;
-
ForEachSqlNode:<foreach /> 标签的 SqlNode 实现类;
-
VarDeclSqlNode:<bind /> 标签的 SqlNode 实现类;
-
-
NodeHandler:
//在XMLScriptBuilder的构造方法中初始化了SQL中的节点处理器集合
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());
}
-
BindHandler用于解析<bind /> 标签节点
private class BindHandler implements NodeHandler { public BindHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) { //获取name和value属性 final String name = nodeToHandle.getStringAttribute("name"); final String expression = nodeToHandle.getStringAttribute("value"); //包装成简单的VarDeclSqlNode类 final VarDeclSqlNode node = new VarDeclSqlNode(name, expression); targetContents.add(node); } }
-
TrimHandler用于解析<trim />标签节点
private class TrimHandler implements NodeHandler { public TrimHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) { //trim标签下可包含where/set/if/when等标签,将之封装成MixedSqlNode MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); // read prefix/preffixOverrides/suffix/suffixOverrides properties String prefix = nodeToHandle.getStringAttribute("prefix"); String prefixOverrides = nodeToHandle.getStringAttribute("prefixOverrides"); String suffix = nodeToHandle.getStringAttribute("suffix"); String suffixOverrides = nodeToHandle.getStringAttribute("suffixOverrides"); // 委托TrimSqlNode处理SQL TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides); targetContents.add(trim); } }
-
WhereHandler用于解析<where />标签节点
private class WhereHandler implements NodeHandler { public WhereHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) { MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); // 与TrimSqlNode相同,包装成简单的WhereSqlNode类 WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode); targetContents.add(where); } }
-
SetHandler用于解析<set />标签节点
private class SetHandler implements NodeHandler { public SetHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) { MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); // 与TrimSqlNode相同,包装成简单的SetSqlNode类 SetSqlNode set = new SetSqlNode(configuration, mixedSqlNode); targetContents.add(set); } }
-
ForEachHandler用于解析<foreach />标签节点
private class ForEachHandler implements NodeHandler { public ForEachHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) { MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); // read collection/item/index/open/close/separator properties 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"); // 独立的 SqlNode ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, mixedSqlNode, collection, index, item, open, close, separator); targetContents.add(forEachSqlNode); } }
-
IfHandler用于解析<if />标签节点
private class IfHandler implements NodeHandler { public IfHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) { MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); // read test properties String test = nodeToHandle.getStringAttribute("test"); IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test); targetContents.add(ifSqlNode); } }
-
OtherwiseHandler/ChooseHandler用于解析otherwise/choose/when节点,这三者一般搭配使用
-
OtherwiseHandler
private class OtherwiseHandler implements NodeHandler { public OtherwiseHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) { MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle); targetContents.add(mixedSqlNode); } }
-
ChooseHandler,内含choose/when的解析
private class ChooseHandler implements NodeHandler { public ChooseHandler() { // Prevent Synthetic Access } @Override public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) { List<SqlNode> whenSqlNodes = new ArrayList<>(); List<SqlNode> otherwiseSqlNodes = new ArrayList<>(); //解析choose...when..otherwise结构 handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes); //检查otherwise标签是否只有一个,大于一个则报错 SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes); ChooseSqlNode chooseSqlNode = new ChooseSqlNode(whenSqlNodes, defaultSqlNode); targetContents.add(chooseSqlNode); } // when标签使用IfHandler解析,otherwise标签使用OtherwiseHandler private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List<SqlNode> ifSqlNodes, List<SqlNode> defaultSqlNodes) { List<XNode> 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); } } } private SqlNode getDefaultSqlNode(List<SqlNode> 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; } }
-
ChooseSqlNode:choose…when…otherwise结构类似于java的switch…case结构
public class ChooseSqlNode implements SqlNode { private SqlNode defaultSqlNode; private List<SqlNode> ifSqlNodes; public ChooseSqlNode(List<SqlNode> ifSqlNodes, SqlNode defaultSqlNode) { this.ifSqlNodes = ifSqlNodes; this.defaultSqlNode = defaultSqlNode; } public boolean apply(DynamicContext context) { for (SqlNode sqlNode : ifSqlNodes) { if (sqlNode.apply(context)) { return true; } } if (defaultSqlNode != null) { defaultSqlNode.apply(context); return true; } return false; }
-
通过上述的解析,如果包含子标签,比如<if />标签,会通过策略模式使用对应的sql标签处理器进行解析,调用
IfHandler.handleNode()
方法,进行递归调用一步一步解析<if />标签,最终封装为一个MixedSqlNode,再将MixedSqlNode封装到SqlSource中;回到之前的地方封装SqlSource后,接下来是将刚才解析到的各种信息通过builderAssistant(构建者助手)创建MappedStatement对象;
-
-
创建MappedStatement对象
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 = applyCurrentNamespace(id, false); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; //利用构建者模式,去创建MappedStatement.Builder,用于创建MappedStatement对象 MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType) .resource(resource) .fetchSize(fetchSize) .timeout(timeout) .statementType(statementType) .keyGenerator(keyGenerator) .keyProperty(keyProperty) .keyColumn(keyColumn) .databaseId(databaseId) .lang(lang) .resultOrdered(resultOrdered) .resultSets(resultSets) .resultMaps(getStatementResultMaps(resultMap, resultType, id)) .resultSetType(resultSetType) .flushCacheRequired(valueOrDefault(flushCache, !isSelect)) .useCache(valueOrDefault(useCache, isSelect)) .cache(currentCache); ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id); if (statementParameterMap != null) { statementBuilder.parameterMap(statementParameterMap); } // 通过MappedStatement.Builder,构建一个MappedStatement MappedStatement statement = statementBuilder.build(); // 将MappedStatement对象存储到Configuration中的Map集合中,key为statement的id,value为MappedStatement对象 configuration.addMappedStatement(statement); return statement; }
创建MappedStatement对象后将MappedStatement对象存储到Configuration中的Map集合中,key为statement的id,value为MappedStatement对象。
以上就是已经mapper映射文件全部解析完毕了,方法继续往后执行就是去解析注解方式的mapper接口,全部解析工作完成后创建了SqlSessionFactory并返回;
Mybatis的解析流程已经全部完成了,Mybatis的执行sql 的流程请听下回分解;