mybatis源码分析-配置文件解析(mapper节点的解析)

mybatis源码分析-配置文件解析(2 mapper节点解析)

mappers文件单独放一个文章出来,因为解析mappers很关键,是mybatis最重要的一个环节,解析了mappers下面的节点后,回去解析具体的mapper文件里面的sql等

mappers节点解析过程分析

mapperElement(root.evalNode("mappers"));

//解析mappers的节点,他配置有好多种
private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      //如果是配置了package
      if ("package".equals(child.getName())) {
        //com.wjk.mapper        <package name="com.wjk.mapper"/>
        String mapperPackage = child.getStringAttribute("name");
        configuration.addMappers(mapperPackage);
      } else {
        //resource,url,class的配置优先解析
        String resource = child.getStringAttribute("resource");
        String url = child.getStringAttribute("url");
        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.");
        }
      }
    }
  }
}

mapperParser.parse()解析

public void parse() {
  //这里判断是loadedResources = new HashSet<String>(); loadResources是一个hashSet,里面的contain方法判断是否存在key
  //加载我们的定义的mapper.xml的文件
  if (!configuration.isResourceLoaded(resource)) {
    //解析mapper节点
    configurationElement(parser.evalNode("/mapper"));
    // 添加资源路径到“已解析资源集合”中
    configuration.addLoadedResource(resource);
    // 通过命名空间绑定 Mapper 接口
    bindMapperForNamespace();
  }

  // 处理未完成解析的节点
  parsePendingResultMaps();
  parsePendingCacheRefs();
  parsePendingStatements();
}

//解析mapper节点
private void configurationElement(XNode context) {
  try {
    //获得namespace <mapper namespace="com.wjk.mapper.UserMapper">
    String namespace = context.getStringAttribute("namespace");
    if (namespace == null || namespace.equals("")) {
      throw new BuilderException("Mapper's namespace cannot be empty");
    }
    builderAssistant.setCurrentNamespace(namespace);
    cacheRefElement(context.evalNode("cache-ref"));
    cacheElement(context.evalNode("cache"));
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    //解析resultMap
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    //解析<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);
  }
}

private void buildStatementFromContext(List<XNode> list) {
  if (configuration.getDatabaseId() != null) {
    buildStatementFromContext(list, configuration.getDatabaseId());
  }
  buildStatementFromContext(list, null);
}

private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
  //具体解析我们的节点
  for (XNode context : list) {
    final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
    try {
      statementParser.parseStatementNode();
    } catch (IncompleteElementException e) {
      configuration.addIncompleteStatement(statementParser);
    }
  }
}

parseStatementNode解析具体的节点

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);
  //获得resultmap
  String resultMap = context.getStringAttribute("resultMap");
  //获得resultType,最后也会转换成resultmap
  String resultType = context.getStringAttribute("resultType");
  String lang = context.getStringAttribute("lang");
  LanguageDriver langDriver = getLanguageDriver(lang);
  //获得返回值的class类型,很关键
  Class<?> resultTypeClass = resolveClass(resultType);
  String resultSetType = context.getStringAttribute("resultSetType");
  StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
  //nodeName是我们的标签select ,update,insert之类的
  String nodeName = context.getNode().getNodeName();
  //获得我们具体的sqlCommandType类型
  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);

  // Include Fragments before parsing
  XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  includeParser.applyIncludes(context.getNode());

  // Parse selectKey after includes and remove them.
  processSelectKeyNodes(id, parameterTypeClass, langDriver);
  
  // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
  //获得sqlSource,sqlSource里面有sql #{}部分代替成 ?展示,然后里面的字段隐射放到list里面parameterMapping
  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))
        ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
  }
	//生成一个MappedStatement
  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered, 
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}

builderAssistant.addMappedStatement()方法,生成MappedStatement放到configuration里面

public MappedStatement addMappedStatement(
  String id,
  SqlSource sqlSource,
  StatementType statementType,
  SqlCommandType sqlCommandType,
  Integer fetchSize,
  Integer timeout,
  String parameterMap,
  Class<?> parameterType,
  String resultMap,
  Class<?> resultType,
  ResultSetType resultSetType,
  boolean flushCache,
  boolean useCache,
  boolean resultOrdered,
  KeyGenerator keyGenerator,
  String keyProperty,
  String keyColumn,
  String databaseId,
  LanguageDriver lang,
  String resultSets) {

  if (unresolvedCacheRef) {
    throw new IncompleteElementException("Cache-ref not yet resolved");
  }
  //namespace + id
  id = applyCurrentNamespace(id, false);
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  //id selectTest sqlSoure包含sql和parameterMappings                                                         //sql类型 select
  MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
    .resource(resource) //userMapper.xml
    .fetchSize(fetchSize)
    .timeout(timeout)
    .statementType(statementType)//PREPARED
    .keyGenerator(keyGenerator)
    .keyProperty(keyProperty)
    .keyColumn(keyColumn)
    .databaseId(databaseId)
    .lang(lang)
    .resultOrdered(resultOrdered)
    .resultSets(resultSets)
    //如果是resultType也转换成resultMap
    .resultMaps(getStatementResultMaps(resultMap, resultType, id)) //class com.wjk.entity.user
    .resultSetType(resultSetType)
    .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
    .useCache(valueOrDefault(useCache, isSelect))
    .cache(currentCache);
  ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
  if (statementParameterMap != null) {
    statementBuilder.parameterMap(statementParameterMap);
  }

  MappedStatement statement = statementBuilder.build();
  //放入到configuration里面 mappedStatements是一个StrictMap
  configuration.addMappedStatement(statement);
  return statement;
}

//  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
public void addMappedStatement(MappedStatement ms) {
  mappedStatements.put(ms.getId(), ms);
}

//类似于我们的hashMap
public V put(String key, V value) {
  //如果存在key,直接抛出异常
  if (containsKey(key)) {
    throw new IllegalArgumentException(name + " already contains value for " + key);
  }
  //如果存在点 namespace.id,直接往map里面放入两个
  //一个是id,一个是nameSpace+id的放入map里面
  if (key.contains(".")) {
    final String shortKey = getShortName(key);
    if (super.get(shortKey) == null) {
      super.put(shortKey, value);
    } else {
      super.put(shortKey, (V) new Ambiguity(shortKey));
    }
  }
  return super.put(key, value);
}

总结:

mappers节点解析过程

1.解析mappers节点

2.遍历mappers下面的xxxmapper.xml文件,解析里面的所有节点,select,insert等

3.遍历里面的每个节点,生成mappedStatement对象,mappedStatement里面有参数解析,结果集解析,sqlSource(后面参数解析使用,里面还有sql,里面的#{}用?代替)

4.mappedStatement对象放入到mappedStatement里面,他是一个StrictMap,里面存两个一个是id,还有一个是namespace+id, value都是mappedStatement

  • 12
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值