Mybatis总结(一)SqlSessionFactory初始化过程(源码分析)

版权声明:本文为博主原创文章,转载添加原文链接 https://blog.csdn.net/qq_34190023/article/details/80873736

SqlSessionFactoryBuilder根据传入的数据流生成Configuration对象,然后根据Configuration对象创建默认的SqlSessionFactory实例。

Mybatis初始化要经过简单的以下几步:

1. 调用SqlSessionFactoryBuilder对象的build(inputStream)方法;

2. SqlSessionFactoryBuilder会根据输入流inputStream等信息创建XMLConfigBuilder对象;

3. SqlSessionFactoryBuilder调用XMLConfigBuilder对象的parse()方法;

4. XMLConfigBuilder对象返回Configuration对象;

5. SqlSessionFactoryBuilder根据Configuration对象创建一个DefaultSessionFactory对象;

6. SqlSessionFactoryBuilder返回 DefaultSessionFactory对象给Client,供Client使用。


一、SqlSessionFactory的创建:建造者模式:

SqlSessionFactoryBuilder相关的代码如下所示: 

public class SqlSessionFactoryBuilder {
  public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }
  public SqlSessionFactory build(Reader reader, String environment) {
    return build(reader, environment, null);
  }
  public SqlSessionFactory build(Reader reader, Properties properties) {
    return build(reader, null, properties);
  }
  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); // Don4j解析器
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }
  public SqlSessionFactory build(InputStream inputStream, String environment) {
    return build(inputStream, environment, null);
  }
  public SqlSessionFactory build(InputStream inputStream, Properties properties) {
    return build(inputStream, null, properties);
  }
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {

//创建XMLConfigBuilder对象用来解析XML配置文件,生成Configuration对象
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

//将XML配置文件内的信息解析成Java对象Configuration对象。根据Configuration对象创建出SqlSessionFactory对象
      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.
      }
    }
  }
//内部通过Configuration对象来创建SqlSessionFactory,也可以自己通过API构造好Configuration对象,调用此方法创建SqlSessionFactory    
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }
}:

SqlSessionFactoryBuilder用于创建SqlSessionFactory。通过传入的xml配置文件,进行解析,保存为内部的Configuration对象,最终调用build方法,new出一个DefaultSqlSessionFactory。

本过程设计的对象:

SqlSessionFactoryBuilder SqlSessionFactory的构造器,用于创建SqlSessionFactory,采用了Builder设计模式

Configuration :该对象是mybatis-config.xml文件中所有mybatis配置信息

SqlSessionFactorySqlSession工厂类,以工厂形式创建SqlSession对象,采用了Factory工厂设计模式

XmlConfigParser :负责将mybatis-config.xml配置文件解析成Configuration对象,共SqlSessonFactoryBuilder使用,创建SqlSessionFactory


二、解析xml配置为Configuration对象的过程:

这个过程比较复杂,主要是解析配置文件,然后保存到map中。

SqlSessionFactoryBuilder执行build()方法,调用了XMLConfigBuilderparse()方法,然后返回了Configuration对象

过程: XMLConfigBuilder会将XML配置文件的信息转换为Document对象,而XML配置定义文件DTD转换成XMLMapperEntityResolver对象,然后将二者封装到XpathParser对象中,XpathParser的作用是提供根据Xpath表达式获取基本的DOM节点Node信息的操作。

XMLConfigBuilder会将xml以及配置的mapper文件转化成对应的document对象,将配置定义文件dtd加载成EntityResolve对象,组成XPathParser对象,然后根据XPath表达式访问Node节点

XMLConfigBuilder调用parse()方法:从XPathParser中取出<configuration>节点对应的Node对象,然后解析此Node节点的子Nodeproperties, settings,typeAliases,typeHandlers, objectFactory, objectWrapperFactory, plugins, environments,databaseIdProvider,mappers

源码分析:

public Configuration parse() {

if (parsed) {
   throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  parseConfiguration(parser.evalNode("/configuration"));  //
  return configuration;

}


//解析 "/configuration"节点下的子节点信息,然后将解析的结果设置到Configuration对象中


private void parseConfiguration(XNode root) {
try {
 propertiesElement(root.evalNode("properties"));//1.首先处理properties 节点
 Properties settings = settingsAsProperties(root.evalNode("settings")); 
 loadCustomVfs(settings);
 typeAliasesElement(root.evalNode("typeAliases"));//2.处理typeAliases
 pluginElement(root.evalNode("plugins"));     //3.处理插件
 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")); // 这里解析出SQL。其实也就是mapper文件或mapper注解
} catch (Exception e) {
 throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}

解析mapper的过程:

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);  // 这里添加了mappers
      } 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.");
        }
      }
    }
  }
}

这里的Configuration对象在循环中add了mapper

Configuration

public class Configuration {
protected Environment environment;

。。。。

protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

public void addMappers(String packageName, Class<?> superType) {

  mapperRegistry.addMappers(packageName, superType);

}
public void addMappers(String packageName) {
  mapperRegistry.addMappers(packageName);
}

可以看到在Configuration中,使用mapperRegistry进行添加

MapperRegistry

public void addMappers(String packageName) {
  addMappers(packageName, Object.class);
}

public void addMappers(String packageName, Class<?> superType) {
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
  resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
  Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
  for (Class<?> mapperClass : mapperSet) {
    addMapper(mapperClass);
  }
}

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<T>(type));
      // It's important that the type is added before the parser is run
      // otherwise the binding may automatically be attempted by the
      // mapper parser. If the type is already known, it won't try.
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

MapperRegistry调用用了MapperAnnotationBuilder进行解析

 MapperAnnotationBuilder

MapperAnnotationBuilder:
public class MapperAnnotationBuilder {
  private final Set<Class<? extends Annotation>> sqlAnnotationTypes = new HashSet<Class<? extends Annotation>>();
  private final Set<Class<? extends Annotation>> sqlProviderAnnotationTypes = new HashSet<Class<? extends Annotation>>();
  private Configuration configuration;
  private MapperBuilderAssistant assistant;
  private Class<?> type;
// 这里将注解的情况加入进去,添加注解的,
  public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
    String resource = type.getName().replace('.', '/') + ".java (best guess)";
    this.assistant = new MapperBuilderAssistant(configuration, resource);
    this.configuration = configuration;
    this.type = type;   // 这个type是接口
    sqlAnnotationTypes.add(Select.class);
    sqlAnnotationTypes.add(Insert.class);
    sqlAnnotationTypes.add(Update.class);
    sqlAnnotationTypes.add(Delete.class);
    sqlProviderAnnotationTypes.add(SelectProvider.class);
    sqlProviderAnnotationTypes.add(InsertProvider.class);
    sqlProviderAnnotationTypes.add(UpdateProvider.class);
    sqlProviderAnnotationTypes.add(DeleteProvider.class);
  }
  public void parse() {
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) {
      loadXmlResource();  // 首先这里是先加载xml。如果发现没有xml,不抛出异常,这里是忽略这个异常
      configuration.addLoadedResource(resource);  // 把加载好的资源放在loadedResources这个set容器中,表示已经加载
      assistant.setCurrentNamespace(type.getName());  // 设置当前的namespace为当前类名
      parseCache();
      parseCacheRef();
      Method[] methods = type.getMethods();  // 找到接口的所有方法
      for (Method method : methods) {
        try {
          // issue #237
          if (!method.isBridge()) {  // 解析方法
            parseStatement(method);   // 这里对参数进行了解析
          }
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    parsePendingMethods();
  }

这个过程先是解析了加载了xml。这里涉及特别精巧,在加载xml的过程中,如果没有xml,那么xml解析器会抛出异常,而mybatis将抛出的异常进行捕获,然后忽略了这个异常,等下一个解析注解。

 private void loadXmlResource() {
    // Spring may not know the real resource name so we check a flag
    // to prevent loading again a resource twice
    // this flag is set at XMLMapperBuilder#bindMapperForNamespace
    if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
      String xmlResource = type.getName().replace('.', '/') + ".xml";  // 这里就是默认的xml路径了,如果没有配置,那么默认需要同包下的xml
      InputStream inputStream = null;
      try {
        inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
      } catch (IOException e) {  // 如果没有,则忽略这个异常
        // ignore, resource is not required
      }
      if (inputStream != null) {
        XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
        xmlParser.parse();
      }
    }
  }

如果有xml,则使用XMLMapperBuilder 进行解析xml。后面讨论

回到解析方法:

 public void parse() {
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) {
      loadXmlResource();  // 首先这里是先加载xml。如果发现没有xml,不抛出异常,这里是忽略这个异常
      configuration.addLoadedResource(resource);  // 把加载好的资源放在loadedResources这个set容器中,表示已经加载
      assistant.setCurrentNamespace(type.getName());  // 设置当前的namespace为当前类名
      parseCache();
      parseCacheRef();
      Method[] methods = type.getMethods();  // 找到接口的所有方法
      for (Method method : methods) {
        try {
          // issue #237
          if (!method.isBridge()) {  // 解析方法
            parseStatement(method);   // 这里对参数进行了解析
          }
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    parsePendingMethods();
  }

加载xml后,会再次去遍历注解,如果xml和注解同时配置,根据代码先解析xml,后解析注解,可以得知,注解是会覆盖xml的同名方法的。

接下来会对方法参数进行解析。

// 对方法的参数进行解析
void parseStatement(Method method) {
  Class<?> parameterTypeClass = getParameterType(method);
  LanguageDriver languageDriver = getLanguageDriver(method);
  SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);  //这里会对方法以及参数进行解析,最终封装为一个很重要的类-SqlSource
  if (sqlSource != null) {
    Options options = method.getAnnotation(Options.class);
    final String mappedStatementId = type.getName() + "." + method.getName();
    Integer fetchSize = null;
    Integer timeout = null;
    StatementType statementType = StatementType.PREPARED;
    ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
    SqlCommandType sqlCommandType = getSqlCommandType(method);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = !isSelect;
    boolean useCache = isSelect;

    KeyGenerator keyGenerator;
    String keyProperty = "id";
    String keyColumn = null;
    if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
      // first check for SelectKey annotation - that overrides everything else
      SelectKey selectKey = method.getAnnotation(SelectKey.class);
      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 {
        keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
        keyProperty = options.keyProperty();
        keyColumn = options.keyColumn();
      }
    } else {
      keyGenerator = NoKeyGenerator.INSTANCE;
    }
    if (options != null) {
      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();
      resultSetType = options.resultSetType();
    }

    String resultMapId = null;
    ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
    if (resultMapAnnotation != null) {
      String[] resultMaps = resultMapAnnotation.value();
      StringBuilder sb = new StringBuilder();
      for (String resultMap : resultMaps) {
        if (sb.length() > 0) {
          sb.append(",");
        }
        sb.append(resultMap);
      }
      resultMapId = sb.toString();
    } else if (isSelect) {
      resultMapId = parseResultMap(method);
    }

    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,
        // DatabaseID
        null,
        languageDriver,
        // ResultSets
        options != null ? nullOrEmpty(options.resultSets()) : null);
  }
}

SqlSource的获取:

private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
  try {
    Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
    Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
    if (sqlAnnotationType != null) {
      if (sqlProviderAnnotationType != null) {
        throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());
      }
      Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
      final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
      return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
    } else if (sqlProviderAnnotationType != null) {
      Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
      return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation);
    }
    return null;
  } catch (Exception e) {
    throw new BuilderException("Could not find value method on SQL annotation.  Cause: " + e, e);
  }
}

ProviderSqlSource类是SqlSource的子类。存储了封装的SQL信息

public class ProviderSqlSource implements SqlSource {
public ProviderSqlSource(Configuration config, Object provider) {
  String providerMethodName;
  try {
    this.sqlSourceParser = new SqlSourceBuilder(config);
    this.providerType = (Class<?>) provider.getClass().getMethod("type").invoke(provider);
    providerMethodName = (String) provider.getClass().getMethod("method").invoke(provider);

    for (Method m : this.providerType.getMethods()) {
      if (providerMethodName.equals(m.getName())) {
        if (m.getReturnType() == String.class) {
          if (providerMethod != null){
            throw new BuilderException("Error creating SqlSource for SqlProvider. Method '"
                    + providerMethodName + "' is found multiple in SqlProvider '" + this.providerType.getName()
                    + "'. Sql provider method can not overload.");
          }
          this.providerMethod = m;
          this.providerMethodArgumentNames = new ParamNameResolver(config, m).getNames();  // 参数名
        }
      }
    }
  } catch (BuilderException e) {
    throw e;
  } catch (Exception e) {
    throw new BuilderException("Error creating SqlSource for SqlProvider.  Cause: " + e, e);
  }
  if (this.providerMethod == null) {
    throw new BuilderException("Error creating SqlSource for SqlProvider. Method '"
        + providerMethodName + "' not found in SqlProvider '" + this.providerType.getName() + "'.");
  }
}

ParamNameResolver是获取方法参数名称的类:

public class ParamNameResolver {
public ParamNameResolver(Configuration config, Method method) {
  final Class<?>[] paramTypes = method.getParameterTypes();  // 这里会先获取方法的参数类型
  final Annotation[][] paramAnnotations = method.getParameterAnnotations();
  final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
  int paramCount = paramAnnotations.length;
  // get names from @Param annotations
  for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
    if (isSpecialParameter(paramTypes[paramIndex])) {  // 跳过特殊的参数类型,也就是判断参数的类是否是RowBounds或者ResultHandler,如果是,就跳过
      // skip special parameters
      continue;
    }
    String name = null;
    for (Annotation annotation : paramAnnotations[paramIndex]) {
      if (annotation instanceof Param) {   // 这里解析了Param
        hasParamAnnotation = true;
        name = ((Param) annotation).value();
        break;
      }
    }
    if (name == null) {   // 没有@Param注解
      // @Param was not specified.
      if (config.isUseActualParamName()) {
        name = getActualParamName(method, paramIndex);  // 这里会获取到真实的参数名或者是arg+索引值
      }
      if (name == null) {    //如果还是没有取到值
        // use the parameter index as the name ("0", "1", ...)
        // gcode issue #71
        name = String.valueOf(map.size());
      }
    }
    map.put(paramIndex, name);  // 参数索引 + 参数名
  }
  names = Collections.unmodifiableSortedMap(map);
}

可以看到,这里会先判断方法参数是否有@Param注解,如果有,则获取值,然后保存到map中,如果没有,判断isUseActualParamName。如果判断成功,会获取真实参数名或者是arg+索引值。如果不是,则命名默认为索引值。


参数的解析就到这里一段落。接下来是之前没有解决的xml解析的问题。


动态SQL是如何解析出来的?

XMLMapperBuilderparse方法:

public void parse() {

  if (!configuration.isResourceLoaded(resource)) {

    configurationElement(parser.evalNode("/mapper"));

    configuration.addLoadedResource(resource);

    bindMapperForNamespace();

  }

  parsePendingResultMaps();

  parsePendingCacheRefs();

  parsePendingStatements();

}
private void configurationElement(XNode context) {

  try {

    String namespace = context.getStringAttribute("namespace");

    if (namespace == null || namespace.equals("")) {

      throw new BuilderException("Mapper's namespace cannot be empty");

    }

    builderAssistant.setCurrentNamespace(namespace);

    cacheRefElement(context.evalNode("cache-ref"));

    cacheElement(context.evalNode("cache"));

    parameterMapElement(context.evalNodes("/mapper/parameterMap"));

    resultMapElements(context.evalNodes("/mapper/resultMap"));

    sqlElement(context.evalNodes("/mapper/sql"));

    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));

  } catch (Exception e) {

    throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);

  }

}
private void buildStatementFromContext(List<XNode> list) {

  if (configuration.getDatabaseId() != null) {

    buildStatementFromContext(list, configuration.getDatabaseId());

  }

  buildStatementFromContext(list, null);

}
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);

    }

  }

}

最终是使用 XMLStatementBuilder进行解析元素

XMLStatementBuilder

public class XMLStatementBuilder extends BaseBuilder {
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()));  // 默认是使用PreparedStatement,如果配置了,就使用配置的

  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) 这里解析了SQL

  SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); // 这里创建了sql

  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);

}

首先,可以看到它从上下文中获取了配置文件配置的信息。根据这些信息决定创建sql的策略。

如StatementType如果没有配置的话,默认就是PreparedStatement。

关键点在于langDriver的createSqlSource方法。这里创建了SqlSource

LanguageDriver是一个就接口,xmlLanguageDriver是其实现类

public interface LanguageDriver {
  ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql); 
  SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType); 
  SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);
}
看一下XMLLanguageDriver是如何实现的
public class XMLLanguageDriver implements LanguageDriver {
  @Override
  public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
  }
  @Override
  public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
    XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
    return builder.parseScriptNode();
  }
  @Override
  public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
    // issue #3
    if (script.startsWith("<script>")) {
      XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver());
      return createSqlSource(configuration, parser.evalNode("/script"), parameterType);
    } else {
      // issue #127
      script = PropertyParser.parse(script, configuration.getVariables());
      TextSqlNode textSqlNode = new TextSqlNode(script);
      if (textSqlNode.isDynamic()) {
        return new DynamicSqlSource(configuration, textSqlNode);
      } else {
        return new RawSqlSource(configuration, script, parameterType);
      }
    }
  }
}
之前调用的是其第二个方法,可以看到,这里又使用了建造模式,又是使用 XMLScriptBuilder 进行构建SqlSource

XMLScriptBuilder是SqlSource创建的关键类。

// 将xml中的sql封装为动态的SqlSource,最后会解析成静态的
public SqlSource parseScriptNode() {
  List<SqlNode> contents = parseDynamicTags(context);
  MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
  SqlSource sqlSource = null;
  if (isDynamic) {
    sqlSource = new DynamicSqlSource(configuration, rootSqlNode);  // 创建动态的sql
  } else {
    sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);  // 创建原始的sql
  }
  return sqlSource;
}
List<SqlNode> parseDynamicTags(XNode node) {
  List<SqlNode> contents = new ArrayList<SqlNode>();
  NodeList children = node.getNode().getChildNodes();
  for (int i = 0; i < children.getLength(); i++) {
    XNode child = node.newXNode(children.item(i));
    if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
      String data = child.getStringBody("");
      TextSqlNode textSqlNode = new TextSqlNode(data);
      if (textSqlNode.isDynamic()) {
        contents.add(textSqlNode);
        isDynamic = true;
      } else {
        contents.add(new StaticTextSqlNode(data));
      }
    } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
      String nodeName = child.getNode().getNodeName();
      NodeHandler handler = nodeHandlers(nodeName);
      if (handler == null) {
        throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
      }
      handler.handleNode(child, contents);
      isDynamic = true;
    }
  }
  return contents;
}
NodeHandler nodeHandlers(String nodeName) {
  Map<String, NodeHandler> map = new HashMap<String, NodeHandler>();
  map.put("trim", new TrimHandler());
  map.put("where", new WhereHandler());
  map.put("set", new SetHandler());
  map.put("foreach", new ForEachHandler());
  map.put("if", new IfHandler());
  map.put("choose", new ChooseHandler());
  map.put("when", new IfHandler());
  map.put("otherwise", new OtherwiseHandler());
  map.put("bind", new BindHandler());
  return map.get(nodeName);
}

这里进行了动态SQL的解析,可以看到nodeHandlers方法,对不同的元素,进行不同的Handler进行解析。

最终根据判断isDynamic判断是否创建动态的SQL或静态的。最后返回这个SqlSource。


对于SqlSource的解析:

SqlSource是获取了,但是还需要经过解析,

以动态的SqlSource为例子

public class DynamicSqlSource implements SqlSource {

  private Configuration configuration;
  private SqlNode rootSqlNode;

  public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
    this.configuration = configuration;
    this.rootSqlNode = rootSqlNode;
  }
  // 在会话创建时,会被调用,然后会生成一个静态的sql,带?参数的【也就是去掉参数】
  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    rootSqlNode.apply(context);
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
      boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
    }
    return boundSql;
  }

}

sqlSourceBuilder

public class SqlSourceBuilder extends BaseBuilder {
private static final String parameterProperties = "javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName";
public SqlSourceBuilder(Configuration configuration) {
  super(configuration);
}
public SqlSource parse(String originalSql,Class<?> parameterType,Map<String,Object> additionalParameters) {
  ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
  GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
  String sql = parser.parse(originalSql);
  return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}
GenericTokenParser 
public class GenericTokenParser {
public String parse(String text) {
  if (text == null || text.isEmpty()) {  return "";  }
  char[] src = text.toCharArray();
  int offset = 0;
  // search open token
  int start = text.indexOf(openToken, offset);
  if (start == -1) {    return text;  }
  final StringBuilder builder = new StringBuilder();
  StringBuilder expression = null;
  while (start > -1) {
    if (start > 0 && src[start - 1] == '\\') {
      // this open token is escaped. remove the backslash and continue.
      builder.append(src, offset, start - offset - 1).append(openToken);
      offset = start + openToken.length();
    } else {
      // found open token. let's search close token.
      if (expression == null) {   expression = new StringBuilder(); } 
else {    expression.setLength(0);   }
      builder.append(src, offset, start - offset);
      offset = start + openToken.length();
      int end = text.indexOf(closeToken, offset);
      while (end > -1) {
        if (end > offset && src[end - 1] == '\\') {
          // this close token is escaped. remove the backslash and continue.
          expression.append(src, offset, end - offset - 1).append(closeToken);
          offset = end + closeToken.length();
          end = text.indexOf(closeToken, offset);
        } else {
          expression.append(src, offset, end - offset);
          offset = end + closeToken.length();
          break;
        }
      }
      if (end == -1) {
        // close token was not found.
        builder.append(src, start, src.length - start);
        offset = src.length;
      } else {
        builder.append(handler.handleToken(expression.toString()));  // 添加?
        offset = end + closeToken.length();
      }
    }
    start = text.indexOf(openToken, offset);
  }
  if (offset < src.length) {
    builder.append(src, offset, src.length - offset);
  }
  return builder.toString();
}

这里做了很多很复杂的事情,但是handler的handlerToken就是将用户在SQL里面写的#{}替换成?,最终由PreparedStatement编织最终传给数据库的SQL。


SqlSourceBuilder 

public class SqlSourceBuilder extends BaseBuilder {
private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {
@Override
public String handleToken(String content) {
  parameterMappings.add(buildParameterMapping(content));
  return "?";
}
private ParameterMapping buildParameterMapping(String content) {
  Map<String, String> propertiesMap = parseParameterMapping(content);
  String property = propertiesMap.get("property");
  Class<?> propertyType;
  if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params
    propertyType = metaParameters.getGetterType(property);
  } else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
    propertyType = parameterType;
  } else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
    propertyType = java.sql.ResultSet.class;
  } else if (property != null) {
    MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
    if (metaClass.hasGetter(property)) {
      propertyType = metaClass.getGetterType(property);
    } else {
      propertyType = Object.class;
    }
  } else {
    propertyType = Object.class;
  }
  ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
  Class<?> javaType = propertyType;
  String typeHandlerAlias = null;
  for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
    String name = entry.getKey();
    String value = entry.getValue();
    if ("javaType".equals(name)) {
      javaType = resolveClass(value);
      builder.javaType(javaType);
    } else if ("jdbcType".equals(name)) {
      builder.jdbcType(resolveJdbcType(value));
    } else if ("mode".equals(name)) {
      builder.mode(resolveParameterMode(value));
    } else if ("numericScale".equals(name)) {
      builder.numericScale(Integer.valueOf(value));
    } else if ("resultMap".equals(name)) {
      builder.resultMapId(value);
    } else if ("typeHandler".equals(name)) {
      typeHandlerAlias = value;
    } else if ("jdbcTypeName".equals(name)) {
      builder.jdbcTypeName(value);
    } else if ("property".equals(name)) {
      // Do Nothing
    } else if ("expression".equals(name)) {
      throw new BuilderException("Expression based parameters are not supported yet");
    } else {
      throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}.  Valid properties are " + parameterProperties);
    }
  }
  if (typeHandlerAlias != null) {
    builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
  }
  return builder.build();
}
private Map<String, String> parseParameterMapping(String content) {
  try {
    return new ParameterExpression(content);
  } catch (BuilderException ex) {
    throw ex;
  } catch (Exception ex) {
    throw new BuilderException("Parsing error was found in mapping #{" + content + "}.  Check syntax #{property|(expression), var1=value1, var2=value2, ...} ", ex);
  }
}
}
}

在整个过程比较复杂,其实也就是不断解析xml或注解的过程


流程图

猜你喜欢

转载自blog.csdn.net/qq_34190023/article/details/80873736
今日推荐