mybatis源码分析-mapper文件之sql标签

前面了解到mybatis解析完resultMap标签的内容后,对sql进行解析,来看一下sql的解析过程。


 private void sqlElement(List list) throws Exception {
    if (configuration.getDatabaseId() != null) {
      sqlElement(list, configuration.getDatabaseId());
    }
    sqlElement(list, null);
  }

 private void sqlElement(List list, String requiredDatabaseId) throws Exception {
    for (XNode context : list) {
      String databaseId = context.getStringAttribute("databaseId");
      String id = context.getStringAttribute("id");
	  //命名空间 + sql标签中的id 
      id = builderAssistant.applyCurrentNamespace(id, false);
       //解析数据库id
      if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
        //将sql节点的内容添加到自己属性的Map中
        sqlFragments.put(id, context);
      }
    }
  }

解析节点的过程分为,判断数据库id是否匹配,获取相对应属性,替换标签和标签,创建SqlSource(替换${}占位符),生成MappedStatement对象,并且将这个对象新增到Configuration类的属性中。

 MappedStatement对象的作用,为sql语句绑定参数,执行sql语句,将结果集映射成对象

 public void parseStatementNode() {
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");
	//数据库id进行比对是否一致
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
	
	//获取insert update delete select 标签中其他的属性值 代码略

    
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
	//递归将标签变成sql语句,删除sql标签,并且将${}这类的参数赋上真实值
    includeParser.applyIncludes(context.getNode());

    //判断主键自动生成器是使用哪种,
	//mysql,sql server是可以自增的,oracler是使用序列
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
	//SqlSource其实就是sql语句,这里将对sql语句进行解析,如果是动态的则使用动态的解析
	//静态的则直接赋真实值,删除标签
    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;
	//命名空间 + 增删改查标签的id
    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添加到Configuration类的属性中
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }
 
 // 创建SqlSource过程,先创建XMLScriptBuilder
   public SqlSource parseScriptNode() {
   //判断是否含有动态的sql,占位符,动态sql相关节点
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
    SqlSource sqlSource = null;
	//判断是否是动态sql语句
    if (isDynamic) {
	//创建sqlSource
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }
  
  protected MixedSqlNode parseDynamicTags(XNode node) {
    List contents = new ArrayList();
    NodeList children = node.getNode().getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
		//创建XNode节点,这一步将${}能变成常量的都变成常量,所以 ${}这种会有sql注入的问题
      XNode child = node.newXNode(children.item(i));
      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
        String data = child.getStringBody("");
        TextSqlNode textSqlNode = new TextSqlNode(data);
		//还存在${}这种,那么就是动态sql了
        if (textSqlNode.isDynamic()) {
          contents.add(textSqlNode);
          isDynamic = true;
        } else {
          contents.add(new StaticTextSqlNode(data));
        }
      }
      //...
    }
    return new MixedSqlNode(contents);
  }

上述代码结束后,MappedStatement对象最终会放入Configuration类的一个map中,id就是MappedStatement对象的id,MappedStatement对象的id是什么?进一步查看会发现是命名空间 + 增删改查标签的id,也就是说,如果你的两个增删改查的方法名字是一样的,但是参数类型不一样,对于java来说,这是多态,mybatis来说,没有多态,有一个方法会被覆盖。

 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");
    }
    //将id变成命名空间 + id
    id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

   //...略
    configuration.addMappedStatement(statement);
    return statement;
  }

mybatis的mapper文件的sql解析部分大致就完成了,mybatis先进行数据库id进行判断是否正确,是否一致,然后解析sql语句,对sql语句进行解析,将以及${}这种占位符进行替换,生成SqlSource对象,然后生成MappedStatement 对象。

你可能感兴趣的:(mybatis)