博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mybatis3.3.x技术内幕(十):Mybatis初始化流程(下)
阅读量:6814 次
发布时间:2019-06-26

本文共 10204 字,大约阅读时间需要 34 分钟。

hot3.png

Mybatis初始化过程中,解析parameterMap、resultMap、"select|insert|update|delete"元素,无疑是重头戏。本节将详细分析解析过程。

元素parameterMap将会解析为ParameterMap对象,该对象包含一个List<ParameterMapping>集合,是one-to-many关系。

元素resultMap将会解析为ResultMap对象,该对象包含一个List<ResultMapping>集合,是one-to-many关系

元素"select|insert|update|delete"将会被解析为MappedStatement对象,该对象包含了ParameterMap、ResultMap等对象。

1. 解析parameterMap元素

161124_aIyE_2727738.jpg

(Made In Visual  Paradigm)

MapperBuilderAssistant是一个通用构建Mapper辅助类。

ParameterMapping.Builder用于构建ParameterMapping对象,而ParameterMap.Builder则用于构建ParameterMap对象。

其中resolveTypeHandler()很重要,我们自定义的TypeHandler要起作用,就靠该方法正确绑定TypeHandler了,后续会单独开一篇关于TypeHandler的文章。

下面看看Mabtis解析parameterMap元素的源码。

  private void parameterMapElement(List
 list) throws Exception {    for (XNode parameterMapNode : list) {      String id = parameterMapNode.getStringAttribute("id");      String type = parameterMapNode.getStringAttribute("type");      Class
 parameterClass = resolveClass(type);      List
 parameterNodes = parameterMapNode.evalNodes("parameter");      List
 parameterMappings = new ArrayList
();      // 循环获得所有的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
> typeHandlerClass = (Class
>) resolveClass(typeHandler);        ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);        parameterMappings.add(parameterMapping);      }      // 创建ParameterMap并加入List
,同时把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
> 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
 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元素

164702_cxsc_2727738.jpg

(Made In Visual  Paradigm)

解析ResultMap元素和解析parameterMap元素是极其相似的,有区别的地方,主要是ResultMap有继承(extends)的功能,以及ResultMap会将List<ResultMapping>再进行一次计算,拆分为多个List<ResultMapping>对象,也就是大集合,分类拆分为多个小集合。

public class ResultMap {  // ...  private List
 resultMappings;  private List
 idResultMappings;  private List
 constructorResultMappings;   private List
 propertyResultMappings;  // ...}

org.apache.ibatis.builder.MapperBuilderAssistant.addResultMap()方法源码。

  public ResultMap addResultMap(      String id,      Class
 type,      String extend,      Discriminator discriminator,      List
 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
 extendedResultMappings = new ArrayList
(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
 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"元素

173525_pVvO_2727738.jpg

(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: 
 and 
 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)。(经过网络爬虫或转载的文章,经常丢失流程图、时序图,格式错乱等,还是看原版的比较好)

转载于:https://my.oschina.net/zudajun/blog/669868

你可能感兴趣的文章
LiveUSB像光驱LiveCD一样启动
查看>>
Linux利用sendmail和fetion发送报警通知
查看>>
C/C++中一次性执行多个DOS命令
查看>>
(转载)经典SQL语句大全3-技巧篇
查看>>
在SSIS包中使用 Checkpoint从失败处重新启动包
查看>>
关于项目自动化测试架构的改良计划 - 解析XInclude标记
查看>>
Powershell DSC 5.0 - Push 模式
查看>>
Provisioning Services 7.8 入门系列教程之一 部署学习环境介绍
查看>>
xen虚拟化实战系列(十一)之xen虚拟机磁盘文件挂载
查看>>
技术分享连载(四十五)
查看>>
hive lock监控
查看>>
NICs bonding in openfiler 2.3
查看>>
bash shell退格键乱码
查看>>
分治法实现大数相乘 C#实现
查看>>
Msg 9002 The transaction log for database '' is full
查看>>
高斯求和1+2+3+······+n-1+n=?
查看>>
Android学习启动篇
查看>>
《Pro ASP.NET MVC 3 Framework》学习笔记之二十五【Filters】
查看>>
一步一步学Silverlight 2系列(32):图形图像综合实例—“功夫之王”剧照播放...
查看>>
Adobe Indesign怎么让图片衬于文字下方?
查看>>