Mybatis源码分析(3) -- Mapper文件解析
前面我们分析到<mappers>
的解析最终是由XMLMapperBuilder和MapperAnnotationBuilder解析,XMLMapperBuilder用于解析xml配置文件,MapperAnnotationBuilder用于解析接口中的注解。
Mapper Xml文件解析
在分析XMLMapperBuilder之前我们先了解一下,Mapper Xml配置文件,了解了xml是怎么配置的,然后再源码是如何解析的。
Xml配置文件如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.qdlc.p2p.dal.mybatis.model.mapper.AccountCashMapper" >
<resultMap id="BaseResultMap" type="com.qdlc.p2p.dal.dto.AccountCash" >
<id column="id" property="id" jdbcType="INTEGER" />
<result column="user_id" property="userId" jdbcType="INTEGER" />
<result column="status" property="status" jdbcType="BIT" />
<result column="bank_no" property="bankNo" jdbcType="VARCHAR" />
<result column="bank" property="bank" jdbcType="VARCHAR" />
<result column="branch" property="branch" jdbcType="VARCHAR" />
<result column="money" property="money" jdbcType="DECIMAL" />
</resultMap>
<resultMap id="ModelResultMap" type="com.qdlc.p2p.dal.model.AccountCashModel" extends="BaseResultMap">
<result column="user_name" property="userName" jdbcType="VARCHAR" />
<result column="real_name" property="realName" jdbcType="VARCHAR" />
</resultMap>
<sql id="Base_Column_List" >
id, user_id, status, bank_no, bank, branch, money, amount, fee, fee_bear, type, order_no,
add_time, add_ip, verify_time, verify_user_name, verify_remark, loan_no
</sql>
<!-- 根据ID查找 -->
<select id="findById" resultMap="BaseResultMap" parameterType="java.lang.Long" >
select <include refid="Base_Column_List" /> from qd_account_cash where id = #{id}
</select>
<!-- 根据id,修改记录状态 -->
<update id="updateStatus">
update qd_account_cash set status = #{status} where id = #{id} and status = #{preStatus}
</update>
</mapper>
Mapper Xml文件一般都是这么设置的,在Xml中定义各种需要执行的SQL语句。
了解了xml文件结构之后,我们开始分析XMLMapperBuilder。根据上一篇中的分析,我们知道<mappers>
的解析是调用如下代码:
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
那么,我们就先从XMLMapperBuilder构造方法开始,然后再分析parse()方法。下面是构造方法:
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
configuration, resource, sqlFragments);
}
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
//设置Configuration到父类,顺便也设置了TypeAliasRegistry和TypeHandlerRegistry
super(configuration);
//MapperBuilderAssistant用于构建相关联的对象
this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
//XPathParser解析器很重要,负责解析xml内容
this.parser = parser;
this.sqlFragments = sqlFragments;
this.resource = resource;
}
下面我们再看看parse()方法:
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
//开始解析<mapper>根节点
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingChacheRefs();
parsePendingStatements();
}
private void configurationElement(XNode context) {
try {
//获取命名空间namespace属性
String namespace = context.getStringAttribute("namespace");
if (namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
//设置当前节点的命名空间
builderAssistant.setCurrentNamespace(namespace);
//开始解析各种子节点
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
由于节点类型比较多,就不一一分析了,这里分析下最常用的<resultMap>
和<select>
。也就是:
resultMapElements(context.evalNodes("/mapper/resultMap"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
解析 resultMap
<resultMap>
解析是由下面的方法完成的,负责将<resultMap>
的子节点解析成ResultMapping对象,然后将所有的ResultMapping对象构造成一个完整的ResultMap对象,最后存放到Configuration中去。
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
//获取id属性
String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier());
//获取最终的结果类型
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
//获取extends属性
String extend = resultMapNode.getStringAttribute("extends");
//获取autoMapping属性
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
//通过别名获取真实的类型
Class<?> typeClass = resolveClass(type);
Discriminator discriminator = null;
List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
resultMappings.addAll(additionalResultMappings);
//开始遍历子节点,例如<id>、<result>
List<XNode> resultChildren = resultMapNode.getChildren();
for (XNode resultChild : resultChildren) {
if ("constructor".equals(resultChild.getName())) {
processConstructorElement(resultChild, typeClass, resultMappings);
} else if ("discriminator".equals(resultChild.getName())) {
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
} else {
ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
//解析获得ResultMapping对象放入到resultMappings中去
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
//由ResultMapResolver来解析出ResultMap对象
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}
ResultMapping的获取
XMLMapperBuilder#buildResultMappingFromContext()方法:
该方法负责获取节点中的属性,然后委派给MapperBuilderAssistant去创建ResultMapping。
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, ArrayList<ResultFlag> flags) throws Exception {
//获取各种属性,判断是否有嵌入的<resultMap>
String property = context.getStringAttribute("property");
String column = context.getStringAttribute("column");
String javaType = context.getStringAttribute("javaType");
String jdbcType = context.getStringAttribute("jdbcType");
String nestedSelect = context.getStringAttribute("select");
String nestedResultMap = context.getStringAttribute("resultMap",
processNestedResultMappings(context, Collections.<ResultMapping> emptyList()));
String notNullColumn = context.getStringAttribute("notNullColumn");
String columnPrefix = context.getStringAttribute("columnPrefix");
String typeHandler = context.getStringAttribute("typeHandler");
String resulSet = context.getStringAttribute("resultSet");
String foreignColumn = context.getStringAttribute("foreignColumn");
Class<?> javaTypeClass = resolveClass(javaType);
@SuppressWarnings("unchecked")
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
//委派给builderAssistant去创建ResultMapping
return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resulSet, foreignColumn);
}
MapperBuilderAssistant#buildResultMapping()方法:
该方法实际上就是调用了MapperBuilderAssistant#assembleResultMapping(),下面是这个方法,看看ResultMapping是怎么创建的。
private ResultMapping assembleResultMapping(
Class<?> resultType,
String property,
String column,
Class<?> javaType,
JdbcType jdbcType,
String nestedSelect,
String nestedResultMap,
String notNullColumn,
String columnPrefix,
Class<? extends TypeHandler<?>> typeHandler,
List<ResultFlag> flags,
String resultSet,
String foreignColumn) {
//解析出javaTypeClass,如果javaType没有设置,则通过反射获取
Class<?> javaTypeClass = resolveResultJavaType(resultType, property, javaType);
//获取类型句柄类型
TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler);
//解析组合列名
List<ResultMapping> composites = parseCompositeColumnName(column);
if (composites.size() > 0) column = null;
//构建出ResultMapping对象
ResultMapping.Builder builder = new ResultMapping.Builder(configuration, property, column, javaTypeClass);
builder.jdbcType(jdbcType);
builder.nestedQueryId(applyCurrentNamespace(nestedSelect, true));
builder.nestedResultMapId(applyCurrentNamespace(nestedResultMap, true));
builder.resultSet(resultSet);
builder.typeHandler(typeHandlerInstance);
builder.flags(flags == null ? new ArrayList<ResultFlag>() : flags);
builder.composites(composites);
builder.notNullColumns(parseMultipleColumnNames(notNullColumn));
builder.columnPrefix(columnPrefix);
builder.foreignColumn(foreignColumn);
return builder.build();
}
ResultMap的获取
ResultMapResolver#resolve()方法
该方法返回一个ResultMap对象,也就是最终解析好的<resultMap>
节点对象。
public ResultMap resolve() {
return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping);
}
这个方法很简单,还是委派给MapperBuilderAssistant#addResultMap()方法处理。下面再看看这个方法。 MapperBuilderAssistant#addResultMap()方法。该方法负责将ResultMapping集合构建成ResultMap对象,然后存放到Configuration中去。
public ResultMap addResultMap(String id, Class<?> type, String extend, Discriminator discriminator, List<ResultMapping> resultMappings, Boolean autoMapping) {
//获取id,这里的id是namespace + . + id
id = applyCurrentNamespace(id, false);
extend = applyCurrentNamespace(extend, true);
ResultMap.Builder resultMapBuilder = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping);
if (extend != null) {
//判断是否有父类resultMap
if (!configuration.hasResultMap(extend)) {
throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
}
//获取extends指定的resultMap
ResultMap resultMap = configuration.getResultMap(extend);
List<ResultMapping> extendedResultMappings = new ArrayList<ResultMapping>(resultMap.getResultMappings());
extendedResultMappings.removeAll(resultMappings);
//如果该resultMap使用了constructor,则需要删除父类的constructor
boolean declaresConstructor = false;
for (ResultMapping resultMapping : resultMappings) {
if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
declaresConstructor = true;
break;
}
}
if (declaresConstructor) {
Iterator<ResultMapping> extendedResultMappingsIter = extendedResultMappings.iterator();
while (extendedResultMappingsIter.hasNext()) {
if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) {
extendedResultMappingsIter.remove();
}
}
}
resultMappings.addAll(extendedResultMappings);
}
resultMapBuilder.discriminator(discriminator);
//最终创建出ResultMap,然后存放到Configuration中去
ResultMap resultMap = resultMapBuilder.build();
configuration.addResultMap(resultMap);
return resultMap;
}
解析select、insert、update、delete标签
private void buildStatementFromContext(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
buildStatementFromContext(list, null);
}
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
//List<XNode>参数就是解析出来的节点集合
for (XNode context : list) {
//每一个节点都由XMLStatementBuilder去解析
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}
从上面的代码中可以知道,<select>
节点被解析成了List<XNode>集合,然后将每一个XNode对象交给XMLStatementBuilder去处理,处理的方法是XMLStatementBuilder#parseStatementNode(),接下来我们看看这个方法是如何出列SQL语句的。
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");
//获取SQL语句类型,默认是PREPARED
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
//获取Sql语句类型
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
//是否是select语句,如果是select则使用缓存(flushCache=false,useCache=true)
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
//解析<inclide>节点
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
//解析<selectKey>节点
processSelectKeyNodes(id, parameterTypeClass, langDriver);
//解析Sql语句,封装成SqlSource对象 (<selectKey>和<include>已经被解析和删除了)
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
String resultSets = context.getStringAttribute("resultSets");
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
KeyGenerator keyGenerator;
//获取语句id,并选择key生成器
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对象
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
MappedStatement的创建
MappedStatement是对sql语句的封装,封装了<mappers>
中各个语句标签的定义。MappedStatement包含了定义的参数类型,返回结果类型,SQL语句等等。
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对象
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);
//设置参数Map
setStatementParameterMap(parameterMap, parameterType, statementBuilder);
//设置结果Map
setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
//设置是否需要使用缓存
setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder);
//构建出MappedStatement对象,存放到Configuration中去(存在Map中,以statement.getId()为键)
MappedStatement statement = statementBuilder.build();
configuration.addMappedStatement(statement);
return statement;
}
Mapper 接口解析
Mapper接口的解析是由MapperAnnotationBuilder处理的,该类负责解析接口中的注解类型@Select、@Insert、@Update、@Delete等。下面先看看在接口中是怎么写注解的。
public interface GoodsMapper {
@Select("select * from table_goods where id = #{id}")
Goods selectGood(int id);
}
了解了接口中注解的写法后,在后顾下MapperRegistry#addMapper(Class type)中是如下调用的。
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
也就是说接口的解析交给MapperAnnotationBuilder来处理。那么,到底是怎么解析接口上的注解的呢? 下面开始分析MapperAnnotationBuilder这个类,先分析构造方法,再看parse()方法。
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
//定义一个资源名称
String resource = type.getName().replace('.', '/') + ".java (best guess)";
//又是MapperBuilderAssistant对象,这和XMLMapperBuilder中是一样的
this.assistant = new MapperBuilderAssistant(configuration, resource);
this.configuration = configuration;
//Mapper接口类型
this.type = type;
//mybatis提供的注解
sqlAnnotationTypes.add(Select.class);
sqlAnnotationTypes.add(Insert.class);
sqlAnnotationTypes.add(Update.class);
sqlAnnotationTypes.add(Delete.class);
sqlProviderAnnotationTypes.add(SelectProvider.class);
sqlProviderAnnotationTypes.add(InsertProvider.class);
sqlProviderAnnotationTypes.add(UpdateProvider.class);
sqlProviderAnnotationTypes.add(DeleteProvider.class);
}
构造方法很简单,再看parse()方法。
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
//加载与这个接口相关联的xml文件,也就是说,例如接口为GoodsMapper.java,则会去找相应的GoodsMapper.xml文件
loadXmlResource();
configuration.addLoadedResource(resource);
//设置命名空间为接口名称
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
//遍历接口中所有方法
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
//解析方法上的注解语句
parseStatement(method);
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
那么,关联的xml资源文件是怎么加载的呢? 这就又回到我们在上面分析的Mapper Xml文件加载了。不如看看loadXmlResource()这个方法吧。
private void loadXmlResource() {
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
//找到资源路径,所以这个资源路径需要与包名一致了,否则找不到
String xmlResource = type.getName().replace('.', '/') + ".xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
} catch (IOException e) {
//资源没找到,忽略异常情况
}
//开始解析mapper xml文件
if (inputStream != null) {
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
xmlParser.parse();
}
}
}
接下来,看看接口方法中的注解是如何解析的。也就是parseStatement(method)这个方法。
void parseStatement(Method method) {
//获取方法的参数
Class<?> parameterTypeClass = getParameterType(method);
//解析出SqlSource
LanguageDriver languageDriver = getLanguageDriver(method);
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
if (sqlSource != null) {
//解析出@Options注解
Options options = method.getAnnotation(Options.class);
//语句Id为:接口名 + . + 方法名
final String mappedStatementId = type.getName() + "." + method.getName();
Integer fetchSize = null;
Integer timeout = null;
StatementType statementType = StatementType.PREPARED;
ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
//解析出方法上的语句注解@Select、@Insert、@Update、@Delete,只会解析到一个注解
SqlCommandType sqlCommandType = getSqlCommandType(method);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = !isSelect;
boolean useCache = isSelect;
KeyGenerator keyGenerator;
String keyProperty = "id";
String keyColumn = null;
if (SqlCommandType.INSERT.equals(sqlCommandType)) {
//解析@SelectKey注解
SelectKey selectKey = method.getAnnotation(SelectKey.class);
if (selectKey != null) {
keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
keyProperty = selectKey.keyProperty();
} else {
if (options == null) {
keyGenerator = configuration.isUseGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
} else {
keyGenerator = options.useGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
keyProperty = options.keyProperty();
keyColumn = options.keyColumn();
}
}
} else {
keyGenerator = new NoKeyGenerator();
}
//如果设置了@Options注解,获取@Options中值
if (options != null) {
flushCache = options.flushCache();
useCache = options.useCache();
fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
timeout = options.timeout() > -1 ? options.timeout() : null;
statementType = options.statementType();
resultSetType = options.resultSetType();
}
String resultMapId = null;
//获取@ResultMap注解
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) {
//如果没有设置@ResultMap,则根据方法返回类型来创建ResultMap,然后存放到Configuration中去,返回一个resultMapId(这里很复杂,就不详细分析了)
resultMapId = parseResultMap(method);
}
// 最后,会创建MappedStatement存放到Configuration中去
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
null, // ParameterMapID
parameterTypeClass,
resultMapId, // ResultMapID
getReturnType(method),
resultSetType,
flushCache,
useCache,
false,
keyGenerator,
keyProperty,
keyColumn,
null,
languageDriver,
null);
}
}
总结
- Mybatis将
<resultMap>
最终解析成ResultMap对象,存放到Configuration中去。而<resultMap>
的子节点,比如<id>
、<result>
等都解析成ResultMapping对象。 - Mybatis将
<select>
、<inclue>
、<update>
、<delete>
这些语句节点最终都解析成了MappedStatement对象,该对象包含了所有的语句定义。MappedStatement对象中包含了ParameteMap、ResultMap、SqlSource等对象。