mabatisplus-mapper文件的加载原理

文章目录

加载过程

mabatisplus-mapper文件的加载原理

1、加载时机:项目启动实例化SqlSessionFactory时

项目初始化时,会实例化 SqlSessionFactory。实例化过程中,通过加载Xml mapper文件(利用XMLMapperBuilder对象),保存sql信息。

  • com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration#sqlSessionFactory
  • com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean#buildSqlSessionFactory
    • com.baomidou.mybatisplus.core.MybatisConfiguration(存储被加载对象)
    • org.apache.ibatis.builder.xml.XMLMapperBuilder
      • org.apache.ibatis.builder.xml.XMLStatementBuilder
        • org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode
// com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean#buildSqlSessionFactory 片段
Resource[] var24 = this.mapperLocations;
int var27 = var24.length;

for(int var5 = 0; var5 < var27; ++var5) {// 遍历mapper路径下的所有XML Mapper文件
    Resource mapperLocation = var24[var5];
    if (mapperLocation != null) {
        try {
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
            xmlMapperBuilder.parse();// 解析XML Mapper
        } catch (Exception var19) {
            throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", var19);
        } finally {
            ErrorContext.instance().reset();
        }

        LOGGER.debug(() -> {
            return "Parsed mapper file: '" + mapperLocation + "'";
        });
    }
}

2、加载处理过程

循环加载、处理每个XML Mapper

// org.apache.ibatis.builder.xml.XMLMapperBuilder#parse
public void parse() {
        if (!this.configuration.isResourceLoaded(this.resource)) {
        	// 加载XML Mapper文件内容
            this.configurationElement(this.parser.evalNode("/mapper"));// 将XML 元素都转化到类中
            this.configuration.addLoadedResource(this.resource);
            this.bindMapperForNamespace();
        }
		// 处理加载后的XML Mapper信息
        this.parsePendingResultMaps();
        this.parsePendingCacheRefs();
        this.parsePendingStatements();// 解析待处理statement,生成sql statement(不再包含 xml中的 sql引用关系)
}

1、加载的内容

  • XML 的各种标签:org.apache.ibatis.builder.xml.XMLMapperBuilder#configurationElement
    • 添加待处理缓存引用(cache-ref
      • 处理方法:org.apache.ibatis.builder.xml.XMLMapperBuilder#cacheRefElement
    • 添加待处理结果集(/mapper/resultMap)
      • 处理方法:org.apache.ibatis.builder.xml.XMLMapperBuilder#resultMapElements
    • 添加待处理语句(select|insert|update|delete)
      • 处理方法:org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(java.util.List<org.apache.ibatis.parsing.XNode>, java.lang.String)
    • 其他标签:
      • cache
      • /mapper/parameterMap
      • /mapper/sql
        • 存储到 org.apache.ibatis.builder.xml.XMLMapperBuilder#sqlFragments
        • 若存在 SQL片段,一个SQL片段会对应生成两条记录:shortKey,namespace.shortKey
          • org.apache.ibatis.session.Configuration.StrictMap#put
  • 持久化XML Mapper的文件所在路径到内存中:
    • org.apache.ibatis.session.Configuration#loadedResources
  • 根据namespace,持久化XML Mapper与对应 Class类(Dao)的对应关系:org.apache.ibatis.builder.xml.XMLMapperBuilder#bindMapperForNamespace
    • Class:org.apache.ibatis.session.Configuration#mapperRegistry
    • “namespace:”+namespace:org.apache.ibatis.session.Configuration#loadedResources

2、对被加载内容的处理过程

存储被加载对象的内存对象:com.baomidou.mybatisplus.core.MybatisConfiguration

1、解析待处理结果集
  • 处理方法:org.apache.ibatis.builder.xml.XMLMapperBuilder#parsePendingResultMaps
    • org.apache.ibatis.builder.ResultMapResolver#resolve
  • 处理内容:org.apache.ibatis.session.Configuration#incompleteResultMaps
  • 结果:
    • 从org.apache.ibatis.builder.MapperBuilderAssistant 的成员变量org.apache.ibatis.session.Configuration#resultMaps中获取元素对象org.apache.ibatis.mapping.ResultMap,
    • 利用获取的对象信息,重新构造一个 org.apache.ibatis.mapping.ResultMap对象,
    • 将新构造的对象,存储并覆盖到 org.apache.ibatis.builder.MapperBuilderAssistant 的成员变量org.apache.ibatis.session.Configuration#resultMaps 中
2、解析待处理缓存引用
  • 处理方法:org.apache.ibatis.builder.xml.XMLMapperBuilder#parsePendingCacheRefs
    • org.apache.ibatis.builder.CacheRefResolver#resolveCacheRef
  • 处理内容:org.apache.ibatis.session.Configuration#incompleteCacheRefs
  • 结果:
    • 从org.apache.ibatis.builder.MapperBuilderAssistant 的成员变量org.apache.ibatis.session.Configuration#caches中获取元素对象org.apache.ibatis.cache.Cache,
    • 将其存储到 org.apache.ibatis.builder.MapperBuilderAssistant#currentCache中
3、解析待处理语句
  • 处理方法:org.apache.ibatis.builder.xml.XMLMapperBuilder#parsePendingStatements
    • org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode
  • 处理内容:org.apache.ibatis.session.Configuration#incompleteStatements
  • 结果:
    • 构造 org.apache.ibatis.mapping.MappedStatement对象,
    • 将其存储到 org.apache.ibatis.builder.MapperBuilderAssistant 的成员变量org.apache.ibatis.session.Configuration#mappedStatements

注意点

SQL片段内,使用Include标签时,refid需指定被调用SQL片段的全限定名称(namespace.id)

对于使用“”元素定义的sql片段,若该片段与他的外层引用片段同属于一个命名空间namespace,则内层sql的引用必须使用全限定namespace

情景详解:

  • AMapper.xml 中定义了两个sql片段,他们的id分别为:sqlOne, sqlTwo;
    • 存在引用关系:sqlOne 中 使用“<include>”元素引用了 sqlTwo(由于处于同一个namespace中,使用的refid=“sql的id名”);
  • BMapper.xml 中定义了一个“<select>”元素,且使用“<include>”元素引用了AMapper.xml中的id="sqlOne"的SQL片段(使用的refid=“namespace”+“.”+“sql的id名”);

此时,在BMapper.xml中,对于SQL片段“sqlTwo”,由于没有前缀namespace,就会默认为本namespace中的SQL片段,为之加上自己的namespace名,此时,就会找不到SQL片段,造成执行报错(找不到元素,无法转化xml为sql语句)
mabatisplus-mapper文件的加载原理

上一篇:The error may exist in com/bjpowernode/dao/StudentDao.xml ### Cause: org.apache.ibatis.builder.Build


下一篇:iBatis自动生成的主键 (Oracle,MS Sql Server,MySQL)