Mybatis初始化过程当中,解析parameterMap、resultMap、"select|insert|update|delete"元素,无疑是重头戏。本节将详细分析解析过程。java
元素parameterMap将会解析为ParameterMap对象,该对象包含一个List<ParameterMapping>集合,是one-to-many关系。node
元素resultMap将会解析为ResultMap对象,该对象包含一个List<ResultMapping>集合,是one-to-many关系。sql
元素"select|insert|update|delete"将会被解析为MappedStatement对象,该对象包含了ParameterMap、ResultMap等对象。apache
1. 解析parameterMap元素网络
(Made In Visual Paradigm)数据结构
MapperBuilderAssistant是一个通用构建Mapper辅助类。app
ParameterMapping.Builder用于构建ParameterMapping对象,而ParameterMap.Builder则用于构建ParameterMap对象。fetch
其中resolveTypeHandler()很重要,咱们自定义的TypeHandler要起做用,就靠该方法正确绑定TypeHandler了,后续会单独开一篇关于TypeHandler的文章。ui
下面看看Mabtis解析parameterMap元素的源码。this
private void parameterMapElement(List<XNode> list) throws Exception { for (XNode parameterMapNode : list) { String id = parameterMapNode.getStringAttribute("id"); String type = parameterMapNode.getStringAttribute("type"); Class<?> parameterClass = resolveClass(type); List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter"); List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>(); // 循环得到全部的ParameterMapping集合 for (XNode parameterNode : parameterNodes) { String property = parameterNode.getStringAttribute("property"); String javaType = parameterNode.getStringAttribute("javaType"); String jdbcType = parameterNode.getStringAttribute("jdbcType"); String resultMap = parameterNode.getStringAttribute("resultMap"); String mode = parameterNode.getStringAttribute("mode"); String typeHandler = parameterNode.getStringAttribute("typeHandler"); Integer numericScale = parameterNode.getIntAttribute("numericScale"); ParameterMode modeEnum = resolveParameterMode(mode); Class<?> javaTypeClass = resolveClass(javaType); JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType); @SuppressWarnings("unchecked") Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler); ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale); parameterMappings.add(parameterMapping); } // 建立ParameterMap并加入List<ParameterMapping>,同时把ParameterMap注册到Configuration内。 builderAssistant.addParameterMap(id, parameterClass, parameterMappings); } }
再看看builderAssistant.buildParameterMapping()方法源码。
public ParameterMapping buildParameterMapping( Class<?> parameterType, String property, Class<?> javaType, JdbcType jdbcType, String resultMap, ParameterMode parameterMode, Class<? extends TypeHandler<?>> typeHandler, Integer numericScale) { resultMap = applyCurrentNamespace(resultMap, true); Class<?> javaTypeClass = resolveParameterJavaType(parameterType, property, javaType, jdbcType); TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler); // 下面的一系列方法链,其实都是赋值语句 return new ParameterMapping.Builder(configuration, property, javaTypeClass) .jdbcType(jdbcType) .resultMapId(resultMap) .mode(parameterMode) .numericScale(numericScale) .typeHandler(typeHandlerInstance) .build(); // 内部将调用resolveTypeHandler()方法 }
在看看build()方法。
public ParameterMapping build() { // 给每个ParameterMapping绑定一个TypeHandler,且必须绑定 resolveTypeHandler(); validate(); return parameterMapping; }
一个ParameterMapping,其实就是一个参数属性的封装,从jdbcType到javaType的转换,或者从javaType到jdbcType的转换,全由TypeHandler处理。
到此,一个ParameterMapping就解析结束了。
最后,看看ParameterMap是如何建立并注册的。
public ParameterMap addParameterMap(String id, Class<?> parameterClass, List<ParameterMapping> parameterMappings) { // 处理namespace名称空间 id = applyCurrentNamespace(id, false); ParameterMap parameterMap = new ParameterMap.Builder(configuration, id, parameterClass, parameterMappings).build(); // 注册至Configuration configuration.addParameterMap(parameterMap); return parameterMap; }
public void addParameterMap(ParameterMap pm) { // 放到map中 parameterMaps.put(pm.getId(), pm); }
至此,一个ParameterMap就解析完了。
2. 解析ResultMap元素
(Made In Visual Paradigm)
解析ResultMap元素和解析parameterMap元素是极其类似的,有区别的地方,主要是ResultMap有继承(extends)的功能,以及ResultMap会将List<ResultMapping>再进行一次计算,拆分为多个List<ResultMapping>对象,也就是大集合,分类拆分为多个小集合。
public class ResultMap { // ... private List<ResultMapping> resultMappings; private List<ResultMapping> idResultMappings; private List<ResultMapping> constructorResultMappings; private List<ResultMapping> propertyResultMappings; // ... }
org.apache.ibatis.builder.MapperBuilderAssistant.addResultMap()方法源码。
public ResultMap addResultMap( String id, Class<?> type, String extend, Discriminator discriminator, List<ResultMapping> resultMappings, Boolean autoMapping) { id = applyCurrentNamespace(id, false); extend = applyCurrentNamespace(extend, true); if (extend != null) { if (!configuration.hasResultMap(extend)) { throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'"); } // 处理继承ResultMap属性 ResultMap resultMap = configuration.getResultMap(extend); List<ResultMapping> extendedResultMappings = new ArrayList<ResultMapping>(resultMap.getResultMappings()); // 删除重复元素 extendedResultMappings.removeAll(resultMappings); // Remove parent constructor if this resultMap declares a 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); } ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping) .discriminator(discriminator) .build(); // build()内将大集合,分类拆分为多个小集合。 // 注册到Configuration内 configuration.addResultMap(resultMap); return resultMap; }
至此,一个ResultMap就解析完了。且每个ResultMapping,都绑定了一个TypeHandler,和ParameterMapping同样。
3. 解析"select|insert|update|delete"元素
(Made In Visual Paradigm)
org.apache.ibatis.builder.xml.XMLStatementBuilder.parseStatementNode()源码。
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"); StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); String nodeName = context.getNode().getNodeName(); SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); boolean useCache = context.getBooleanAttribute("useCache", isSelect); boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); // Include Fragments before parsing XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); // Parse selectKey after includes and remove them. processSelectKeyNodes(id, parameterTypeClass, langDriver); // Parse the SQL (pre: <selectKey> and <include> were parsed and removed) SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); String resultSets = context.getStringAttribute("resultSets"); String keyProperty = context.getStringAttribute("keyProperty"); String keyColumn = context.getStringAttribute("keyColumn"); KeyGenerator keyGenerator; 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(); } builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); }
全程平面式的解析,最后生成MappedStatement对象,并注册至Configuration内部。
解析过程当中,出现的一些陌生的配置参数或类,如KeyGenerator、SqlSource、ResultSetType、LanguageDriver、constructor、discriminator等等,后续会逐一进行详细的分析。
总结:解析的过程,因为要处理很是多的配置参数,代码显得很长,可是,抓住Xml元素至Mybatis内部的数据结构映射关系,阅读起来就容易的多了。
版权提示:文章出自开源中国社区,若对文章感兴趣,可关注个人开源中国社区博客(http://my.oschina.net/zudajun)。(通过网络爬虫或转载的文章,常常丢失流程图、时序图,格式错乱等,仍是看原版的比较好)