mybatissqlSource时序图(二)

编程

上图是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);

}

}

以上是 mybatissqlSource时序图(二) 的全部内容, 来源链接: utcz.com/z/514372.html

回到顶部