本篇文章主要讲解映射文件的解析过程

Mapper映射文件有哪几种配置方式呢?看下面的代码:

  1. <!-- 映射文件 -->
  2. <mappers>
  3. <!-- 通过resource指定Mapper文件 --> 方式一
  4. <mapper resource="com/yht/mybatisTest/dao/goods.xml" />
  5.  
  6. <!-- 通过class指定接口,但需要将接口与Mapper文件同名,从而将两者建立起关系,此处接口是GoodsDao,那么Mapper映射文件就需要是GoodsDao.xml --> 方式二
  7. <mapper class="com.yht.mybatisTest.dao.GoodsDao" />
  8.  
  9. <!-- 扫描指定包中的接口,需要将接口名与Mapper文件同名 --> 方式三
  10. <package name="com.yht.mybatisTest.dao"/>
  11.  
  12. <!-- 通过url指定Mapper文件位置 --> 方式四
  13. <mapper url="file://........" />
  14. </mappers>

源码部分如下:

  1. private void mapperElement(XNode parent) throws Exception {
  2. if (parent != null) {
    // 循环处理mappers节点下所有的子节点
  3. for (XNode child : parent.getChildren()) {
  4. if ("package".equals(child.getName())) {
    // 获的package节点的name属性值
  5. String mapperPackage = child.getStringAttribute("name");
    // 针对 方式三 的解析方法
  6. configuration.addMappers(mapperPackage);
  7. } else {
    // 获取mapper节点的resource属性值
  8. String resource = child.getStringAttribute("resource");
  9. // 获取mapper节点的url属性值
    String url = child.getStringAttribute("url");
    // 获取mapper节点的class属性值
  10. String mapperClass = child.getStringAttribute("class");
  11. if (resource != null && url == null && mapperClass == null) {
  12. ErrorContext.instance().resource(resource);
  13. InputStream inputStream = Resources.getResourceAsStream(resource);
  14. XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
    // 针对 方式一 的解析方法
  15. mapperParser.parse();
  16. } else if (resource == null && url != null && mapperClass == null) {
  17. ErrorContext.instance().resource(url);
  18. InputStream inputStream = Resources.getUrlAsStream(url);
  19. XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
  20. // 针对 方式四 的解析方法
    mapperParser.parse();
  21. } else if (resource == null && url == null && mapperClass != null) {
  22. Class<?> mapperInterface = Resources.classForName(mapperClass);
    // 针对 方式二 的解析方法
  23. configuration.addMapper(mapperInterface);
  24. } else {
  25. throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
  26. }
  27. }
  28. }
  29. }
  30. }

由上面代码可知:针对四种不同的配置分别进行了解析,这里我们主要分析 方式一 的解析方法,进入该方法:

  1. public void parse() {
    // 检测Mapper映射文件是否被解析过
  2. if (!configuration.isResourceLoaded(resource)) {
    // 解析mapper节点
  3. configurationElement(parser.evalNode("/mapper"));
    // 将资源文件添加到 已解析资源集合 中
  4. configuration.addLoadedResource(resource);
    // 注册Mapper接口
  5. bindMapperForNamespace();
  6. }
  7. // 处理 configurationElement方法中解析失败的<ResultMap />节点
  8. parsePendingResultMaps();
    // 处理 configurationElement方法中解析失败的<cache-ref />节点
  9. parsePendingChacheRefs();
    // 处理 configurationElement方法中解析失败的SQL语句节点
  10. parsePendingStatements();
  11. }

一 解析Mapper节点

进入XMLMapperBuilder类的configurationElement方法中:

  1. private void configurationElement(XNode context) {
  2. try {
  3. String namespace = context.getStringAttribute("namespace");
  4. if (namespace.equals("")) {
  5. throw new BuilderException("Mapper's namespace cannot be empty");
  6. }
    // 设置当前的namespace
  7. builderAssistant.setCurrentNamespace(namespace);
    // 解析<cache-ref/>节点
  8. cacheRefElement(context.evalNode("cache-ref"));
    // 解析<cache/>节点
  9. cacheElement(context.evalNode("cache"));
    // 解析parameterMap节点
  10. parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    // 解析resultMap节点
  11. resultMapElements(context.evalNodes("/mapper/resultMap"));
    // <sql/>节点
  12. sqlElement(context.evalNodes("/mapper/sql"));
    // 解析<insert>等节点
  13. buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  14. } catch (Exception e) {
  15. throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
  16. }
  17. }

XMLMapperBuilder类主要是用于解析映射配置文件,它继承了BaseBuilder抽象类,所有对映射配置文件的解析方法都在这个类中,接下来就分别对这些节点的解析过程进行分析。

1.<cache-ref />的解析

1.1 使用方法:

  1. <!-- 表示使用以下namespace中的cache对象,也就是说和下面namespace共用一个cache对象 -->
    <cache-ref namespace="com.yht.mybatisTest.dao.GoodsDao"/>

1.2 源码分析:

  1. private void cacheRefElement(XNode context) {
  2. if (context != null) {
    // 这个方法是把当前节点的namespace作为key,<cache-ref>节点指定的namespace属性值作为value,存放到HashMap中;前者共用后者的cache对象
  3. configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
    //cacheRefResolver是一个cache引用解析器,封装了当前XMLMapperBuilder对应的MapperBuilderAssistant对象,和被引用的namespace
  4. CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
  5. try {
    // 这个方法主要是指定当前mapper文件使用的cache对象,也就是设置MapperBuilderAssistant的currentCache和unresolvedCacheRef字段
  6. cacheRefResolver.resolveCacheRef();
  7. } catch (IncompleteElementException e) {
  8. configuration.addIncompleteCacheRef(cacheRefResolver);
  9. }
  10. }
  11. }

进入 cacheRefResolver.resolveCacheRef();方法:

  1. public Cache resolveCacheRef() {
    // 进入此方法
  2. return assistant.useCacheRef(cacheRefNamespace);
  3. }

进入MapperBuilderAssistant类的userCacheRef方法,这个类是XMLMapperBuilder的一个辅助类,用于保存当前mapper文件的namespace,以及使用的cache对象

  1. public Cache useCacheRef(String namespace) {
  2. if (namespace == null) {
  3. throw new BuilderException("cache-ref element requires a namespace attribute.");
  4. }
  5. try {
  6. unresolvedCacheRef = true;
    // 根据被引用的namespace,获取对应的cache对象
  7. Cache cache = configuration.getCache(namespace);
  8. if (cache == null) {
  9. throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
  10. }
    // 使当前mapper文件的cache对象currentCache指向cache,也就是共用一个cache对象
  11. currentCache = cache;
  12. unresolvedCacheRef = false;
  13. return cache;
  14. } catch (IllegalArgumentException e) {
  15. throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
  16. }
  17. }

1.3 总结:对于<cahce-ref>节点的解析,就是找到被引用namespace对应的cache对象,然后是当前namespace中的currentCache执向那个cache对象,也就是两者共用一个cache对象。在这个过程中,MapperBuilderAssistant这个辅助类保存了当前mapper文件中的namespace的值,cache对象以及其他属性。

2.<cache />的解析

2.1 使用方法:

  1. <cache
  2. eviction="FIFO"
  3. flushInterval="60000"
  4. size="512"
  5. readOnly="true"/>

cache节点有这几个标签:

(a)  eviction:缓存的回收策略

(b) flushInterval:刷新间隔

(c) size:要缓存的元素数目

(d) readOnly:如果为true表示只读,不能修改

(e) type:指定自定义的缓存的全类名

2.2 源码分析:

进入XMLMapperBuilder类的cacheElement方法:

  1. private void cacheElement(XNode context) throws Exception {
  2. if (context != null) {
    // 获取<cache>节点的type属性,默认值是PERPETUAL
  3. String type = context.getStringAttribute("type", "PERPETUAL");
    // 获取type属性对应的Cache接口实现
  4. Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
    // 获取<cache>节点的eviction属性
  5. String eviction = context.getStringAttribute("eviction", "LRU");
  6. // 根据eviction属性获取对应的类
    Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
    // 获取<cache>节点的flushInterval属性
  7. Long flushInterval = context.getLongAttribute("flushInterval");
    // 获取size熟悉和readOnly属性
  8. Integer size = context.getIntAttribute("size");
  9. boolean readWrite = !context.getBooleanAttribute("readOnly", false);
  10. Properties props = context.getChildrenAsProperties();
    // 以上从<cache>节点配置中获取的属性和对应的class,都是为生成cache对象做准备的,此处cache对象的生成使用了构造者模式
  11. builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, props);
  12. }
  13. }

通过上面的代码可知:Cache对象是由MapperBuilderAssistant类生成的,进入useNewCache方法:

  1. public Cache useNewCache(Class<? extends Cache> typeClass,
  2. Class<? extends Cache> evictionClass,
  3. Long flushInterval,
  4. Integer size,
  5. boolean readWrite,
  6. Properties props) {
  7. typeClass = valueOrDefault(typeClass, PerpetualCache.class);
  8. evictionClass = valueOrDefault(evictionClass, LruCache.class);
    // 这里使用到了构造者模式,CacheBuilder是建造者的角色,Cache是生成的产品,产品类的角色
  9. Cache cache = new CacheBuilder(currentNamespace)
  10. .implementation(typeClass)
  11. .addDecorator(evictionClass)
  12. .clearInterval(flushInterval)
  13. .size(size)
  14. .readWrite(readWrite)
  15. .properties(props)
  16. .build();
    // 将cache对象放到configuration对象的StrctMap中,cache的id作为key,cache对象作为value。此处的cache对象使用了装饰器模式,最底层的对象是PerpetualCache
  17. configuration.addCache(cache);
    // 记录当前命名空间使用的cache对象
  18. currentCache = cache;
  19. return cache;
  20. }

CacheBuilder是Cache的建造者,接下来分析CacheBuilder这个类:

  1. // 这个类是建造者角色,根据<cache>节点中配置的各种属性来生成不同的Cache对象,<cache>节点中配置的属性都被赋予了这个类中的下面这些属性,然后又为这些属性提供了不同的赋值方法,可以灵活的生成任意组合的Cache对象;这是典型的建造者模式
    public class CacheBuilder {
  2. private String id; // Cache对象的唯一表示,一般情况下对应Mapper映射文件的namespace
  3. private Class<? extends Cache> implementation; // Cache接口的真正实现类,默认是PerpetualCache
  4. private List<Class<? extends Cache>> decorators; // 装饰器集合,默认只包含LRUCache.class
  5. private Integer size; // Cache的大小
  6. private Long clearInterval; //清理时间周期
  7. private boolean readWrite; // 是否可读写
  8. private Properties properties;// 其它配置信息

  9. public CacheBuilder(String id) {
  10. this.id = id;
  11. this.decorators = new ArrayList<Class<? extends Cache>>();
  12. }
  13. // 这几个方法就是为生成的Cache对象使用到的方法
  14. public CacheBuilder implementation(Class<? extends Cache> implementation) {
  15. this.implementation = implementation;
  16. return this;
  17. }
  18.  
  19. public CacheBuilder addDecorator(Class<? extends Cache> decorator) {
  20. if (decorator != null) {
  21. this.decorators.add(decorator);
  22. }
  23. return this;
  24. }
  25.  
  26. public CacheBuilder size(Integer size) {
  27. this.size = size;
  28. return this;
  29. }
  30.  
  31. public CacheBuilder clearInterval(Long clearInterval) {
  32. this.clearInterval = clearInterval;
  33. return this;
  34. }
  35.  
  36. // 生成Cache对象,cache对象是产品角色
  37. public Cache build() {
    //implement为null,decorators为空,则给予默认值
  38. setDefaultImplementations();
    // 根据implement指定的类型,创建Cache对象
  39. Cache cache = newBaseCacheInstance(implementation, id);
    // 根据<cache>节点下配置的<properties>信息,初始化Cache对象
  40. setCacheProperties(cache);
    // 如果cache对象的类型是PerpetualCahce类型,那么为其添加decorators集合中的装饰器,cache对象本身使用了装饰器模式
  41. if (PerpetualCache.class.equals(cache.getClass())) { // issue #352, do not apply decorators to custom caches
  42. for (Class<? extends Cache> decorator : decorators) {
    // 为Cache对象添加装饰器
  43. cache = newCacheDecoratorInstance(decorator, cache);
  44. setCacheProperties(cache);// 为cache对象配置属性
  45. }
    // 添加mybatis中提供的标准装饰器
  46. cache = setStandardDecorators(cache);
  47. }
  48. return cache;
  49. }
  50. }

 3.<resultMap/>解析

3.1 使用方法:

  1. <resultMap id="goodsMap" type="goods">
  2. <id column="id" property="id"/>
  3. <result column="name" property="name"/>
  4. </resultMap>

3.2 源码解析

<resultMap>节点定义了数据库的结果集和javaBean对象之间的映射关系,在解析<resultMap>节点之前,先看两个类ResultMapping和ResultMap。

每个ResultMapping对象记录了结果集中的一列与javaBean中一个属性之间的映射关系,看它的属性字段:

  1. private Configuration configuration; //Configuration对象
  2. private String property; // 对应节点的property属性,表示的是javaBean中对应的属性
  3. private String column; // 对应节点的column属性,表示的是从数据库中得到的列名或者列名的别名
  4. private Class<?> javaType; //对应节点的javaType属性,表示的是一个javaBean的完全限定名,或者一个类型别名
  5. private JdbcType jdbcType;// 对应节点的jdbcType属性,表示的是进行映射的列的JDBC类型
  6. private TypeHandler<?> typeHandler;// 对应节点的typeHandler属性,表示的是类型处理器
  7. private String nestedResultMapId; // 对应节点的resultMap属性 嵌套的结果映射时有用到
  8. private String nestedQueryId; //对应节点的select属性 嵌套查询时有用到
  9. private Set<String> notNullColumns;
  10. private String columnPrefix;
  11. private List<ResultFlag> flags;
  12. private List<ResultMapping> composites;
  13. private String resultSet; //对应节点的resultSet属性
  14. private String foreignColumn;// 对应节点的foreignColumn属性
  15. private boolean lazy; //是否延迟加载,对应节点的fetchType属性

对于ResultMap类,每个<resultMap>节点都会被解析成一个ResutltMap对象,看它的属性:

  1. private String id; //<resultMap>节点id的属性
  2. private Class<?> type; // <resultMap>节点type的属性
  3. private List<ResultMapping> resultMappings; //ResutlMapping的集合
  4. private List<ResultMapping> idResultMappings; //记录了映射关系中带有ID标志的映射关系 例如<id>节点和<constructor>节点的<idArg>子节点
  5. private List<ResultMapping> constructorResultMappings; //记录映射关系中带有Constructor标志的映射关系,例如<constructor>所有子元素
  6. private List<ResultMapping> propertyResultMappings; // 记录映射关系中不带有Constructor标志的映射关系
  7. private Set<String> mappedColumns; // 记录所有映射关系中涉及的column熟悉的集合
  8. private Discriminator discriminator;// 鉴别器 对应<discriminator>节点
  9. private boolean hasNestedResultMaps; // 是否含有嵌套的结果映射,如果有,则为true
  10. private boolean hasNestedQueries; // 是否含有嵌套查询,如果有,则为true
  11. private Boolean autoMapping; //是否开启自动映射

现在我们进入<resultMap>节点的源码解析部分,进入XMLMapperBuilder的resultMapElement方法:

  1. private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
  2. ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
    // 获取<resutlMap>节点的id属性
  3. String id = resultMapNode.getStringAttribute("id",
  4. resultMapNode.getValueBasedIdentifier());
    // 获取<resultMap>节点的type属性
  5. String type = resultMapNode.getStringAttribute("type",
  6. resultMapNode.getStringAttribute("ofType",
  7. resultMapNode.getStringAttribute("resultType",
  8. resultMapNode.getStringAttribute("javaType"))));
    // 获取<resultMap>节点的extends属性,该属性指定了<resultMap>节点的继承关系
  9. String extend = resultMapNode.getStringAttribute("extends");
    // 读取resultMap节点的autoMapping属性
  10. Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
    // 解析type类型
  11. Class<?> typeClass = resolveClass(type);
  12. Discriminator discriminator = null;
  13. List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
  14. resultMappings.addAll(additionalResultMappings);
  15. List<XNode> resultChildren = resultMapNode.getChildren();
    // 处理<resultMap>的所有子节点
  16. for (XNode resultChild : resultChildren) {
    // 处理<constructor>节点
  17. if ("constructor".equals(resultChild.getName())) {
  18. processConstructorElement(resultChild, typeClass, resultMappings);
  19. } else if ("discriminator".equals(resultChild.getName())) {
    // 处理<discriminator>节点
  20. discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
  21. } else {
    // 处理<id>,<result>,<association>,<collection>等节点
  22. ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
  23. if ("id".equals(resultChild.getName())) {
  24. flags.add(ResultFlag.ID);
  25. }
    // 创建ResultMapping对象,并添加到集合中
  26. resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
  27. }
  28. }
  29. ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
  30. try {
    // 创建ResultMap对象,并添加到Configuration.resultMaps集合中
  31. return resultMapResolver.resolve();
  32. } catch (IncompleteElementException e) {
  33. configuration.addIncompleteResultMap(resultMapResolver);
  34. throw e;
  35. }
  36. }

接下来,我们分析上面红色字体的方法首先是buildResultMappingFromContext方法,根据字面意思也可以知道,该方法是从上下文环境中获取到的属性信息创建ResultMapping对象,进入该方法:

  1. private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, ArrayList<ResultFlag> flags) throws Exception {
    // 获取每一个映射关系中 property,column,....的属性值
  2. String property = context.getStringAttribute("property");
  3. String column = context.getStringAttribute("column");
  4. String javaType = context.getStringAttribute("javaType");
  5. String jdbcType = context.getStringAttribute("jdbcType");
  6. String nestedSelect = context.getStringAttribute("select");
  7. String nestedResultMap = context.getStringAttribute("resultMap",
  8. processNestedResultMappings(context, Collections.<ResultMapping> emptyList()));
  9. String notNullColumn = context.getStringAttribute("notNullColumn");
  10. String columnPrefix = context.getStringAttribute("columnPrefix");
  11. String typeHandler = context.getStringAttribute("typeHandler");
  12. String resulSet = context.getStringAttribute("resultSet");
  13. String foreignColumn = context.getStringAttribute("foreignColumn");
  14. boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
    // 解析javaType,jdbcType和TypeHandler
  15. Class<?> javaTypeClass = resolveClass(javaType);
  16. @SuppressWarnings("unchecked")
  17. Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
  18. JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
    // 创建ResultMapping对象
  19. return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resulSet, foreignColumn, lazy);
  20. }

4.<sql/>的解析

4.1 使用用法

  1. <sql id="sql_where_key">
  2. id = #{id}
  3. </sql>

4.2 源码解析

  1. private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
    // 遍历<sql>节点
  2. for (XNode context : list) {
    // 获取databaseId属性
  3. String databaseId = context.getStringAttribute("databaseId");
    // 获取id属性
  4. String id = context.getStringAttribute("id");
    // 为id添加命名空间
  5. id = builderAssistant.applyCurrentNamespace(id, false);
    // 以id为key,context为value存放到Map中
  6. if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) sqlFragments.put(id, context);
  7. }
  8. }

5.<select>,<insert>等sql节点的解析

5.1 使用方法:

  1. <select id="selectGoodsById" resultMap="goodsMap">
  2. select * from goods
  3. <where>
  4. <include refid="sql_where_key" />
  5. </where>
  6. </select>

5.2 源码解析

在源码解析前,先了解SQLSource接口和MappedStatement类

SqlSource接口表示映射文件或者注解中描述的sql语句,但是它并不是数据库可执行的sql语句,因为它还可能包含有动态sql语句相关的节点或者占位符等需要解析的元素。

  1. public interface SqlSource {
    // 根据映射文件或者注解描述的sql语句,以及传入的参数,返回可执行的sql
  2. BoundSql getBoundSql(Object parameterObject);
  3. }

MappedStatement表示映射文件中定义的sql节点,它的部分属性如下:

  1. private String resource; //节点中id的属性
  2. private Configuration configuration;
  3. private String id;
  4. private Integer fetchSize;
  5. private Integer timeout;
  6. private StatementType statementType;
  7. private ResultSetType resultSetType;
  8. private SqlSource sqlSource; //sqlSource对象,对应一条sql语句
  9. private Cache cache;
    private SqlCommandType sqlCommandType; // SQL的类型,INSERT,SELECT 等

SQL节点的解析是XMLStatementBuilder类来解析的,进入解析方法的入口:

  1. public void parseStatementNode() {
  2. String id = context.getStringAttribute("id");
  3. String databaseId = context.getStringAttribute("databaseId");
  4.  
  5. if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) return;
  6.  
  7. Integer fetchSize = context.getIntAttribute("fetchSize");
  8. Integer timeout = context.getIntAttribute("timeout");
  9. String parameterMap = context.getStringAttribute("parameterMap");
  10. String parameterType = context.getStringAttribute("parameterType");
  11. Class<?> parameterTypeClass = resolveClass(parameterType);
  12. String resultMap = context.getStringAttribute("resultMap");
  13. String resultType = context.getStringAttribute("resultType");
  14. String lang = context.getStringAttribute("lang");
  15. LanguageDriver langDriver = getLanguageDriver(lang);
  16.  
  17. Class<?> resultTypeClass = resolveClass(resultType);
  18. String resultSetType = context.getStringAttribute("resultSetType");
  19. StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  20. ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
  21.  
  22. String nodeName = context.getNode().getNodeName();
  23. SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
  24. boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  25. boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
  26. boolean useCache = context.getBooleanAttribute("useCache", isSelect);
  27. boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
  28.  
  29. // 以上代码是获取sql节点中的各种属性值,如 useCache,resultMap,resultType,paramterMap,timeout等
  30.  
  31. // Include Fragments before parsing 解析SQL语句前,先处理<sql>节点中的<include/>节点
  32. XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  33. includeParser.applyIncludes(context.getNode());
  34. // 处理selectKey节点
  35. // Parse selectKey after includes and remove them.
  36. processSelectKeyNodes(id, parameterTypeClass, langDriver);

  37. // 解析SQL语句
  38. // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
  39. SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  40. String resultSets = context.getStringAttribute("resultSets");
  41. String keyProperty = context.getStringAttribute("keyProperty");
  42. String keyColumn = context.getStringAttribute("keyColumn");
  43. KeyGenerator keyGenerator;
  44. String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  45. keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
  46. if (configuration.hasKeyGenerator(keyStatementId)) {
  47. keyGenerator = configuration.getKeyGenerator(keyStatementId);
  48. } else {
  49. keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
  50. configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
  51. ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
  52. }
  53. // 将SQL节点解析为MappedStatement对象,然后放到Configuration.mappedStatements集合中
  54. builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
  55. fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
  56. resultSetTypeEnum, flushCache, useCache, resultOrdered,
  57. keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  58. }

到这里,映射配置文件的整个解析过程就结束了,在下一篇文章中,我们介绍SQL的执行过程。

mybatis源码分析(三)------------映射文件的解析的更多相关文章

  1. memcached源码分析三-libevent与命令解析

    转载请注明出处https://www.cnblogs.com/yang-zd/p/11352833.html,谢谢合作! 前面已经分析了memcached中的slabs内存管理及缓存对象如何利用ite ...

  2. Mybatis源码分析之Mapper文件解析

    感觉CSDN对markdown的支持不够友好,总是伴随各种问题,很恼火! xxMapper.xml的解析主要由XMLMapperBuilder类完成,parse方法来完成解析: public void ...

  3. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  4. 精尽MyBatis源码分析 - MyBatis初始化(二)之加载Mapper接口与XML映射文件

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  5. 精尽 MyBatis 源码分析 - MyBatis 初始化(三)之 SQL 初始化(上)

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  6. 精尽MyBatis源码分析 - SQL执行过程(三)之 ResultSetHandler

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  7. mybatis源码分析(一)

    mybatis源码分析(sqlSessionFactory生成过程) 1. mybatis框架在现在各个IT公司的使用不用多说,这几天看了mybatis的一些源码,赶紧做个笔记. 2. 看源码从一个d ...

  8. MyBatis 源码分析 - 缓存原理

    1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...

  9. MyBatis 源码分析 - 内置数据源

    1.简介 本篇文章将向大家介绍 MyBatis 内置数据源的实现逻辑.搞懂这些数据源的实现,可使大家对数据源有更深入的认识.同时在配置这些数据源时,也会更清楚每种属性的意义和用途.因此,如果大家想知其 ...

  10. MyBatis 源码分析 - 配置文件解析过程

    * 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...

随机推荐

  1. 安装完最小化 RHEL/CentOS 7 后需要做的 30 件事情(一)转载自码农网

    CentOS 是一个工业标准的 Linux 发行版,是红帽企业版 Linux 的衍生版本.你安装完后马上就可以使用,但是为了更好地使用你的系统,你需要进行一些升级.安装新的软件包.配置特定服务和应用程 ...

  2. (转)Spring Boot(十一):Spring Boot 中 MongoDB 的使用

    http://www.ityouknow.com/springboot/2017/05/08/spring-boot-mongodb.html MongoDB 是最早热门非关系数据库的之一,使用也比较 ...

  3. PHP程序员遇到职业问题时,是离职?还是坚持?

    PHP程序员遇到职业问题时,是离职?还是坚持? 初级php程序员最担心在公司里遇到原本其他程序员开发的项目,他们“跑路”以后的工作就由新程序员完成.而新员工也不懂内部的逻辑,酱紫让程序员很难处理后续的 ...

  4. 清除tomcat日志文件的shell脚本

    #! /bin/bash d=`date +%F` exec >> /mydata/script/logs/$d>& echo "开始执行清除tomcat日志文件& ...

  5. UVA1609-Foul Play(构造+递归)

    Problem UVA1609-Foul Play Accept: 101  Submit: 514Time Limit: 3000 mSec Problem Description Input Fo ...

  6. php实现TXT小说章节解析、小说章节在线阅读

    每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 要实现TXT文本章节的解析,大概思路是在每个章节加入了特定的字符,然后根据字符的起始位 ...

  7. nginx之十三:搭建 nginx 反向代理用做内网域名转发

      user www www;worker_processes 1;error_log logs/error.log;pid logs/nginx.pid;worker_rlimit_nofile 6 ...

  8. JDK动态代理(1)-----------new 对象的方式

    //case 1: 直接newHelloWorldImpl helloWorldImpl = new HelloWorldImpl(); //case 2: 反射拿到类之后,通过newInstance ...

  9. Redis入门篇(安装与启动)

    一.Redis介绍 Redis是NoSql的一种,在弄清楚Redis是个什么玩意之前,先了解下NoSql是什么.1.什么是NoSql NoSql,全名:Not Only Sql,是一种非关系型数据库, ...

  10. Linux如何查看端口状态

    netstat命令各个参数说明如下: -t : 指明显示TCP端口 -u : 指明显示UDP端口 -l : 仅显示监听套接字(所谓套接字就是使应用程序能够读写与收发通讯协议(protocol)与资料的 ...