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