mybatis源码解析十三之MappedStatement

介绍

MappedStatement类在Mybatis框架中用于表示XML文件中一个sql语句节点,即一个、或者标签。Mybatis框架在初始化阶段会对XML配置文件进行读取,将其中的sql语句节点对象化为一个个MappedStatement对象。

若是使用注解,则类似注解中的@Select等描述。

public final class MappedStatement {


//节点中的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;  

在Mybatis框架中用于表示XML文件中一个sql语句节点,即一个、或者标签。即一个sql语句对应一个MappedStatment对象。

创建

和Configration一样,是在构建SqlSesstionFactory的时候创建的。对象由Configration持有。


new SqlSessionFactoryBuilder().build(inputStream);
调用

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());//parser.parse()这里调用
    。。。
  }

----------
XMLConfigBuilder.java

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

   private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      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) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          ...

-------
XMLMapperBuilder.java

 public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));
      ...
    }

    ...
  }

  private void configurationElement(XNode context) {
    try {
     ...
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

  private void buildStatementFromContext(List list, String requiredDatabaseId) {
    for (XNode context : list) {
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        statementParser.parseStatementNode();//遍历解析 update 等节点
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }


  public void parseStatementNode() {
    ...
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }
  到这里可以知道MappedStatement是在MapperBuilderAssistant中创建的。

-------
MapperBuilderAssistant.java
  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 = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType);
    statementBuilder.resource(resource);
    statementBuilder.fetchSize(fetchSize);
    statementBuilder.statementType(statementType);
    statementBuilder.keyGenerator(keyGenerator);
    statementBuilder.keyProperty(keyProperty);
    statementBuilder.keyColumn(keyColumn);
    statementBuilder.databaseId(databaseId);
    statementBuilder.lang(lang);
    statementBuilder.resultOrdered(resultOrdered);
    statementBuilder.resulSets(resultSets);
    setStatementTimeout(timeout, statementBuilder);

    setStatementParameterMap(parameterMap, parameterType, statementBuilder);
    setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
    setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder);

    MappedStatement statement = statementBuilder.build();
    configuration.addMappedStatement(statement);
    return statement;
  }

 -----
 Configration.java

  public void addMappedStatement(MappedStatement ms) {
    mappedStatements.put(ms.getId(), ms);
  }

从上面代码可以看到。在解析Insert等节点的时候创建的MappedStaement,在MapperBuilderAssistant中创建。创建完成之后保存在Configration的map中。

StrictMap

MappedStatement使用的map是在Configration中定义的
StrictMap(主要关注put和map):

protected static class StrictMap extends HashMap {
 public V put(String key, V value) {
            if (this.containsKey(key)) {
                throw new IllegalArgumentException(this.name + " already contains value for " + key);
            } else {
                if (key.contains(".")) {
                    String shortKey = this.getShortName(key);
                    if (super.get(shortKey) == null) {
                        super.put(shortKey, value);
                    } else {
                        super.put(shortKey, new Configuration.StrictMap.Ambiguity(shortKey));
                    }
                }

                return super.put(key, value);
            }
        }

        public V get(Object key) {
            V value = super.get(key);
            if (value == null) {
                throw new IllegalArgumentException(this.name + " does not contain value for " + key);
            } else if (value instanceof Configuration.StrictMap.Ambiguity) {
                throw new IllegalArgumentException(((Configuration.StrictMap.Ambiguity)value).getSubject() + " is ambiguous in " + this.name + " (try using the full name including the namespace, or rename one of the entries)");
            } else {
                return value;
            }
        }

        private String getShortName(String key) {
            String[] keyParts = key.split("\\.");
            return keyParts[keyParts.length - 1];
        }

可以看到在put的时候会对key进行判重。即我们在xml或者注解中定义的namesapce.id这组合id必须是唯一的。

获取

根据节点中的id属性加要命名空间来获取

public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      List result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
      return result;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

从这里看出通过调用Configration的 MappedStatement ms = configuration.getMappedStatement(statement)来获取对应的MappedStaement。

你可能感兴趣的:(mybatis,mybatis源码分析)