zoukankan      html  css  js  c++  java
  • Mybatis源码之StatementType

      在mybatis中StatementType的值决定了由什么对象来执行我们的SQL语句。本文来分析下在mybatis中具体是怎么处理的。

    StatementType

    1.StatementType枚举

      StatementType是一个枚举类型。如下:

    /**
     * @author Clinton Begin
     */
    public enum StatementType {
      STATEMENT, PREPARED, CALLABLE
    }
    
    选项 说明
    STATEMENT 对应于Statement对象,有SQL注入的风险
    PREPARED PreparedStatement,预编译处理
    CALLABLE CallableStatement一般调用存储过程的时候使用

    2.设置StatementType

      我们可以在映射文件中通过‘statementType’属性设置,如下:

    在这里插入图片描述

      注意默认是'PREPARED ',通过源码分析我们可以查看到。

    3.源码跟踪查看

      我们要从源码中发现StatementType的处理,应该是从加载配置文件的地方开始查看,所以我们从'new SqlSessionFactoryBuilder().build(in);'这行代码入手

    //InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    InputStream in = TestMybatis.class.getResourceAsStream("/mybatis-config.xml");
    // 获取SqlSessionFactory对象
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    

    进入build方法中查看

      public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
          XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
          // 配置文件解析的重点是 'parser.parse()'这行代码
          return build(parser.parse());
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
          ErrorContext.instance().reset();
          try {
            inputStream.close();
          } catch (IOException e) {
            // Intentionally ignore. Prefer previous error.
          }
        }
      }
    

    进入parse方法中

      public Configuration parse() {
        if (parsed) {
          throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        parsed = true;
        // 进入
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
      }
    
      private void parseConfiguration(XNode root) {
        try {
          //issue #117 read properties first
          propertiesElement(root.evalNode("properties"));
          Properties settings = settingsAsProperties(root.evalNode("settings"));
          loadCustomVfs(settings);
          typeAliasesElement(root.evalNode("typeAliases"));
          pluginElement(root.evalNode("plugins"));
          objectFactoryElement(root.evalNode("objectFactory"));
          objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
          reflectorFactoryElement(root.evalNode("reflectorFactory"));
          settingsElement(settings);
          // read it after objectFactory and objectWrapperFactory issue #631
          environmentsElement(root.evalNode("environments"));
          databaseIdProviderElement(root.evalNode("databaseIdProvider"));
          typeHandlerElement(root.evalNode("typeHandlers"));
          // 上面都是解析主配置文件中子节点,直接进入本方法
          mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
      }
    

    mapperElement方法,该方法会根据我们在mappers中不同的配置做出不同的处理。我们直接看resource的方式

      private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
          for (XNode child : parent.getChildren()) {
            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");
              if (resource != null && url == null && mapperClass == null) {
                ErrorContext.instance().resource(resource);
                // 加载配置文件
                InputStream inputStream = Resources.getResourceAsStream(resource);
                XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                // 解析配置文件的方法 进入
                mapperParser.parse();
              } else if (resource == null && url != null && mapperClass == null) {
                ErrorContext.instance().resource(url);
                InputStream inputStream = Resources.getUrlAsStream(url);
                XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                mapperParser.parse();
              } else if (resource == null && url == null && mapperClass != null) {
                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.");
              }
            }
          }
        }
      }
    

    进入 mapperParser.parse() 方法查看

      public void parse() {
        if (!configuration.isResourceLoaded(resource)) {
        	// 解析根节点
          configurationElement(parser.evalNode("/mapper"));
          configuration.addLoadedResource(resource);
          // 绑定名称空间
          bindMapperForNamespace();
        }
    
        parsePendingResultMaps();
        parsePendingCacheRefs();
        // 重点本方法,解析未处理的Statements
        parsePendingStatements();
      }
    

    进入parsePendingStatements方法:

      private void parsePendingStatements() {
        Collection<XMLStatementBuilder> incompleteStatements = configuration.getIncompleteStatements();
        synchronized (incompleteStatements) {
          Iterator<XMLStatementBuilder> iter = incompleteStatements.iterator();
          while (iter.hasNext()) {
            try {
            	// 解析我们的select|update|insert|delete节点
              iter.next().parseStatementNode();
              iter.remove();
            } catch (IncompleteElementException e) {
              // Statement is still missing a resource...
            }
          }
        }
      }
    

    进入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))
           ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
     }
    
     builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
         fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
         resultSetTypeEnum, flushCache, useCache, resultOrdered, 
         keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
    }
    

    在这里插入图片描述

    在这里插入图片描述

    最后解析完这个select|insert|update|delete节点的信息被封装到了一个MapperedStatement对象中。

    在这里插入图片描述

    至此StatementType解析阶段的内容分析完了~

  • 相关阅读:
    转:Windows 7下安装CentOS双系统
    STL学习总结之<迭代器>
    转:linux静态库与动态库
    指向类成员和成员函数的指针
    STL学习总结之<仿函数>
    转:Linux Crontab 定时任务 命令详解
    转: 解决 Redhat 出现”This system is not registered with RHN”更新
    IOS 判断设备屏幕尺寸、分辨率
    IOS 文件管理共通函数整理
    IOS 编译ffmpeg For SDK6.1,模拟器、armv7、armv7s均可使用
  • 原文地址:https://www.cnblogs.com/dengpengbo/p/10785133.html
Copyright © 2011-2022 走看看