mybatis sqlSource时序图(二)

老子叫甜甜 提交于 2020-03-14 01:42:45

上图是sqlsource在mybatis中创建的时序图;

以下会通过源码的方式将时序图进行深入的说明;

1.XMLStatementBuilder对xxxMapper.xml中对每个CURD进行解析成MappedStatement对象

如:

  <select id="selectRawWithInclude" resultType="Name" lang="raw">
    SELECT firstName, lastName
    <include refid="include"/>
    WHERE lastName LIKE #{name}
  </select>

lang属性指定该MapperStatement中的sqlSource属性是由哪类LanguageDriver进行

public interface LanguageDriver {

  /**
   * 创建ParameterHandler,将入参的数据传入jdbc statement ex:PrepareStatement中的 '?'
   */
  ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);

  /**
   * 通过解析xml中的curd node数据创建SqlSource
   */
  SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);

  /**
   * 通过解析Mapper interface method上的注解数据创建SqlSource
   */
  SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);

}



lang=xml -> XMLLanguageDriver:创建动态sql的LanguageDriver
lang=raw -> RawLanguageDriver:创建静态sql的LanguageDriver

在解析MapperStatement时,通过typeAliasRegistry解析lang值获取LanguageDriver;
  private LanguageDriver getLanguageDriver(String lang) {
    Class<? extends LanguageDriver> langClass = null;
    if (lang != null) {
      langClass = resolveClass(lang);
    }
    return configuration.getLanguageDriver(langClass);
  }

===
  protected <T> Class<? extends T> resolveClass(String alias) {
    if (alias == null) {
      return null;
    }
    try {
      return resolveAlias(alias);
    } catch (Exception e) {
      throw new BuilderException("Error resolving class. Cause: " + e, e);
    }
  }
===
  protected <T> Class<? extends T> resolveAlias(String alias) {
    return typeAliasRegistry.resolveAlias(alias);
  }

===
然后由Configuration中的languageRegistry获取LanguageDriver对象
  public LanguageDriver getLanguageDriver(Class<? extends LanguageDriver> langClass) {
    if (langClass == null) {
      return languageRegistry.getDefaultDriver();
    }
    languageRegistry.register(langClass);
    return languageRegistry.getDriver(langClass);
  }

languageRegistry初始化
    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

2.调用XMLScriptBuilder解析sql语句,将trim/where/set/foreach/if/choose/when/otherwise/bind标签解析成相应的SqlNode对象,最后由MixedSqlNode进行封装;

public class MixedSqlNode implements SqlNode {
  private final List<SqlNode> contents;

  public MixedSqlNode(List<SqlNode> contents) {
    this.contents = contents;
  }

  @Override
  public boolean apply(DynamicContext context) {
    contents.forEach(node -> node.apply(context));
    return true;
  }
}

将sql语句中的标签通过实现NodeHandler接口的类生成SqlNode对象;由MixedSqlNode对象进行封装;

XMLScriptBuilder类很精简,初始化sql 标签的map;定义生成SqlNode的NodeHandler接口

nodeHandlerMap.put("trim", new TrimHandler());
nodeHandlerMap.put("where", new WhereHandler());
nodeHandlerMap.put("set", new SetHandler());
nodeHandlerMap.put("foreach", new ForEachHandler());
nodeHandlerMap.put("if", new IfHandler());
nodeHandlerMap.put("choose", new ChooseHandler());
nodeHandlerMap.put("when", new IfHandler());
nodeHandlerMap.put("otherwise", new OtherwiseHandler());
nodeHandlerMap.put("bind", new BindHandler());

private interface NodeHandler {
  void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);
}

3.将sql语句中的标签进行解析时,判断是否是动态语句

XMLScriptBuilder#
public SqlSource parseScriptNode() {
  MixedSqlNode rootSqlNode = parseDynamicTags(context);
  SqlSource sqlSource;
  if (isDynamic) {
    sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
  } else {
    sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
  }
  return sqlSource;
}

如果是动态的则返回DynamicSqlSource,反之则返回RawSqlSource

4/5/6.MappStatement对象中包含属性SqlSource;

7.在调用SqlSession.select方法时,调用MappedStatement.getBoundSql(parameterObject)获取BoundSql对象

MappedStatement#
public BoundSql getBoundSql(Object parameterObject) {
  BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
  ...
  return boundSql;
}
DynamicSqlSource#
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);
  context.getBindings().forEach(boundSql::setAdditionalParameter);
  return boundSql;
}

1.创建DynamicContext对象
2.调用rootSqlNode.apply(context)方法,将SqlNode对象的apply方法,将sql动态语句转成静态语句
3.将解析后的sql、入参类型、context.getBindings()值传入SqlSourceBuilder,进行解析返回StaticSqlSource
4.传入入参对象,返回boundSql

8.调用SqlSourceBuilder.parse方法,

SqlSourceBuilder#
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());
}

针对#{id, typeHandler=org.apache.ibatis.submitted.uuid_test.UUIDTypeHandler}这种情况,
1.将#{}的内容转化为ParameterExpression对象
2.将additionalParameters中的值转换为MetaObject;通过属性名获取属性的类型
3.通过根据类型判断是否有类型处理器
4.将对象中的内容进行解析,转换为ParameterMapping

通过以上方式,将动态sql语句中的#{}值转化为ParameterMapping;进行生成StaticSqlSource对象

9/10.调用StaticSqlSource.getBoundSql方法;

public class StaticSqlSource implements SqlSource {
  ...
  @Override
  public BoundSql getBoundSql(Object parameterObject) {
    return new BoundSql(configuration, sql, parameterMappings, parameterObject);
  }

}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!