Mybatis3源码分析(05)-加载Configuration-加载MappedStatement

MappedStatement说明

一个MappedStatement对象对应Mapper配置文件中的一个select/update/insert/delete节点,主要描述的是一条SQL语句。其属性有
  
  //节点中的id属性加要命名空间
  private String id;
  //直接从节点属性中取
  private Integer fetchSize;
  //直接从节点属性中取
  private Integer timeout;
  private StatementType statementType;
  private ResultSetType resultSetType;
  //对应一条SQL语句
  private SqlSource sqlSource;

  //每条语句都对就一个缓存,如果有的话。
  private Cache cache;
  //这个已经过时了
  private ParameterMap parameterMap;
  private List resultMaps;
  private boolean flushCacheRequired;
  private boolean useCache;
  private boolean resultOrdered;
  //SQL的类型,select/update/insert/detete
  private SqlCommandType sqlCommandType;
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  
  //是否有内映射
  private boolean hasNestedResultMaps;
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;
  private String[] resultSets;

上面属性都比较简单,复杂的是SqlSource,下面有详细的描述!

XMLStatementBuilder.parseStatementNode()方法

resultMap元素的解析已经分析完毕。与resultMap不一样,XmlMapperBuilder在解析select/update/insert/delete的元素时会创建一个XMLStatementBuilder对象,解析的工作交由其方法parseStatementNode()方法完成。
private void buildStatementFromContext(List list, String requiredDatabaseId) {
    for (XNode context : list) {
      //一个select/update/insert/delete元素创建一个XMLStatementBuilder对象
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        //将元素解析成MappedStatemenet对象,并加入到Configuration中去
        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);
    String resultMap = context.getStringAttribute("resultMap");
    String resultType = context.getStringAttribute("resultType");
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    Class resultTypeClass = resolveClass(resultType);
    String resultSetType = context.getStringAttribute("resultSetType");
    //Statement的类型,对应jdbc里的三个类型:Statement、PreparedStatement、CallableStatement,默认使用PreparedStatement
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    //这个也是跟jdbc里相对应的,一般采用默认即可
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
   
    //Sql的类型,select/update/insert/delete
    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);

    //不做分析
    // 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);
    
    //生成SqlSource对象,这个对象非常重要,接下来详细分析
    // Parse the SQL (pre:  and  were parsed and removed)
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    String resultSets = context.getStringAttribute("resultSets");
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    
    //自动生成key,这里也不做讨论
    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))
          ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
    }
    //生成MappedStatement对象,并加到Configuration中
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }
上在方法里附件解析一些基本的属性外还有两个主要的部分
  1. SqlSource的构建过程
     SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  2. MappedStatement的构建过程
     builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
            fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
            resultSetTypeEnum, flushCache, useCache, resultOrdered, 
            keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);

SqlSource构建过程

SqlSource接口

/**
 * Represents the content of a mapped statement read from an XML file or an annotation. 
 * It creates the SQL that will be passed to the database out of the input parameter received from the user.
 *
 * @author Clinton Begin
 */
public interface SqlSource {

  BoundSql getBoundSql(Object parameterObject);

}
SqlSource表示从mapper.xml或注解中读取的sql内容,该sql一般还不能都被直接执行,例如

		
	

再来个动态sql的:

附上IfHandler的代码
private class IfHandler implements NodeHandler {
    public void handleNode(XNode nodeToHandle, List targetContents) {
      //解析子节点
      List contents = parseDynamicTags(nodeToHandle);
      //组合
      MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
      String test = nodeToHandle.getStringAttribute("test");
      //生成IfSqlNode
      IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
      targetContents.add(ifSqlNode);
    }
  }

其他的nodeHandler在这里就不讨论了,实现方式与IfHandler差不多。如下两个方法也不在这里做讨论
  1. SqlSource.getBoundSql()方法
  2. SqlNode.apply(DynamicContextcontext)方法
因为这两个方法都是在sql被执行时才调用。在后续的SqlSession实现章节再讨论!

你可能感兴趣的:(Mybatis)