Mybatis源码分析(3) -- Mapper文件解析

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);
	}
}

总结

  1. Mybatis将<resultMap>最终解析成ResultMap对象,存放到Configuration中去。而<resultMap>的子节点,比如<id><result>等都解析成ResultMapping对象。
  2. Mybatis将<select><inclue><update><delete>这些语句节点最终都解析成了MappedStatement对象,该对象包含了所有的语句定义。MappedStatement对象中包含了ParameteMap、ResultMap、SqlSource等对象。

猜你喜欢

转载自my.oschina.net/xiaoqiyiye/blog/1625422
今日推荐