1.四种解析mapper方式 : package,resource,url,class。

  <mappers>
<mapper resource="org/apache/ibatis/builder/BlogMapper.xml"/>
<mapper url="file:./src/test/java/org/apache/ibatis/builder/NestedBlogMapper.xml"/>
<mapper class="org.apache.ibatis.builder.CachedAuthorMapper"/>
<package name="org.apache.ibatis.builder.mapper"/>
</mappers>
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//package引入
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
//resource引入
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
}
} else if (resource == null && url != null && mapperClass == null) {
//url引入
ErrorContext.instance().resource(url);
try (InputStream inputStream = Resources.getUrlAsStream(url)) {
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
}
} else if (resource == null && url == null && mapperClass != null) {
//mapperClass引入
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.");
}
}
}
}
}

2.跟一下package引入。

  public <T> void addMapper(Class<T> type) {
//必须是接口
if (type.isInterface()) {
//重复性检查
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<>(type));
//解析Mapper接口
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
  public void parse() {
String resource = type.toString();
//判断该resource有没有引入
if (!configuration.isResourceLoaded(resource)) {
//加载该命名空间下对应的xml
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
//解析二级缓存注解CacheNamespace
parseCache();
//解析二级缓存注解CacheNamespaceRef
parseCacheRef();
for (Method method : type.getMethods()) {
//检查下method类型,不能是桥接方法和接口中的默认方法
if (!canHaveStatement(method)) {
continue;
}
//select操作解析@select,@SelectProvider注释方法中的带有@ResultMap的方法
if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()
&& method.getAnnotation(ResultMap.class) == null) {
parseResultMap(method);
}
try {
//解析Statement
parseStatement(method);
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
//解析IncompleteMethod,解析失败的方法。
parsePendingMethods();
}
 void parseStatement(Method method) {
//取得方法的参数类型
final Class<?> parameterTypeClass = getParameterType(method);
//根据@Lang取得方法的LanguageDriver,默认是XMLLanguageDriver
final LanguageDriver languageDriver = getLanguageDriver(method);
//解析所有合法的statement注解,譬如@Select, @Update, @Insert, @Delete, @SelectProvider...
getAnnotationWrapper(method, true, statementAnnotationTypes).ifPresent(statementAnnotation -> {
//生成SqlSource
final SqlSource sqlSource = buildSqlSource(statementAnnotation.getAnnotation(), parameterTypeClass, languageDriver, method);
final SqlCommandType sqlCommandType = statementAnnotation.getSqlCommandType();
//取得@Options注解
final Options options = getAnnotationWrapper(method, false, Options.class).map(x -> (Options) x.getAnnotation()).orElse(null);
//设置mappedStatementId,方法的全路径
final String mappedStatementId = type.getName() + "." + method.getName(); final KeyGenerator keyGenerator;
String keyProperty = null;
String keyColumn = null;
//插入和更新需要涉及到主键自动生成
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
// @SelectKey注解生成主键
SelectKey selectKey = getAnnotationWrapper(method, false, SelectKey.class).map(x -> (SelectKey) x.getAnnotation()).orElse(null);
if (selectKey != null) {
keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
keyProperty = selectKey.keyProperty();
} else if (options == null) {
//默认模式
keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
} else {
//根据@Options中的value值实现自定义设置
keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
keyProperty = options.keyProperty();
keyColumn = options.keyColumn();
}
} else {
keyGenerator = NoKeyGenerator.INSTANCE;
} Integer fetchSize = null;
Integer timeout = null;
StatementType statementType = StatementType.PREPARED;
ResultSetType resultSetType = configuration.getDefaultResultSetType();
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = !isSelect;
boolean useCache = isSelect;
if (options != null) {
//设置对应的options
if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
flushCache = true;
} else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
flushCache = false;
}
useCache = options.useCache();
fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
timeout = options.timeout() > -1 ? options.timeout() : null;
statementType = options.statementType();
if (options.resultSetType() != ResultSetType.DEFAULT) {
resultSetType = options.resultSetType();
}
} String resultMapId = null;
if (isSelect) {
ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
if (resultMapAnnotation != null) {
//根据 @ResultMap({ "xx", "xxx" })设置resultMapId
resultMapId = String.join(",", resultMapAnnotation.value());
} else {
//没配置@ResultMap则走generateResultMapName方法生成resultMapId(方法的详细路径+“-”+参数类型)
resultMapId = generateResultMapName(method);
}
}
//生成Statement`
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
// ParameterMapID
null,
parameterTypeClass,
resultMapId,
getReturnType(method),
resultSetType,
flushCache,
useCache,
// TODO gcode issue #577
false,
keyGenerator,
keyProperty,
keyColumn,
statementAnnotation.getDatabaseId(),
languageDriver,
// ResultSets
options != null ? nullOrEmpty(options.resultSets()) : null);
});
}

3.其它的引入方式resource和url方式,就是先解析一波xml文件,大同小异。

  public void parse() {
if (!configuration.isResourceLoaded(resource)) {
//解析mapper.xml文件
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
//解析对应空间下的mapper接口(跟上面的package方法一样的解析过程)
bindMapperForNamespace();
} parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
  private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.isEmpty()) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
//设置命名空间
builderAssistant.setCurrentNamespace(namespace);
//设置二级缓存
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
//解析parameterMap
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//解析resultMap
resultMapElements(context.evalNodes("/mapper/resultMap"));
//解析sql片段
sqlElement(context.evalNodes("/mapper/sql"));
//解析真正的sql语句
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的解析分注解如@Select和xml两种解析,看代码我们可以看出来,同一个方法都是先找xml,后分析注解,根据configuration.isResourceLoaded来判断是否已经解析,防止重复解析。

最新文章

  1. (zz)linux awk
  2. c# -- 对象销毁和垃圾回收
  3. 对RESTful Web API的理解与设计思路
  4. c语言知识(2)
  5. 使用LKDBHelper 插入相同id时候应该是更新数据而不是插入新的数据
  6. jquery中如何退出each循环
  7. 开源免费的C/C++网络库(c/c++ sockets library)
  8. UIView上添加了一个按钮和一个单击手势的事件相应,互相不影响的处理方法。。
  9. spring mvc 使用jsr-303进行表单验证的方法介绍
  10. pro文件常用内容
  11. 数据结构-String、char
  12. STL - list(双向链表)
  13. Web 项目系列之浏览器机制(一)
  14. Clion+Cmake+Qt5+Qwt+msys2+MinGW在Windows下的安装配置使用教程
  15. LFYZ-OJ ID: 1008 求A/B高精度值
  16. ubuntu 安装pip3 遇到Ignoring ensurepip failure: pip 8.1.1 requires SSL/TLS错误
  17. GreenDao3使用完全解析
  18. Arduino入门笔记(3):单LED闪烁
  19. 6. Activity life cycle
  20. spark 学习(二) RDD及共享变量

热门文章

  1. 小白养成记——JavaWeb之文件的上传
  2. 双重检验锁模式为什么要使用volatile?
  3. JavaScript:什么是回调?
  4. Markdown(2)基本语法
  5. es6 快速入门 系列 —— 变量声明:let和const
  6. 使用python进行接口自动化测试,批量执行测试用例
  7. 12. Vue搭建本地服务
  8. django Form 效验
  9. 手把手教你集成华为机器学习服务(ML Kit)人脸检测功能
  10. C# 应用 - 使用 WepApp 处理文件上传、下载请求