以下是使用mybatis默认设置PreparedStatement执行insert的简略时序图,省略了XMLConfigBuilder.parse()的解析细节,缓存和事务等的使用,可以和源码对照着看
XMLConfigBuilder:解析mybatis配置文件
XMLMapperBuilder: 解析mapper配置文件
XMLStatementBuilder: 解析SQL语句标签(此处得到SqlSource对象)
注意:
1. KeyGenerator的设置也是在XMLConfigBuilder.parse()过程中进行的,每个mapper文件中的每个不同id的标签(insert | update等),可能对应的KeyGeneratior类型也不一样
2. 对SQL语句解析结果的存储会放到SqlSource中,最终执行的SQL语句会放到BoundSql中,对过滤条件的处理在SqlSource中的SqlNode的apply()方法执行,并通过SqlSourceBuilder.parse()来获取最终给数据库的预编译SQL语句
3. XMLConfigBuilder:解析mapper文件,对于类似<insert><if></if></insert>标签嵌套的标签,生成的SqlSource是一个DynamicSqlSource,里边对于SQL语句和标签保存的是SqlNode节点。其他的生成的SqlSource是RawSqlSource
4. 在第21步,Configuration.newStatementHandler(Executor, MappedStatement, ParamObject, RowBounds, ResultHandler, BoundSql)中会调用到BaseStatementHandler的构造函数,源码如下:
1 public abstract class BaseStatementHandler implements StatementHandler { 2 3 protected final Configuration configuration; 4 protected final ObjectFactory objectFactory; 5 protected final TypeHandlerRegistry typeHandlerRegistry; 6 protected final ResultSetHandler resultSetHandler; 7 protected final ParameterHandler parameterHandler; 8 9 protected final Executor executor; 10 protected final MappedStatement mappedStatement; 11 protected final RowBounds rowBounds; 12 13 protected BoundSql boundSql; 14 15 protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 16 this.configuration = mappedStatement.getConfiguration(); 17 this.executor = executor; 18 this.mappedStatement = mappedStatement; 19 this.rowBounds = rowBounds; 20 21 this.typeHandlerRegistry = configuration.getTypeHandlerRegistry(); 22 this.objectFactory = configuration.getObjectFactory(); 23 24 if (boundSql == null) { // issue #435, get the key before calculating the statement 25 generateKeys(parameterObject); 26 boundSql = mappedStatement.getBoundSql(parameterObject); //此处会转换动态SQL语句,根据传递的参数和判断条件,得出最终的SQL语句(拥有占位符) 27 } 28 29 this.boundSql = boundSql; 30 31 this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql); 32 this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql); 33 } 34 } 35 36 public final class MappedStatement { 37 public BoundSql getBoundSql(Object parameterObject) { 38 BoundSql boundSql = sqlSource.getBoundSql(parameterObject); 39 List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); 40 if (parameterMappings == null || parameterMappings.size() <= 0) { 41 boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject); 42 } 43 44 // check for nested result maps in parameter mappings (issue #30) 45 for (ParameterMapping pm : boundSql.getParameterMappings()) { 46 String rmId = pm.getResultMapId(); 47 if (rmId != null) { 48 ResultMap rm = configuration.getResultMap(rmId); 49 if (rm != null) { 50 hasNestedResultMaps |= rm.hasNestedResultMaps(); 51 } 52 } 53 } 54 55 return boundSql; 56 } 57 } 58 59 public class DynamicSqlSource implements SqlSource {
private Configuration configuration;
private SqlNode rootSqlNode;
60 public BoundSql getBoundSql(Object parameterObject) { 61 DynamicContext context = new DynamicContext(configuration, parameterObject); 62 rootSqlNode.apply(context); 63 SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); 64 Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); 65 SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); 66 BoundSql boundSql = sqlSource.getBoundSql(parameterObject); 67 for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) { 68 boundSql.setAdditionalParameter(entry.getKey(), entry.getValue()); 69 } 70 return boundSql; 71 } 72 }
总结:
XMLConfigBuilder知识对mapper.xml做解析,并保存解析的结果,解析的结果可能是静态SQL(没有过滤条件,就一条原生SQL语句(包含占位符)),也可能是动态SQL(拥有过滤条件,需要在执行的时候根据参数来过滤)
等到真正开始执行的时候(第21步(创建StatementHandler))时,才会根据传入的参数对过滤条件进行处理,得到最终要预处理的SQL语句(包含占位符):
这一步包含两个最重要的步骤:1. 处理mybatis提供的SqlNode(if,foreach,where,set等) 2. 使用占位符?替换掉固定格式#{xx}