Mybatis源码分析--mybatis之SQL执行

389 阅读2分钟

SQL是怎样执行的呢?

SQL在执行之前,是怎么从xml中拿到的呢?然后放在哪里呢?带着这个疑问,我们debug下源码。

//加载配置文件
SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(inputStream);

其实sqlSessionFactory中就已经准备好SQL了。我们看下截图


SqlSessionFactory是个接口,有两个实现类,默认是DefaultSqlSessionFactory。先看下该类的结构
。可以看到只有一个属性configuration,且是通过构造方法来赋值的。该类还有一个作用就是拿到sqlsession。

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      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.
      }
    }
  }

重点看下XMLConfigBuilder的构造函数

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
//重点。这里会构造一个Configuration对象。
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

我们看下Configuration的源码。
先看下构造函数

public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
 ...//删除一些代码
 //注册一些类对应的别名。xml配置中可体现
 //typeAliasRegistry这个属性是直接new,从而获取的。
  }

现在我们进入parser.parse()

 public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    //解析xml
    //--从根节点configuration
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

进入

//--此方法就是解析configuration节点下的子节点
 //--由此也可看出,我们在configuration下面能配置的节点为以下10个节点
  private void parseConfiguration(XNode root) {
    try {
      // 解析properties元素
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      //解析typeAliases节点
      typeAliasesElement(root.evalNode("typeAliases"));
      // plugins 节点解析
      pluginElement(root.evalNode("plugins"));
      //objectFactory 节点解析
      objectFactoryElement(root.evalNode("objectFactory"));

      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // 解析enviroments元素节点的方法
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //解析typeHandlers节点
      typeHandlerElement(root.evalNode("typeHandlers"));
      
      //sql就是在这里产生的
      // mappers 节点解析
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

进入

 private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        //如果mappers节点的子节点是package, 那么就扫描package下的文件, 注入进configuration
        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, url, class 三选一
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //mapper映射文件都是通过XMLMapperBuilder解析
            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();
    parsePendingStatements();
  }

进入到

  public StaticSqlSource(Configuration configuration, String sql, List<ParameterMapping> parameterMappings) {
    this.sql = sql;
    this.parameterMappings = parameterMappings;
    this.configuration = configuration;
  }

此时已经拿到sql了