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中。
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。