mybatis源码分析(三)------------映射文件的解析
本篇文章主要讲解映射文件的解析过程
Mapper映射文件有哪几种配置方式呢?看下面的代码:
<!-- 映射文件 -->
<mappers>
<!-- 通过resource指定Mapper文件 --> 方式一
<mapper resource="com/yht/mybatisTest/dao/goods.xml" /> <!-- 通过class指定接口,但需要将接口与Mapper文件同名,从而将两者建立起关系,此处接口是GoodsDao,那么Mapper映射文件就需要是GoodsDao.xml --> 方式二
<mapper class="com.yht.mybatisTest.dao.GoodsDao" /> <!-- 扫描指定包中的接口,需要将接口名与Mapper文件同名 --> 方式三
<package name="com.yht.mybatisTest.dao"/> <!-- 通过url指定Mapper文件位置 --> 方式四
<mapper url="file://........" />
</mappers>
源码部分如下:
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
// 循环处理mappers节点下所有的子节点
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// 获的package节点的name属性值
String mapperPackage = child.getStringAttribute("name");
// 针对 方式三 的解析方法
configuration.addMappers(mapperPackage);
} else {
// 获取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);
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.");
}
}
}
}
}
由上面代码可知:针对四种不同的配置分别进行了解析,这里我们主要分析 方式一 的解析方法,进入该方法:
public void parse() {
// 检测Mapper映射文件是否被解析过
if (!configuration.isResourceLoaded(resource)) {
// 解析mapper节点
configurationElement(parser.evalNode("/mapper"));
// 将资源文件添加到 已解析资源集合 中
configuration.addLoadedResource(resource);
// 注册Mapper接口
bindMapperForNamespace();
}
// 处理 configurationElement方法中解析失败的<ResultMap />节点
parsePendingResultMaps();
// 处理 configurationElement方法中解析失败的<cache-ref />节点
parsePendingChacheRefs();
// 处理 configurationElement方法中解析失败的SQL语句节点
parsePendingStatements();
}
一 解析Mapper节点
进入XMLMapperBuilder类的configurationElement方法中:
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (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/>节点
sqlElement(context.evalNodes("/mapper/sql"));
// 解析<insert>等节点
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
XMLMapperBuilder类主要是用于解析映射配置文件,它继承了BaseBuilder抽象类,所有对映射配置文件的解析方法都在这个类中,接下来就分别对这些节点的解析过程进行分析。
1.<cache-ref />的解析
1.1 使用方法:
<!-- 表示使用以下namespace中的cache对象,也就是说和下面namespace共用一个cache对象 -->
<cache-ref namespace="com.yht.mybatisTest.dao.GoodsDao"/>
1.2 源码分析:
private void cacheRefElement(XNode context) {
if (context != null) {
// 这个方法是把当前节点的namespace作为key,<cache-ref>节点指定的namespace属性值作为value,存放到HashMap中;前者共用后者的cache对象
configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
//cacheRefResolver是一个cache引用解析器,封装了当前XMLMapperBuilder对应的MapperBuilderAssistant对象,和被引用的namespace
CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
try {
// 这个方法主要是指定当前mapper文件使用的cache对象,也就是设置MapperBuilderAssistant的currentCache和unresolvedCacheRef字段
cacheRefResolver.resolveCacheRef();
} catch (IncompleteElementException e) {
configuration.addIncompleteCacheRef(cacheRefResolver);
}
}
}
进入 cacheRefResolver.resolveCacheRef();方法:
public Cache resolveCacheRef() {
// 进入此方法
return assistant.useCacheRef(cacheRefNamespace);
}
进入MapperBuilderAssistant类的userCacheRef方法,这个类是XMLMapperBuilder的一个辅助类,用于保存当前mapper文件的namespace,以及使用的cache对象
public Cache useCacheRef(String namespace) {
if (namespace == null) {
throw new BuilderException("cache-ref element requires a namespace attribute.");
}
try {
unresolvedCacheRef = true;
// 根据被引用的namespace,获取对应的cache对象
Cache cache = configuration.getCache(namespace);
if (cache == null) {
throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
}
// 使当前mapper文件的cache对象currentCache指向cache,也就是共用一个cache对象
currentCache = cache;
unresolvedCacheRef = false;
return cache;
} catch (IllegalArgumentException e) {
throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
}
}
1.3 总结:对于<cahce-ref>节点的解析,就是找到被引用namespace对应的cache对象,然后是当前namespace中的currentCache执向那个cache对象,也就是两者共用一个cache对象。在这个过程中,MapperBuilderAssistant这个辅助类保存了当前mapper文件中的namespace的值,cache对象以及其他属性。
2.<cache />的解析
2.1 使用方法:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
cache节点有这几个标签:
(a) eviction:缓存的回收策略
(b) flushInterval:刷新间隔
(c) size:要缓存的元素数目
(d) readOnly:如果为true表示只读,不能修改
(e) type:指定自定义的缓存的全类名
2.2 源码分析:
进入XMLMapperBuilder类的cacheElement方法:
private void cacheElement(XNode context) throws Exception {
if (context != null) {
// 获取<cache>节点的type属性,默认值是PERPETUAL
String type = context.getStringAttribute("type", "PERPETUAL");
// 获取type属性对应的Cache接口实现
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
// 获取<cache>节点的eviction属性
String eviction = context.getStringAttribute("eviction", "LRU");
// 根据eviction属性获取对应的类
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
// 获取<cache>节点的flushInterval属性
Long flushInterval = context.getLongAttribute("flushInterval");
// 获取size熟悉和readOnly属性
Integer size = context.getIntAttribute("size");
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
Properties props = context.getChildrenAsProperties();
// 以上从<cache>节点配置中获取的属性和对应的class,都是为生成cache对象做准备的,此处cache对象的生成使用了构造者模式
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, props);
}
}
通过上面的代码可知:Cache对象是由MapperBuilderAssistant类生成的,进入useNewCache方法:
public Cache useNewCache(Class<? extends Cache> typeClass,
Class<? extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
Properties props) {
typeClass = valueOrDefault(typeClass, PerpetualCache.class);
evictionClass = valueOrDefault(evictionClass, LruCache.class);
// 这里使用到了构造者模式,CacheBuilder是建造者的角色,Cache是生成的产品,产品类的角色
Cache cache = new CacheBuilder(currentNamespace)
.implementation(typeClass)
.addDecorator(evictionClass)
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.properties(props)
.build();
// 将cache对象放到configuration对象的StrctMap中,cache的id作为key,cache对象作为value。此处的cache对象使用了装饰器模式,最底层的对象是PerpetualCache
configuration.addCache(cache);
// 记录当前命名空间使用的cache对象
currentCache = cache;
return cache;
}
CacheBuilder是Cache的建造者,接下来分析CacheBuilder这个类:
// 这个类是建造者角色,根据<cache>节点中配置的各种属性来生成不同的Cache对象,<cache>节点中配置的属性都被赋予了这个类中的下面这些属性,然后又为这些属性提供了不同的赋值方法,可以灵活的生成任意组合的Cache对象;这是典型的建造者模式
public class CacheBuilder {
private String id; // Cache对象的唯一表示,一般情况下对应Mapper映射文件的namespace
private Class<? extends Cache> implementation; // Cache接口的真正实现类,默认是PerpetualCache
private List<Class<? extends Cache>> decorators; // 装饰器集合,默认只包含LRUCache.class
private Integer size; // Cache的大小
private Long clearInterval; //清理时间周期
private boolean readWrite; // 是否可读写
private Properties properties;// 其它配置信息
public CacheBuilder(String id) {
this.id = id;
this.decorators = new ArrayList<Class<? extends Cache>>();
}
// 这几个方法就是为生成的Cache对象使用到的方法
public CacheBuilder implementation(Class<? extends Cache> implementation) {
this.implementation = implementation;
return this;
} public CacheBuilder addDecorator(Class<? extends Cache> decorator) {
if (decorator != null) {
this.decorators.add(decorator);
}
return this;
} public CacheBuilder size(Integer size) {
this.size = size;
return this;
} public CacheBuilder clearInterval(Long clearInterval) {
this.clearInterval = clearInterval;
return this;
} // 生成Cache对象,cache对象是产品角色
public Cache build() {
//implement为null,decorators为空,则给予默认值
setDefaultImplementations();
// 根据implement指定的类型,创建Cache对象
Cache cache = newBaseCacheInstance(implementation, id);
// 根据<cache>节点下配置的<properties>信息,初始化Cache对象
setCacheProperties(cache);
// 如果cache对象的类型是PerpetualCahce类型,那么为其添加decorators集合中的装饰器,cache对象本身使用了装饰器模式
if (PerpetualCache.class.equals(cache.getClass())) { // issue #352, do not apply decorators to custom caches
for (Class<? extends Cache> decorator : decorators) {
// 为Cache对象添加装饰器
cache = newCacheDecoratorInstance(decorator, cache);
setCacheProperties(cache);// 为cache对象配置属性
}
// 添加mybatis中提供的标准装饰器
cache = setStandardDecorators(cache);
}
return cache;
}
}
3.<resultMap/>解析
3.1 使用方法:
<resultMap id="goodsMap" type="goods">
<id column="id" property="id"/>
<result column="name" property="name"/>
</resultMap>
3.2 源码解析
<resultMap>节点定义了数据库的结果集和javaBean对象之间的映射关系,在解析<resultMap>节点之前,先看两个类ResultMapping和ResultMap。
每个ResultMapping对象记录了结果集中的一列与javaBean中一个属性之间的映射关系,看它的属性字段:
private Configuration configuration; //Configuration对象
private String property; // 对应节点的property属性,表示的是javaBean中对应的属性
private String column; // 对应节点的column属性,表示的是从数据库中得到的列名或者列名的别名
private Class<?> javaType; //对应节点的javaType属性,表示的是一个javaBean的完全限定名,或者一个类型别名
private JdbcType jdbcType;// 对应节点的jdbcType属性,表示的是进行映射的列的JDBC类型
private TypeHandler<?> typeHandler;// 对应节点的typeHandler属性,表示的是类型处理器
private String nestedResultMapId; // 对应节点的resultMap属性 嵌套的结果映射时有用到
private String nestedQueryId; //对应节点的select属性 嵌套查询时有用到
private Set<String> notNullColumns;
private String columnPrefix;
private List<ResultFlag> flags;
private List<ResultMapping> composites;
private String resultSet; //对应节点的resultSet属性
private String foreignColumn;// 对应节点的foreignColumn属性
private boolean lazy; //是否延迟加载,对应节点的fetchType属性
对于ResultMap类,每个<resultMap>节点都会被解析成一个ResutltMap对象,看它的属性:
private String id; //<resultMap>节点id的属性
private Class<?> type; // <resultMap>节点type的属性
private List<ResultMapping> resultMappings; //ResutlMapping的集合
private List<ResultMapping> idResultMappings; //记录了映射关系中带有ID标志的映射关系 例如<id>节点和<constructor>节点的<idArg>子节点
private List<ResultMapping> constructorResultMappings; //记录映射关系中带有Constructor标志的映射关系,例如<constructor>所有子元素
private List<ResultMapping> propertyResultMappings; // 记录映射关系中不带有Constructor标志的映射关系
private Set<String> mappedColumns; // 记录所有映射关系中涉及的column熟悉的集合
private Discriminator discriminator;// 鉴别器 对应<discriminator>节点
private boolean hasNestedResultMaps; // 是否含有嵌套的结果映射,如果有,则为true
private boolean hasNestedQueries; // 是否含有嵌套查询,如果有,则为true
private Boolean autoMapping; //是否开启自动映射
现在我们进入<resultMap>节点的源码解析部分,进入XMLMapperBuilder的resultMapElement方法:
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
// 获取<resutlMap>节点的id属性
String id = resultMapNode.getStringAttribute("id",
resultMapNode.getValueBasedIdentifier());
// 获取<resultMap>节点的type属性
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
// 获取<resultMap>节点的extends属性,该属性指定了<resultMap>节点的继承关系
String extend = resultMapNode.getStringAttribute("extends");
// 读取resultMap节点的autoMapping属性
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
// 解析type类型
Class<?> typeClass = resolveClass(type);
Discriminator discriminator = null;
List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
resultMappings.addAll(additionalResultMappings);
List<XNode> resultChildren = resultMapNode.getChildren();
// 处理<resultMap>的所有子节点
for (XNode resultChild : resultChildren) {
// 处理<constructor>节点
if ("constructor".equals(resultChild.getName())) {
processConstructorElement(resultChild, typeClass, resultMappings);
} else if ("discriminator".equals(resultChild.getName())) {
// 处理<discriminator>节点
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
} else {
// 处理<id>,<result>,<association>,<collection>等节点
ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
// 创建ResultMapping对象,并添加到集合中
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
// 创建ResultMap对象,并添加到Configuration.resultMaps集合中
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}
接下来,我们分析上面红色字体的方法首先是buildResultMappingFromContext方法,根据字面意思也可以知道,该方法是从上下文环境中获取到的属性信息创建ResultMapping对象,进入该方法:
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, ArrayList<ResultFlag> flags) throws Exception {
// 获取每一个映射关系中 property,column,....的属性值
String property = context.getStringAttribute("property");
String column = context.getStringAttribute("column");
String javaType = context.getStringAttribute("javaType");
String jdbcType = context.getStringAttribute("jdbcType");
String nestedSelect = context.getStringAttribute("select");
String nestedResultMap = context.getStringAttribute("resultMap",
processNestedResultMappings(context, Collections.<ResultMapping> emptyList()));
String notNullColumn = context.getStringAttribute("notNullColumn");
String columnPrefix = context.getStringAttribute("columnPrefix");
String typeHandler = context.getStringAttribute("typeHandler");
String resulSet = context.getStringAttribute("resultSet");
String foreignColumn = context.getStringAttribute("foreignColumn");
boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
// 解析javaType,jdbcType和TypeHandler
Class<?> javaTypeClass = resolveClass(javaType);
@SuppressWarnings("unchecked")
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
// 创建ResultMapping对象
return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resulSet, foreignColumn, lazy);
}
4.<sql/>的解析
4.1 使用用法
<sql id="sql_where_key">
id = #{id}
</sql>
4.2 源码解析
private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
// 遍历<sql>节点
for (XNode context : list) {
// 获取databaseId属性
String databaseId = context.getStringAttribute("databaseId");
// 获取id属性
String id = context.getStringAttribute("id");
// 为id添加命名空间
id = builderAssistant.applyCurrentNamespace(id, false);
// 以id为key,context为value存放到Map中
if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) sqlFragments.put(id, context);
}
}
5.<select>,<insert>等sql节点的解析
5.1 使用方法:
<select id="selectGoodsById" resultMap="goodsMap">
select * from goods
<where>
<include refid="sql_where_key" />
</where>
</select>
5.2 源码解析
在源码解析前,先了解SQLSource接口和MappedStatement类
SqlSource接口表示映射文件或者注解中描述的sql语句,但是它并不是数据库可执行的sql语句,因为它还可能包含有动态sql语句相关的节点或者占位符等需要解析的元素。
public interface SqlSource {
// 根据映射文件或者注解描述的sql语句,以及传入的参数,返回可执行的sql
BoundSql getBoundSql(Object parameterObject);
}
MappedStatement表示映射文件中定义的sql节点,它的部分属性如下:
private String resource; //节点中id的属性
private Configuration configuration;
private String id;
private Integer fetchSize;
private Integer timeout;
private StatementType statementType;
private ResultSetType resultSetType;
private SqlSource sqlSource; //sqlSource对象,对应一条sql语句
private Cache cache;
private SqlCommandType sqlCommandType; // SQL的类型,INSERT,SELECT 等
SQL节点的解析是XMLStatementBuilder类来解析的,进入解析方法的入口:
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); // 以上代码是获取sql节点中的各种属性值,如 useCache,resultMap,resultType,paramterMap,timeout等 // Include Fragments before parsing 解析SQL语句前,先处理<sql>节点中的<include/>节点
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
// 处理selectKey节点
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// 解析SQL语句
// 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();
}
// 将SQL节点解析为MappedStatement对象,然后放到Configuration.mappedStatements集合中
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
到这里,映射配置文件的整个解析过程就结束了,在下一篇文章中,我们介绍SQL的执行过程。
mybatis源码分析(三)------------映射文件的解析的更多相关文章
- memcached源码分析三-libevent与命令解析
转载请注明出处https://www.cnblogs.com/yang-zd/p/11352833.html,谢谢合作! 前面已经分析了memcached中的slabs内存管理及缓存对象如何利用ite ...
- Mybatis源码分析之Mapper文件解析
感觉CSDN对markdown的支持不够友好,总是伴随各种问题,很恼火! xxMapper.xml的解析主要由XMLMapperBuilder类完成,parse方法来完成解析: public void ...
- MyBatis 源码分析 - 映射文件解析过程
1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...
- 精尽MyBatis源码分析 - MyBatis初始化(二)之加载Mapper接口与XML映射文件
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- 精尽 MyBatis 源码分析 - MyBatis 初始化(三)之 SQL 初始化(上)
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- 精尽MyBatis源码分析 - SQL执行过程(三)之 ResultSetHandler
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- mybatis源码分析(一)
mybatis源码分析(sqlSessionFactory生成过程) 1. mybatis框架在现在各个IT公司的使用不用多说,这几天看了mybatis的一些源码,赶紧做个笔记. 2. 看源码从一个d ...
- MyBatis 源码分析 - 缓存原理
1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...
- MyBatis 源码分析 - 内置数据源
1.简介 本篇文章将向大家介绍 MyBatis 内置数据源的实现逻辑.搞懂这些数据源的实现,可使大家对数据源有更深入的认识.同时在配置这些数据源时,也会更清楚每种属性的意义和用途.因此,如果大家想知其 ...
- MyBatis 源码分析 - 配置文件解析过程
* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...
随机推荐
- orcFile split和读数据原理总结(hive0.13)
http://blog.csdn.net/zhaorongsheng/article/details/72903431 官网关于orcfile的介绍 背景 Hive的rcfile格式已经使用多年,但是 ...
- 在 Linux 中自动配置 IPv6 地址
在 Linux 中自动配置 IPv6 地址 在本文中,我们将学习如何为 ULA 自动配置 IP 地址. 何时使用唯一本地地址 唯一本地地址unique local addresses(ULA)使用 f ...
- SpringBoot的简单登陆开发例子
1:这个例子用spirngboot整合mybatis,jdbc等技术开发的 2:步骤 2.1:新建一个工程 主要的两个步骤已经贴图了,第二张图是直接在pom.xml文件中加入依赖 2.2:新建完项目, ...
- Python:Day53 Template基础
一.模板由 html代码+逻辑控制代码 组成 二.逻辑控制代码的组成 1.变量(使用双大括号来引用变量) 语法格式:{{ var_name }} -----------------------Temp ...
- Flask 框架 debug=Ture 和Json解码:
Flask框架常用配置文件: 以下推荐四种方法: (一):创建一个配置类. (二):正常代码配置文件. 接下下来两种方法需要新建文件: 步骤为: 1:在当前目录下新建一个文件,名字为:config.i ...
- 深入理解koa中的co源码
阅读目录 一:理解Generator 二:理解js函数柯里化 三:理解Thunk函数 四:理解CO源码 回到顶部 一:理解Generator 在看co源码之前,我们先来理解下Generator函数.G ...
- UI 前端参考
:http://amazeui.org/ :http://www.dcloud.io/index.html :https://weui.io/ :http://m.sui.taobao.org/get ...
- 探讨.NET Core数据进行3DES加密和解密问题
前言 一直困扰着我关于数据加密这一块,24号晚上用了接近3个小时去完成一项任务,本以为立马能解决,但是为了保证数据的安全性,我们开始去对数据进行加密,然后接下来3个小时专门去研究加密这一块,然而用着用 ...
- 【C#复习总结】 Async 和 Await 的异步编程
谈到异步,必然要说下阻塞,在知乎上看到了网友举的例子非常省动,在这里我引用下. 怎样理解阻塞非阻塞与同步异步的区别? 老张爱喝茶,废话不说,煮开水. 出场人物:老张,水壶两把(普通水壶,简称水壶:会响 ...
- Taro开发微信小程序的初体验
了解Taro 听说Taro是从几个星期前开始的,在一次饭桌上,一个小伙伴说:"Hey, 你听说了Taro么,听说只需要写一套程序就可以生成H5,小程序以及RN的代码模板,并且类似于React ...