zoukankan      html  css  js  c++  java
  • Mybatis3源码笔记(五)mapperElement

    1.四种解析mapper方式 : package,resource,url,class。

      <mappers>
        <mapper resource="org/apache/ibatis/builder/BlogMapper.xml"/>
        <mapper url="file:./src/test/java/org/apache/ibatis/builder/NestedBlogMapper.xml"/>
        <mapper class="org.apache.ibatis.builder.CachedAuthorMapper"/>
        <package name="org.apache.ibatis.builder.mapper"/>
      </mappers>
    
    private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
          for (XNode child : parent.getChildren()) {
            //package引入
            if ("package".equals(child.getName())) {
              String mapperPackage = child.getStringAttribute("name");
              configuration.addMappers(mapperPackage);
            } else {
              String resource = child.getStringAttribute("resource");
              String url = child.getStringAttribute("url");
              String mapperClass = child.getStringAttribute("class");
              //resource引入
              if (resource != null && url == null && mapperClass == null) {
                ErrorContext.instance().resource(resource);
                try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
                  XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                  mapperParser.parse();
                }
              } else if (resource == null && url != null && mapperClass == null) {
                //url引入
                ErrorContext.instance().resource(url);
                try (InputStream inputStream = Resources.getUrlAsStream(url)) {
                  XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                  mapperParser.parse();
                }
              } else if (resource == null && url == null && mapperClass != null) {
                //mapperClass引入
                Class<?> mapperInterface = Resources.classForName(mapperClass);
                configuration.addMapper(mapperInterface);
              } else {
                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
              }
            }
          }
        }
      }
    

    2.跟一下package引入。

      public <T> void addMapper(Class<T> type) {
        //必须是接口
        if (type.isInterface()) {
          //重复性检查
          if (hasMapper(type)) {
            throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
          }
          boolean loadCompleted = false;
          try {
            knownMappers.put(type, new MapperProxyFactory<>(type));
            //解析Mapper接口
            MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
            parser.parse();
            loadCompleted = true;
          } finally {
            if (!loadCompleted) {
              knownMappers.remove(type);
            }
          }
        }
      }
    
      public void parse() {
        String resource = type.toString();
        //判断该resource有没有引入
        if (!configuration.isResourceLoaded(resource)) {
          //加载该命名空间下对应的xml
          loadXmlResource();
          configuration.addLoadedResource(resource);
          assistant.setCurrentNamespace(type.getName());
          //解析二级缓存注解CacheNamespace
          parseCache();
          //解析二级缓存注解CacheNamespaceRef
          parseCacheRef();
          for (Method method : type.getMethods()) {
            //检查下method类型,不能是桥接方法和接口中的默认方法
            if (!canHaveStatement(method)) {
              continue;
            }
            //select操作解析@select,@SelectProvider注释方法中的带有@ResultMap的方法
            if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()
                && method.getAnnotation(ResultMap.class) == null) {
              parseResultMap(method);
            }
            try {
              //解析Statement
              parseStatement(method);
            } catch (IncompleteElementException e) {
              configuration.addIncompleteMethod(new MethodResolver(this, method));
            }
          }
        }
        //解析IncompleteMethod,解析失败的方法。
        parsePendingMethods();
      }
    
     void parseStatement(Method method) {
         //取得方法的参数类型
        final Class<?> parameterTypeClass = getParameterType(method);
        //根据@Lang取得方法的LanguageDriver,默认是XMLLanguageDriver
        final LanguageDriver languageDriver = getLanguageDriver(method);
        //解析所有合法的statement注解,譬如@Select, @Update, @Insert, @Delete, @SelectProvider...
        getAnnotationWrapper(method, true, statementAnnotationTypes).ifPresent(statementAnnotation -> {
          //生成SqlSource
          final SqlSource sqlSource = buildSqlSource(statementAnnotation.getAnnotation(), parameterTypeClass, languageDriver, method);
          final SqlCommandType sqlCommandType = statementAnnotation.getSqlCommandType();
          //取得@Options注解
          final Options options = getAnnotationWrapper(method, false, Options.class).map(x -> (Options) x.getAnnotation()).orElse(null);
          //设置mappedStatementId,方法的全路径
          final String mappedStatementId = type.getName() + "." + method.getName();
    
          final KeyGenerator keyGenerator;
          String keyProperty = null;
          String keyColumn = null;
          //插入和更新需要涉及到主键自动生成
          if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
            // @SelectKey注解生成主键
            SelectKey selectKey = getAnnotationWrapper(method, false, SelectKey.class).map(x -> (SelectKey) x.getAnnotation()).orElse(null);
            if (selectKey != null) {
              keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
              keyProperty = selectKey.keyProperty();
            } else if (options == null) {
              //默认模式
              keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
            } else {
              //根据@Options中的value值实现自定义设置
              keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
              keyProperty = options.keyProperty();
              keyColumn = options.keyColumn();
            }
          } else {
            keyGenerator = NoKeyGenerator.INSTANCE;
          }
    
          Integer fetchSize = null;
          Integer timeout = null;
          StatementType statementType = StatementType.PREPARED;
          ResultSetType resultSetType = configuration.getDefaultResultSetType();
          boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
          boolean flushCache = !isSelect;
          boolean useCache = isSelect;
          if (options != null) {
            //设置对应的options
            if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
              flushCache = true;
            } else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
              flushCache = false;
            }
            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();
            if (options.resultSetType() != ResultSetType.DEFAULT) {
              resultSetType = options.resultSetType();
            }
          }
    
          String resultMapId = null;
          if (isSelect) {
            ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
            if (resultMapAnnotation != null) {
              //根据 @ResultMap({ "xx", "xxx" })设置resultMapId
              resultMapId = String.join(",", resultMapAnnotation.value());
            } else {
              //没配置@ResultMap则走generateResultMapName方法生成resultMapId(方法的详细路径+“-”+参数类型)
              resultMapId = generateResultMapName(method);
            }
          }
          //生成Statement`
          assistant.addMappedStatement(
            mappedStatementId,
            sqlSource,
            statementType,
            sqlCommandType,
            fetchSize,
            timeout,
            // ParameterMapID
            null,
            parameterTypeClass,
            resultMapId,
            getReturnType(method),
            resultSetType,
            flushCache,
            useCache,
            // TODO gcode issue #577
            false,
            keyGenerator,
            keyProperty,
            keyColumn,
            statementAnnotation.getDatabaseId(),
            languageDriver,
            // ResultSets
            options != null ? nullOrEmpty(options.resultSets()) : null);
        });
      }
    

    3.其它的引入方式resource和url方式,就是先解析一波xml文件,大同小异。

      public void parse() {
        if (!configuration.isResourceLoaded(resource)) {
          //解析mapper.xml文件
          configurationElement(parser.evalNode("/mapper"));
          configuration.addLoadedResource(resource);
          //解析对应空间下的mapper接口(跟上面的package方法一样的解析过程)
          bindMapperForNamespace();
        }
    
        parsePendingResultMaps();
        parsePendingCacheRefs();
        parsePendingStatements();
      }
    
      private void configurationElement(XNode context) {
        try {
          String namespace = context.getStringAttribute("namespace");
          if (namespace == null || namespace.isEmpty()) {
            throw new BuilderException("Mapper's namespace cannot be empty");
          }
          //设置命名空间
          builderAssistant.setCurrentNamespace(namespace);
          //设置二级缓存
          cacheRefElement(context.evalNode("cache-ref"));
          cacheElement(context.evalNode("cache"));
          //解析parameterMap
          parameterMapElement(context.evalNodes("/mapper/parameterMap"));
          //解析resultMap
          resultMapElements(context.evalNodes("/mapper/resultMap"));
          //解析sql片段
          sqlElement(context.evalNodes("/mapper/sql"));
          //解析真正的sql语句
          buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
        }
      }
    

    mapper的解析分注解如@Select和xml两种解析,看代码我们可以看出来,同一个方法都是先找xml,后分析注解,根据configuration.isResourceLoaded来判断是否已经解析,防止重复解析。

  • 相关阅读:
    SpringCloud学习教程
    Google浏览器插件推荐
    谷歌身份验证器使用
    js控制某个div在页面加载完成5秒后隐藏
    通过城市联动实时将地址显示到text中
    百度地图通过地址查询并且定位
    yii2.0验证码的两种实现方式
    yii2.0 中数据查询中 or、in、between 及session的使用
    Calling unknown method: appmodulesmobilecontrollersCompanyController::redirect()
    页面权限跳转
  • 原文地址:https://www.cnblogs.com/zhou-yuan/p/14582617.html
Copyright © 2011-2022 走看看