mybatis源码解析十三之MappedStatement

时间:2025-03-18 21:22:56

介绍

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<ResultMap> 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(());//()这里调用
    。。。
  }

----------


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

   private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : ()) {
        if ("package".equals(())) {
          String mapperPackage = ("name");
          (mapperPackage);
        } else {
          String resource = ("resource");
          String url = ("url");
          String mapperClass = ("class");
          if (resource != null && url == null && mapperClass == null) {
            ().resource(resource);
            InputStream inputStream = (resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, ());
            ();
          ...

-------


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

    ...
  }

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

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


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

-------

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

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

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

    MappedStatement statement = ();
    (statement);
    return statement;
  }

 -----
 

  public void addMappedStatement(MappedStatement ms) {
    ((), ms);
  }

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

StrictMap

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

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

                return (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 ) {
                throw new IllegalArgumentException((()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 = ("\\.");
            return keyParts[ - 1];
        }

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

获取

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

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = (statement);
      List<E> result = (ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
      return result;
    } catch (Exception e) {
      throw ("Error querying database.  Cause: " + e, e);
    } finally {
      ().reset();
    }
  }

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