mybatis中是怎么解析mapper中的sql的?

SqlSessionFactoryBuilder->build方法中解析的XML文件
// 解析</mappers>标签
mapperElement(root.evalNode("mappers"));
/**
 * 解析<mappers>标签
 * @param parent  mappers标签对应的XNode对象
 * @throws Exception
 */
private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    // 获取<mappers>标签的子标签
    for (XNode child : parent.getChildren()) {
   // <package>子标签
      if ("package".equals(child.getName())) {
        // 获取mapper接口和mapper映射文件对应的package包名
        String mapperPackage = child.getStringAttribute("name");
        // 将包下所有的mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂
        configuration.addMappers(mapperPackage);
      } else {// <mapper>子标签
        // 获取<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);
          // 专门用来解析mapper映射文件
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
          // 通过XMLMapperBuilder解析mapper映射文件
          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());
          // 通过XMLMapperBuilder解析mapper映射文件
          mapperParser.parse();
        } else if (resource == null && url == null && mapperClass != null) {
          Class<?> mapperInterface = Resources.classForName(mapperClass);
          // 将指定mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂
          configuration.addMapper(mapperInterface);
        } else {
          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }
}

有三种XML写法:

1.接口所在包
  package标签,通过name属性指定mapper接口所在的包名 ,
  此时对应的映射文件必须与接口位于同一路径下,并且名称相同

<mappers>
    <!-- mapper接口所在的包名 -->
     <package name="com.i.mapper"/>
</mappers>

源吗:

if ("package".equals(child.getName())) {
  // 获取mapper接口和mapper映射文件对应的package包名
  String mapperPackage = child.getStringAttribute("name");
  // 将包下所有的mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂
  configuration.addMappers(mapperPackage);
}
反射拿到对应的类
/**
 * @since 3.2.2
 */
public void addMappers(String packageName, Class<?> superType) {
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
  // 根据package名称,加载该包下Mapper接口文件(不是映射文件)
  resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
  // 获取加载的Mapper接口
  Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
  for (Class<?> mapperClass : mapperSet) {
    // 将Mapper接口添加到MapperRegistry中
    addMapper(mapperClass);
  }
}
public <T> void addMapper(Class<T> type) {
  if (type.isInterface()) {
    // 如果Map集合中已经有该mapper接口的映射,就不需要再存储了
    if (hasMapper(type)) {
      throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
    }
    boolean loadCompleted = false;
    try {
   // 将mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂
      knownMappers.put(type, new MapperProxyFactory<T>(type));
      // It's important that the type is added before the parser is run
      // otherwise the binding may automatically be attempted by the
      // mapper parser. If the type is already known, it won't try.
      
      // 用来解析注解方式的mapper接口
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      // 解析注解方式的mapper接口
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

解析注解:

public void parse() {
// 获取mapper接口的全路径
   String resource = type.toString();
   // 是否解析过该mapper接口
   if (!configuration.isResourceLoaded(resource)) {
     // 先解析mapper映射文件
     loadXmlResource();
     // 设置解析标识
     configuration.addLoadedResource(resource);
     // Mapper构建者助手
     assistant.setCurrentNamespace(type.getName());
     // 解析CacheNamespace注解
     parseCache();
     // 解析CacheNamespaceRef注解
     parseCacheRef();
     Method[] methods = type.getMethods();
     for (Method method : methods) {
       try {
         // issue #237
         if (!method.isBridge()) {
           // 每个mapper接口中的方法,都解析成MappedStatement对象
           parseStatement(method);
         }
       } catch (IncompleteElementException e) {
         configuration.addIncompleteMethod(new MethodResolver(this, method));
       }
     }
   }
   parsePendingMethods();
 }

解析单个的mapper.xml:

/**
 * 解析映射文件
 * @param context 映射文件根节点<mapper>对应的XNode
 */
private void configurationElement(XNode context) {
  try {
    // 获取<mapper>标签的namespace值,也就是命名空间
    String namespace = context.getStringAttribute("namespace");
    // 命名空间不能为空
    if (namespace == null || 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>子标签,也就是SQL片段
    sqlElement(context.evalNodes("/mapper/sql"));
    // 解析<select>\<insert>\<update>\<delete>子标签
    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);
  }
}

2.相对路径配置
  mapper标签,通过resource属性引入classpath路径的相对资源

<mappers>
    <!-- 通过resource属性引入classpath路径的相对资源-->
       <mapper resource="com/i/mapper/FlowerMapper.xml"/>
      <mapper resource="com/i/mapper/StudentMapper.xml"/>
      <mapper resource="com/i/mapper/TeacherMapper.xml"/>
</mappers>
3.类注册引入
  mapper标签,通过class属性指定mapper接口名称,
  此时对应的映射文件必须与接口位于同一路径下,并且名称相同

<mappers>
          <!-- 使用接口信息进行配置 -->
          <mapper class="com.i.mapper.FlowerMapper"/>
          <mapper class="com.i.mapper.StudentMapper"/>
          <mapper class="com.i.mapper.TeacherMapper"/>
</mappers>
4.使用URL绝对路径方式引入(不用)
  mapper标签,通过url引入网络资源或者本地磁盘资源

<mappers>
    <mapper url="xml文件访问URL" />
    <mapper url="file:///var/mappers/UserMapper.xml"/>
</mappers>
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值