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,而启动容器启动入口位于 ...
随机推荐
- 整合shiro+jwt大体思路
springboot整合shiro大体上的思路: 1.自定义一个类Realm extends AuthorizingRealm{} 主要是对token授权和认证 重写2个方法 doGetAuthori ...
- 死磕以太坊源码分析之EVM动态数据类型
死磕以太坊源码分析之EVM动态数据类型 配合以下代码进行阅读:https://github.com/blockchainGuide/ 写文不易,给个小关注,有什么问题可以指出,便于大家交流学习. So ...
- 微信小程序(八)-项目实例(原生框架 MINA转云开发)==03-云开发-数据库
云数据库 云数据库开发文档:https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/database.html 1.新建云数据库( ...
- WPF -- 一种添加静态资源的方式
本文介绍使用独立的xaml文件添加静态资源的方式. 步骤 创建XAML文件,如ImageButton.xaml,添加ResourceDictionary标签,并添加静态资源: 在App.xaml的Ap ...
- 剑指 Offer 14- II. 剪绳子 II + 贪心 + 数论 + 快速幂
剑指 Offer 14- II. 剪绳子 II 题目链接 因为有取模的操作,动态规划中max不能用了,我们观察:正整数从1开始,但是1不能拆分成两个正整数之和,所以不能当输入. 2只能拆成 1+1,所 ...
- POJ-1062(原始dijiksra算法+思维)
昂贵的婚礼 POJ-1062 这道题目一开始看的时候难以理解,但是仔细想,还是可以和最短路联系的,我觉得类似于硬币交换等问题. 以下需要注意几个点,第一就是因为题目规定如何和超出了等级限制的人交易,则 ...
- 漏洞复现-CVE-2018-15473-ssh用户枚举漏洞
0x00 实验环境 攻击机:Win 10 0x01 影响版本 OpenSSH 7.7前存在一个用户名枚举漏洞,通过该漏洞,攻击者可以判断某个用户名是否存在于目标主机 0x02 漏洞复现 针 ...
- Mongo的相关语法
mongod的条件操作符 $gt -------- greater than > $gte --------- gt equal >= $lt -------- less than < ...
- 选择 FreeBSD 而不是 Linux 的技术性原因2
ZFSZFS 文件系统是 FreeBSD 上的一等公民.这不仅意味着可以在 ZFS 上安装根目录,安装程序也支持这一点,而且还意味着很多基础系统工具都已经紧密地集成或构建了对 ZFS 的支持.在 Fr ...
- WPF 基础 - 图形的效果与滤镜
UIElement 有 BitmapEffect 和 Effect 属性,BitmapEffect 是由 CPU 的运算能力实现的,比较耗性能,推荐使用 Effect: Effect 包括但不限于 D ...