上图是sqlsource在mybatis中建立的时序图;node
如下会经过源码的方式将时序图进行深刻的说明;sql
1.XMLStatementBuilder对xxxMapper.xml中对每一个CURD进行解析成MappedStatement对象express
如:apache
<select id="selectRawWithInclude" resultType="Name" lang="raw"> SELECT firstName, lastName <include refid="include"/> WHERE lastName LIKE #{name} </select>
lang属性指定该MapperStatement中的sqlSource属性是由哪类LanguageDriver进行markdown
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进行封装;mybatis
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对象进行封装;app
XMLScriptBuilder类很精简,初始化sql 标签的map;定义生成SqlNode的NodeHandler接口ide
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); } }
以上流程进行总结:
1.经过lang属性从languageRegistry中获取LanguageDriver,languageDriver经过xml or annotation解析获取DynamicSqlSource or RawSqlSource
2.以DynamicSqlSource为例,对sql语句中对标签转化为SqlNode对象,而后进行解析,将解析后的数据存放于DynamicContext
3.根据sql语句中的#{}值解析成ParameterMapping,并以configuration、sql(解析后的静态语句)、值的映射(ParameterMapping)组装StaticSqlSource
4.最后转化为BoundSql对象
================================================================
在学习的过程当中,DynamicContext比较有意思,如今说一下这个类
DynamicContext类是在执行多个sqlNode的入参,将每一个SqlNode.apply方法执行完的数据存入于DynamicContext;
DynamicContext的构造函数 public DynamicContext(Configuration configuration, Object parameterObject) { if (parameterObject != null && !(parameterObject instanceof Map)) { MetaObject metaObject = configuration.newMetaObject(parameterObject); boolean existsTypeHandler = configuration.getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass()); bindings = new ContextMap(metaObject, existsTypeHandler); } else { bindings = new ContextMap(null, false); } bindings.put(PARAMETER_OBJECT_KEY, parameterObject); bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId()); }
将解析过程当中产生的属性(如:ForEachSqlNode)存放于bindings,bindings继承于HashMap
并且给bindings注入了propertyAccessor
static { OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor()); }
当获取contextMap中当元素时,会进入到ContextAccessor中先进行获取,如获取属性的切面操做,对执行方法也是能够的,注入methodAccessor
static class ContextAccessor implements PropertyAccessor { @Override public Object getProperty(Map context, Object target, Object name) { Map map = (Map) target; Object result = map.get(name); if (map.containsKey(name) || result != null) { return result; } Object parameterObject = map.get(PARAMETER_OBJECT_KEY); if (parameterObject instanceof Map) { return ((Map)parameterObject).get(name); } return null; } ... }
获取方法
public static Object getValue(String expression, Object root) { try { Map context = Ognl.createDefaultContext(root, MEMBER_ACCESS, CLASS_RESOLVER, null); return Ognl.getValue(parseExpression(expression), context, root); } catch (OgnlException e) { throw new BuilderException("Error evaluating expression '" + expression + "'. Cause: " + e, e); } }
建立OgnlContext,将expression进行解析成property,而后会调用到自定义的PropertyAccessor中
Ognl其实主要功能是对集合、类对象属性表达式的解析,如:
ContextMap bindings = new ContextMap(null, false); OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor()); bindings.put("xxx", "zzz"); bindings.put("yyy", "aaa"); OgnlContext context = new OgnlContext(null,null,new DefaultMemberAccess(true)); context.put("bindings", bindings); Object obj = Ognl.getValue("#bindings.xxx", context, context.getRoot()); System.out.println(obj); // output: "zzz"
方法也是如此,当注入类MethodAccessor时,当经过表达式“bindings.put()”,也会执行MethodAccessor关于put方法的切面