mybatis-mapper xml解析

490 阅读2分钟

背景

mapper xml 解析很复杂,因为其中的标签非常多,比如我们平时开发用到的select,delete,resultMap等都在此时解析。

xml解析模块

mybatis xml解析类都继承自 BaseBuilder

classDiagram
BaseBuilder <|-- MapperBuilderAssistant
BaseBuilder <|-- SqlSourceBuilder
BaseBuilder <|-- XMLConfigBuilder
BaseBuilder <|-- XMLMapperBuilder
BaseBuilder <|-- XMLScriptBuilder
BaseBuilder <|-- XMLStatementBuilder
class MapperBuilderAssistant
class SqlSourceBuilder
class XMLConfigBuilder
class XMLMapperBuilder
class XMLScriptBuilder
class XMLStatementBuilder

上图中XmlConfigBuiler 负责解析mybatis 的配置文件,XmlMapperBuilder 负责解析mapper映射文件,XmlStatementBuilder 负责解析mapper文件中的部分标签比如select|insert|update|deleteSqlSourceBuilder 负责解析真正的sql语句,XMLScriptBuilder 负责解析sql 里面带有动态逻辑标签的语句比如trim,where,set,foreach,if,choose,when,otherwise,bind等,MapperBuilderAssistant 协助类,比如addMappedStatement()方法帮忙构建MappedStatement 对象。

MappedStatement 解析

解析的逻辑大同小异,这里我们选一个典型并且核心的标签类MappedStatement来跟一下解析逻辑,看看能不能从中学到新知识,入口在 org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration

private void parseConfiguration(XNode root) {
  //...
  mapperElement(root.evalNode("mappers"));
}

#org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement()
private void mapperElement(XNode parent) throws Exception {
  for (XNode child : parent.getChildren()) {
    if ("package".equals(child.getName())) {
      String mapperPackage = child.getStringAttribute("name");
      configuration.addMappers(mapperPackage);
    } else {
      String resource = child.getStringAttribute("resource");
      String url = child.getStringAttribute("url");
      String mapperClass = child.getStringAttribute("class");
      if (resource != null && url == null && mapperClass == null) {
        InputStream inputStream = Resources.getResourceAsStream(resource);
        XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
        mapperParser.parse();//进入
      }
    }
  }
}
//省略调用链路直接到 org.apache...XMLMapperBuilder#buildStatementFromContext
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
//解析所有的insert/update/delete/select 标签
  for (XNode context : list) {
    final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
  statementParser.parseStatementNode();
  }
}
//org....xml.XMLStatementBuilder#parseStatementNode
public void parseStatementNode() {
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");

    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);

    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    //...
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    String resultSets = context.getStringAttribute("resultSets");

    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

image.png parseStatementNode() 方法将标签中的属性都取出来,然后通过builderAssistant 这个工具类,构建成MappedStatement对象,并放入configuration 的 map中key 为namespace+id,所以同一个mapper xml 中如果id一样会抛错,如图,这样一来 MappedStatement 就含有这个select中所有的配置,等到要执行具体sql的时候只需要从configuration 取出对应的 MappedStatement 对象即可,这篇文章就介绍到这。