Mybatis3源码笔记(五)mapperElement
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来判断是否已经解析,防止重复解析。
Mybatis3源码笔记(五)mapperElement的更多相关文章
- Tomcat8源码笔记(五)组件Container分析
Tomcat8源码笔记(四)Server和Service初始化 介绍过Tomcat中Service的初始化 最先初始化就是Container,而Container初始化过程是咋样的? 说到Contai ...
- Mybatis3源码笔记(一)环境搭建
1. 源码下载 地址:https://github.com/mybatis/mybatis-3.git. 国内访问有时确实有点慢,像我就直接先fork.然后从git上同步到国内的gitte上,然后在i ...
- Mybatis3源码笔记(八)小窥MyBatis-plus
前言 Mybatis-Plus是一个 MyBatis增强工具包,简化 CRUD 操作,在 MyBatis 的基础上只做增强不做改变,为简化开发.提高效率而生,号称无侵入,现在开发中比较常用,包括我自己 ...
- Mybatis3源码笔记(六)SqlSession执行过程
前几篇大致分析了初始化的过程,今天打算走一个SqlSession具体执行过程. @Test void shouldSelectAllAuthors() { try (SqlSession sessio ...
- Mybatis3源码笔记(七)Plugin
1.Mybatis3的插件其实主要是用到了责任链和动态代理两种模式相结合而生成的.下面我们看一个例子,在执行所有update操作时,执行一个小小的测试输出. @Intercepts({@Signatu ...
- Mybatis3源码笔记(三)Configuration
1. XMLConfigBuilder 上一篇大致介绍了SqlSession的生成.在DefaultSqlSessionFactory的构造函数中就提到了Configuration这个对象.现在我们来 ...
- Mybatis3源码笔记(四)Configuration(续)
1.pluginElement(root.evalNode("plugins")) 解析plugins节点(注册interceptorChain里记录对应的拦截器) private ...
- Mybatis3源码笔记(二)SqlSession
1. 核心层次 2. SqlSession 先从顶层的SqlSession接口开始说起.SqlSession是MyBatis提供的面向用户的API,表示和数据库的会话对象,用于完成对数据库的一系列CR ...
- Tomcat8源码笔记(七)组件启动Server Service Engine Host启动
一.Tomcat启动的入口 Tomcat初始化简单流程前面博客介绍了一遍,组件除了StandardHost都有博客,欢迎大家指文中错误.Tomcat启动类是Bootstrap,而启动容器启动入口位于 ...
随机推荐
- VUE实现富文本编辑以及组件传值的使用总结
VUE实现使用富文本编辑,如下图: 实现这个富文本编辑需要以下步骤: 第一步:安装编辑器组件 npm install vue-quill-editor –-save第二步:创建一个Ue.vue的文件, ...
- Vue框架- 指令操作
目录 一.Vue指令操作 1. 表单指令 2. 条件指令 3. 循环指令 4. 斗篷指令 5. 实例成员:delimiter分隔符(了解) 6. filter过滤器 7. computed计算属性 8 ...
- ServiceMesh
传统微服务架构 在微服务模式下,企业内部服务少则几个到几十个,多则上百个,每个服务一般都以集群方式部署,这时自然产生两个问题: 一.服务发现:服务的消费方(Consumer)如何发现服务的提供方(Pr ...
- Mybatis初步认识
分三层 第一章 1.三层架构 界面层:和用户打交道,接收用户的请求参数明显是处理结果的(jsp,html,servlet) 业务逻辑层:接收了界面层传递的数据,计算逻辑,调用数据库,获取数据 数据访问 ...
- PAT-1151(LCA in a Binary Tree)+最近公共祖先+二叉树的中序遍历和前序遍历
LCA in a Binary Tree PAT-1151 本题的困难在于如何在中序遍历和前序遍历已知的情况下找出两个结点的最近公共祖先. 可以利用据中序遍历和前序遍历构建树的思路,判断两个结点在根节 ...
- LeetCode-在受污染的二叉树中查找元素
在受污染的二叉树中查找元素 LeetCode-1261 /** * 给出一个满足下述规则的二叉树: * root.val == 0 * 如果 treeNode.val == x 且 treeNode. ...
- [Elementary Mechanics-01]Two masses and a spring
[Elementary Mechanics Using Python-01] Question 5.28 Two masses and a spring. Two particles of m = 0 ...
- 话说CAS
一.前言 cas 一般认为是compare and swap 也可以认为是compare and set cas涉及三个值 1)P 变量内存地址 2)E 期望值 ,CPU做计算之前拿出来的旧值 3) ...
- 趣谈 DHCP 协议,有点意思。
计算机网络我也连载了很多篇了,大家可以在我的公众号「程序员cxuan」 或者我的 github 系统学习. 计算机网络第一篇,聊一聊网络基础 :计算机网络基础知识总结 计算机网络第二篇,聊一聊 TCP ...
- 导出文件,responseType设置了blob,实际返回了JSON格式的错误信息的处理方式
需求:导出文件 问题描述:由于后台直接返回的文件流,在请求下载的方法中将XHR 的 responseType 指定为 blob 或者 arraybuffer.但并不是每次的操作都是成功的,所以在接口错 ...