MyBatis框架的使用及源码分析(四) 解析Mapper接口映射xml文件
在<MyBatis框架中Mapper映射配置的使用及原理解析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder> 一文中,我们知道mybatis配置文件是由XMLConfigBuilder来解析的,看以下代码:
- public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}- private void parseConfiguration(XNode root) {
try {
propertiesElement(root.evalNode("properties")); //issue #117 read properties first
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
settingsElement(root.evalNode("settings"));
environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
//解析mapper
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
可以看到解析mapper文件的加载解析i是从 mapperElement(root.evalNode("mappers")); 开始处理的。
我们继续看mapperElement(XNode parent)方法的代码:
- private void mapperElement(XNode parent) throws Exception {
- if (parent != null) {
- for (XNode child : parent.getChildren()) {
- if ("package".equals(child.getName())) { //解析package扫描指定package下的所有mapper接口
- String mapperPackage = child.getStringAttribute("name");
- configuration.addMappers(mapperPackage);
- } else { //解析mapper节点
- String resource = child.getStringAttribute("resource");
- String url = child.getStringAttribute("url");
- String mapperClass = child.getStringAttribute("class");
- if (resource != null && url == null && mapperClass == null) {
- ErrorContext.instance().resource(resource);
- InputStream inputStream = Resources.getResourceAsStream(resource);
- XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
- 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());
- mapperParser.parse();
- } else if (resource == null && url == null && mapperClass != null) {
- Class<?> mapperInterface = Resources.classForName(mapperClass);
- configuration.addMapper(mapperInterface);
- } else {
- throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
- }
- }
- }
- }
- }
可以看到,mybatisConfig.xml配置下的mappers节点有2种子节点:package节点和mapper节点,我这里先讨论xml的模式,先看mapper节点。
mapper节点配置有3个属性:resource,url,class。他们处理的优先级依次是resource,url,class,3个属性只处理一种。resource,url属性映射的是xml的路径,class是mapper接口的类路径。
从源码中我们看到,通过读取resource或url属性得到xml的访问路径后,交给XMLMapperBuilder对象来解析。
我们查看XMLMapperBuilder的parse方法:
- public void parse() {
- if (!configuration.isResourceLoaded(resource)) {
- configurationElement(parser.evalNode("/mapper")); //从mapper节点开始解析
- configuration.addLoadedResource(resource); //标记已经加载了次xml资源
- bindMapperForNamespace(); //绑定到命名空间
- }
- parsePendingResultMaps(); //将resultMap映射信息转换成ResultMap对象
- parsePendingChacheRefs(); //将cache映射信息转换成Cache对象
- parsePendingStatements(); //将sql映射转换成MappedStatement
- }
- //解析xml
private void configurationElement(XNode context) {
- try {
String namespace = context.getStringAttribute("namespace");
if (namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
- //绑定到命名空间
private void bindMapperForNamespace() {
- String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);
}
}
}
}
其中configurationElement(XNode context)负责解析所有的xml元素,bindMapperForNamespace() 绑定到命名空间
而parsePendingResultMaps(), parsePendingChacheRefs(), parsePendingStatements()则分别将对应的xml信息转换成ResultMap对象,Cache对象和MappedStatement对象。
ResultMap的处理
- private void parsePendingResultMaps() {
- Collection<ResultMapResolver> incompleteResultMaps = configuration.getIncompleteResultMaps();
- synchronized (incompleteResultMaps) {
- Iterator<ResultMapResolver> iter = incompleteResultMaps.iterator();
- while (iter.hasNext()) {
- try {
- iter.next().resolve(); //实际处理的是ResultMapResolver.resolve()方法
- iter.remove();
- } catch (IncompleteElementException e) {
- // ResultMap is still missing a resource...
- }
- }
- }
- }
看代码得知,实际处理的是ResultMapResolver.resolve()方法
- package org.apache.ibatis.builder;
- import java.util.List;
- import org.apache.ibatis.mapping.Discriminator;
- import org.apache.ibatis.mapping.ResultMap;
- import org.apache.ibatis.mapping.ResultMapping;
- /**
- * @author Eduardo Macarron
- */
- public class ResultMapResolver {
- private final MapperBuilderAssistant assistant;
- private String id;
- private Class<?> type;
- private String extend;
- private Discriminator discriminator;
- private List<ResultMapping> resultMappings;
- private Boolean autoMapping;
- public ResultMapResolver(MapperBuilderAssistant assistant, String id, Class<?> type, String extend, Discriminator discriminator, List<ResultMapping> resultMappings, Boolean autoMapping) {
- this.assistant = assistant;
- this.id = id;
- this.type = type;
- this.extend = extend;
- this.discriminator = discriminator;
- this.resultMappings = resultMappings;
- this.autoMapping = autoMapping;
- }
- public ResultMap resolve() {
- return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping);
- }
- }
接着发现最终调用的是MapperBuilderAssistant.addResultMap 方法
- public ResultMap addResultMap(
- String id,
- Class<?> type,
- String extend,
- Discriminator discriminator,
- List<ResultMapping> resultMappings,
- Boolean autoMapping) {
- id = applyCurrentNamespace(id, false);
- extend = applyCurrentNamespace(extend, true);
- ResultMap.Builder resultMapBuilder = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping);
- if (extend != null) {
- if (!configuration.hasResultMap(extend)) {
- throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
- }
- ResultMap resultMap = configuration.getResultMap(extend);
- List<ResultMapping> extendedResultMappings = new ArrayList<ResultMapping>(resultMap.getResultMappings());
- extendedResultMappings.removeAll(resultMappings);
- // Remove parent constructor if this resultMap declares a constructor.
- boolean declaresConstructor = false;
- for (ResultMapping resultMapping : resultMappings) {
- if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
- declaresConstructor = true;
- break;
- }
- }
- if (declaresConstructor) {
- Iterator<ResultMapping> extendedResultMappingsIter = extendedResultMappings.iterator();
- while (extendedResultMappingsIter.hasNext()) {
- if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) {
- extendedResultMappingsIter.remove();
- }
- }
- }
- resultMappings.addAll(extendedResultMappings);
- }
- resultMapBuilder.discriminator(discriminator);
- ResultMap resultMap = resultMapBuilder.build();
- configuration.addResultMap(resultMap); //添加到Configuration
- return resultMap;
- }
Cache的处理
- private void parsePendingChacheRefs() {
- Collection<CacheRefResolver> incompleteCacheRefs = configuration.getIncompleteCacheRefs();
- synchronized (incompleteCacheRefs) {
- Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator();
- while (iter.hasNext()) {
- try {
- iter.next().resolveCacheRef();
- iter.remove();
- } catch (IncompleteElementException e) {
- // Cache ref is still missing a resource...
- }
- }
- }
- }
调用的是org.apache.ibatis.builder.CacheRefResolver.resolveCacheRef()方法:
- public Cache resolveCacheRef() {
- return assistant.useCacheRef(cacheRefNamespace);
- }
最终调用org.apache.ibatis.builder.useCacheRef(String namespace)方法创建Cache对象并添加到Configuration:
- public Cache useCacheRef(String namespace) {
- if (namespace == null) {
- throw new BuilderException("cache-ref element requires a namespace attribute.");
- }
- try {
- unresolvedCacheRef = true;
- Cache cache = configuration.getCache(namespace);
- if (cache == null) {
- throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
- }
- currentCache = cache;
- unresolvedCacheRef = false;
- return cache;
- } catch (IllegalArgumentException e) {
- throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
- }
- }
MappedStatement的处理
从parsePendingStatements()方法开始跟踪
- private void parsePendingStatements() {
- Collection<XMLStatementBuilder> incompleteStatements = configuration.getIncompleteStatements();
- synchronized (incompleteStatements) {
- Iterator<XMLStatementBuilder> iter = incompleteStatements.iterator();
- while (iter.hasNext()) {
- try {
- iter.next().parseStatementNode(); //调用XMLStatementBuilder的parseStatementNode方法
- iter.remove();
- } catch (IncompleteElementException e) {
- // Statement is still missing a resource...
- }
- }
- }
- }
然后调用org.apache.ibatis.builder.xml.XMLStatementBuilder的parseStatementNode方法
- 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");
- 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);
- String nodeName = context.getNode().getNodeName();
- 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
- XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
- includeParser.applyIncludes(context.getNode());
- // Parse selectKey after includes and remove them.
- processSelectKeyNodes(id, parameterTypeClass, langDriver);
- // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
- 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))
- ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
- }
- builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
- fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
- resultSetTypeEnum, flushCache, useCache, resultOrdered,
- keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
- }
最后MappedStatement由org.apache.ibatis.builder.MapperBuilderAssistant的addMappedStatement方法创建,并加入到Configuration
- 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 statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType);
- statementBuilder.resource(resource);
- statementBuilder.fetchSize(fetchSize);
- statementBuilder.statementType(statementType);
- statementBuilder.keyGenerator(keyGenerator);
- statementBuilder.keyProperty(keyProperty);
- statementBuilder.keyColumn(keyColumn);
- statementBuilder.databaseId(databaseId);
- statementBuilder.lang(lang);
- statementBuilder.resultOrdered(resultOrdered);
- statementBuilder.resulSets(resultSets);
- setStatementTimeout(timeout, statementBuilder);
- setStatementParameterMap(parameterMap, parameterType, statementBuilder);
- setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
- setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder);
- MappedStatement statement = statementBuilder.build();
- configuration.addMappedStatement(statement);
- return statement;
- }
Mapper xml部分的解析,暂时粗略的写这么多,后续我们还将讲解package包的扫描和指定class的mapper的情况。
MyBatis框架的使用及源码分析(四) 解析Mapper接口映射xml文件的更多相关文章
- MyBatis框架的使用及源码分析(六) MapperRegistry
我们先Mapper接口的调用方式,见<MyBatis框架中Mapper映射配置的使用及原理解析(一) 配置与使用>的示例: public void findUserById() { Sql ...
- MyBatis框架的使用及源码分析(七) MapperProxy,MapperProxyFactory
从上文<MyBatis框架中Mapper映射配置的使用及原理解析(六) MapperRegistry> 中我们知道DefaultSqlSession的getMapper方法,最后是通过Ma ...
- MyBatis框架的使用及源码分析(十一) StatementHandler
我们回忆一下<MyBatis框架的使用及源码分析(十) CacheExecutor,SimpleExecutor,BatchExecutor ,ReuseExecutor> , 这4个Ex ...
- MyBatis框架的使用及源码分析(九) Executor
从<MyBatis框架的使用及源码分析(八) MapperMethod>文中我们知道执行Mapper的每一个接口方法,最后调用的是MapperMethod.execute方法.而当执行Ma ...
- MyBatis框架的使用及源码分析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder
在 <MyBatis框架中Mapper映射配置的使用及原理解析(一) 配置与使用> 的demo中看到了SessionFactory的创建过程: SqlSessionFactory sess ...
- MyBatis框架的使用及源码分析(八) MapperMethod
从 <MyBatis框架中Mapper映射配置的使用及原理解析(七) MapperProxy,MapperProxyFactory> 文中,我们知道Mapper,通过MapperProxy ...
- MyBatis框架的使用及源码分析(五) DefaultSqlSessionFactory和DefaultSqlSession
我们回顾<MyBatis框架中Mapper映射配置的使用及原理解析(一) 配置与使用> 一文的示例 private static SqlSessionFactory getSessionF ...
- MyBatis框架的使用及源码分析(十) CacheExecutor,SimpleExecutor,BatchExecutor ,ReuseExecutor
Executor分成两大类,一类是CacheExecutor,另一类是普通Executor. 普通类又分为: ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情.它为每个语句的执行 ...
- MyBatis框架的使用及源码分析(三) 配置篇 Configuration
从上文<MyBatis框架中Mapper映射配置的使用及原理解析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder> 我们知道XMLConf ...
随机推荐
- 在cmd里面使用mysql命令
1.先找出mysqld文件所在的位置,我的是在C:\Program Files\MySQL\MySQL Server 5.1\bin. 2.cd C:\Program Files\MySQL\MySQ ...
- lintcode-172-删除元素
172-删除元素 给定一个数组和一个值,在原地删除与值相同的数字,返回新数组的长度. 元素的顺序可以改变,并且对新的数组不会有影响. 样例 给出一个数组 [0,4,4,0,0,2,4,4],和值 4 ...
- <Android>tab选项卡
1.继承TabActivity实现 a) 在布局文件中使用FrameLayout列出Tab组件及Tab中的内容组件 b) Activity要继承TabActivity c ...
- 【week2】 四则运算改进
四则运算满足简单加减乘除,以及包含括号的复杂四则运算. 代码描述: 1.采用random随机数产生要参与计算的数字,以及运算符号 2.采用Scanner获取控制台输入的结果,与计算出来的结果进行比对, ...
- Web服务器性能压力测试工具
一.http_load 程序非常小,解压后也不到100K http_load以并行复用的方式运行,用以测试web服务器的吞吐量与负载. 但是它不同于大多数压力测试工具,它可以以一个单一的进程运行,一般 ...
- PHP给图片添加图片水印
涉及到的函数: 1.file_get_contents():用于将文件的内容读入到一个字符串中的首选方法.如果操作系统支持,还会使用内存映射技术来增强性能. 2.list():list() 函数用于在 ...
- Android基础------Intent组件
1.什么是intent 同Activity一样,也是Android应用组件在Android中承担着一种指令输出的作用Intent负责对应用中一次操作的动作及动作相关的数据进行描述.Android则根据 ...
- vs code 自动补全效果不理想的问题
之前一直用webstorm,最近换换口味,改用了VS Code,发现VS Code 智能提示得到的都不是我想要的 就比如 ! + tab ,HTML结构都出不来.经过一番搜索,发现是 VS Code ...
- 反向传播算法 Backpropagation Algorithm
假设我们有一个固定样本集,它包含 个样例.我们可以用批量梯度下降法来求解神经网络.具体来讲,对于单个样例(x,y),其代价函数为:这是一个(二分之一的)方差代价函数.给定一个包含 个样例的数据集,我们 ...
- bzoj 4568 [SCOI 2016] 幸运数字
题目大意 给定一棵\(n\)个点的树,每个点有权值 \(q\)次询问树上路径中 每个点权值可选可不选的最大异或和 \(n\le 2*10^4,q\le 2*10^5,val[i]\le 2^{60}\ ...