mybatis 映射文件加载

以前一直是做游戏后端开发,现在开始转应用开发,初学mybatis,如果有理解错误的地方,感谢大家的指出

 

第一次搭建SSM框架,对dao层接口和sql的映射文件怎么联系到一起的很好奇,查了一些资料,自己也跟的源码走了一边,大致有个思路,写下来

先看spriing和mybatis的配置文件

 

我们扫描映射文件的时候,是用了一个org.mybatis.spring.SqlSessionFactoryBean的类,并且将每一个xml文件加载到mapperLocations这个变量中,现在打开这个类,跳转到buildSqlSessionFactory这个方法里,可以找到以下代码

进入parse函数

public void parse() {
    //如果configuration对象还没加载xml配置文件(避免重复加载,实际上是确认是否解析了mapper节点的属性及内容,
    //为解析它的子节点如cache、sql、select、resultMap、parameterMap等做准备),
    //则从输入流中解析mapper节点,然后再将resource的状态置为已加载
    if (!configuration.isResourceLoaded(resource)) {
    //解析xml的每一个子节点 configurationElement(parser.evalNode(
"/mapper")); configuration.addLoadedResource(resource); bindMapperForNamespace(); } //解析在configurationElement函数中处理resultMap时其extends属性指向的父对象还没被处理的<resultMap>节点 parsePendingResultMaps(); //解析在configurationElement函数中处理cache-ref时其指向的对象不存在的<cache>节点(如果cache-ref先于其指向的cache节点加载就会出现这种情况) parsePendingChacheRefs(); //同上,如果cache没加载的话处理statement时也会抛出异常 parsePendingStatements(); }
private void configurationElement(XNode context) {
    try {
      //获取mapper节点的namespace属性
      String namespace = context.getStringAttribute("namespace");
      if (namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      //设置当前namespace
      builderAssistant.setCurrentNamespace(namespace);
      //解析mapper的<cache-ref>节点
      cacheRefElement(context.evalNode("cache-ref"));
      //解析mapper的<cache>节点
      cacheElement(context.evalNode("cache"));
      //解析mapper的<parameterMap>节点
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      //解析mapper的<resultMap>节点
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      //解析mapper的<sql>节点
      sqlElement(context.evalNodes("/mapper/sql"));
      //使用XMLStatementBuilder的对象解析mapper的<select>、<insert>、<update>、<delete>节点,
      //mybaits会使用MappedStatement.Builder类build一个MappedStatement对象,
      //所以mybaits中一个sql对应一个MappedStatement
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

configurationElement函数几乎解析了mapper节点下所有子节点,至此mybaits解析了mapper中的所有节点,并将其加入到了Configuration对象中提供给sqlSessionFactory对象随时使用

我们进入buildStatementFromContext函数

  private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }

然后继续进buildStatementFromContext函数

  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
     //解析 statementParser.parseStatementNode(); }
catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } }

对每一个增删改查的节点进行解析,看看parseStatementNode

public void parseStatementNode() {
   //这个id就是我们执行sql时指定的名字
    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);
  }

这个函数解析了各个子节点的具体值 然后传入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);
    }
    //我们生成一个statement 对象
    MappedStatement statement = statementBuilder.build();
    //将statement 对象添加到configuration中
    configuration.addMappedStatement(statement);
    return statement;
  }

看一下configuration.addMappedStatement方法

  public void addMappedStatement(MappedStatement ms) {
    mappedStatements.put(ms.getId(), ms);
  }

我们最后根据id也就是sql执行的名称  存入mappedStatements这个容器中

我们还可以在这个类中找到一个方法

  public MappedStatement getMappedStatement(String id) {
    return this.getMappedStatement(id, true);
  }

这个方法是通过sql名称返回一个MappedStatement对象,而这个对象是可以执行sql的,所以 我们可以猜想,当我们在执行dao层接口的时候,最后一定是调用到这里

以上就是映射文件加载大致过程

参考与感谢

http://blog.csdn.net/flashflight/article/details/43926091

 

posted @ 2018-01-10 15:38  未完待丶续  阅读(406)  评论(0编辑  收藏  举报