上图是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); } }
来源:oschina
链接:https://my.oschina.net/u/257801/blog/3193672