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 ...
随机推荐
- 安装完最小化 RHEL/CentOS 7 后需要做的 30 件事情(一)转载自码农网
CentOS 是一个工业标准的 Linux 发行版,是红帽企业版 Linux 的衍生版本.你安装完后马上就可以使用,但是为了更好地使用你的系统,你需要进行一些升级.安装新的软件包.配置特定服务和应用程 ...
- (转)Spring Boot(十一):Spring Boot 中 MongoDB 的使用
http://www.ityouknow.com/springboot/2017/05/08/spring-boot-mongodb.html MongoDB 是最早热门非关系数据库的之一,使用也比较 ...
- PHP程序员遇到职业问题时,是离职?还是坚持?
PHP程序员遇到职业问题时,是离职?还是坚持? 初级php程序员最担心在公司里遇到原本其他程序员开发的项目,他们“跑路”以后的工作就由新程序员完成.而新员工也不懂内部的逻辑,酱紫让程序员很难处理后续的 ...
- 清除tomcat日志文件的shell脚本
#! /bin/bash d=`date +%F` exec >> /mydata/script/logs/$d>& echo "开始执行清除tomcat日志文件& ...
- UVA1609-Foul Play(构造+递归)
Problem UVA1609-Foul Play Accept: 101 Submit: 514Time Limit: 3000 mSec Problem Description Input Fo ...
- php实现TXT小说章节解析、小说章节在线阅读
每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 要实现TXT文本章节的解析,大概思路是在每个章节加入了特定的字符,然后根据字符的起始位 ...
- nginx之十三:搭建 nginx 反向代理用做内网域名转发
user www www;worker_processes 1;error_log logs/error.log;pid logs/nginx.pid;worker_rlimit_nofile 6 ...
- JDK动态代理(1)-----------new 对象的方式
//case 1: 直接newHelloWorldImpl helloWorldImpl = new HelloWorldImpl(); //case 2: 反射拿到类之后,通过newInstance ...
- Redis入门篇(安装与启动)
一.Redis介绍 Redis是NoSql的一种,在弄清楚Redis是个什么玩意之前,先了解下NoSql是什么.1.什么是NoSql NoSql,全名:Not Only Sql,是一种非关系型数据库, ...
- Linux如何查看端口状态
netstat命令各个参数说明如下: -t : 指明显示TCP端口 -u : 指明显示UDP端口 -l : 仅显示监听套接字(所谓套接字就是使应用程序能够读写与收发通讯协议(protocol)与资料的 ...