MyBatis註解@Select、@Update分析
MyBatis註解@Select、@Update分析
前面幾篇文章分別分析了Mybatis中的Configuration的配置資訊,MyBatis中的Mapper呼叫等等,在分析配置資訊時只是講了如何解析xml中的sql查詢,但是並沒有講怎麼解析Mapper中註解對應的SQL,就是如下:
@ResultMap("BaseResultMap")
@Select("select id, username, password, age, mobile, hotel_address from user where id=#{id};")
User getUser2(@Param("id" ) Long id);
這一節就是具體來說如何解析註解,並將其儲存進mappedStatements中。
1. 解析configuration
我們回到解析mapper的原始碼所在,為mapperElement(root.evalNode(“mappers”));在mapperElement方法中,無論是哪一種條件,最終會有一個addMapper的操作,所以這裡直接看addMapper操作即可,我們轉到最終的addMapper方法。
public <T> void addMapper(Class<T> type) {
if (type. isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<T>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
這個方法中不僅僅做了 knownMappers.put(type, new MapperProxyFactory(type));操作,同時進行了MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);對於註解提供的方法就是在這裡開始解析的,我們進入到parse()方法中。
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
進入到parseStatement中,檢視解析註釋細節,如下:
void parseStatement(Method method) {
final String mappedStatementId = type.getName() + "." + method.getName();
//獲取到具體SQL
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
//獲取到resultMap
String resultMapId = null;
ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
if (resultMapAnnotation != null) {
String[] resultMaps = resultMapAnnotation.value();
StringBuilder sb = new StringBuilder();
for (String resultMap : resultMaps) {
if (sb.length() > 0) {
sb.append(",");
}
sb.append(resultMap);
}
resultMapId = sb.toString();
} else if (isSelect) {
resultMapId = parseResultMap(method);
}
//將對應sqlSource以及其他引數儲存進Map中
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
// ParameterMapID
null,
parameterTypeClass,
resultMapId,
getReturnType(method));
}
}
這裡根據Mapper所在的包名加上對應解析的方法名,最終組裝成mappedStatementId,也就是最終儲存進map中的key。
在組裝完對應的key、value sqlSource等引數後,呼叫addMappedStatement進行進一步的包裝,再看addMappedStatement方法。
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)
.resource(resource)
.fetchSize(fetchSize)
.timeout(timeout)
.statementType(statementType)
.keyGenerator(keyGenerator)
.keyProperty(keyProperty)
.keyColumn(keyColumn)
.databaseId(databaseId)
.lang(lang)
.resultOrdered(resultOrdered)
.resultSets(resultSets)
.resultMaps(getStatementResultMaps(resultMap, resultType, id))
.resultSetType(resultSetType)
.flushCacheRequired(valueOrDefault(flushCache, !isSelect))
.useCache(valueOrDefault(useCache, isSelect))
.cache(currentCache);
ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}
MappedStatement statement = statementBuilder.build();
configuration.addMappedStatement(statement);
return statement;
}
這個方法中在經過將cache、timeout、sql、resource、fetchSize等等引數進行包裝後生成MappedStatement物件,最後通過configuration.addMappedStatement(statement)方法,將statement儲存進mappedStatements Map中。
對註解形式提供的SQL的分析就到此為止了。