增删改查标签解析生成SQL过程
增删改查标签解析生成SQL过程
目录
一、将增删改查中的写的语句解析成SqlSource
对于xml中的每个增删改查的解析
来一个官方案例:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
首先需要将标签<增删改查>解析成多个SqlNode,然后将SqlNode转换成对应的SqlSource
这一步发生在org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode中的
/**
* 通过class org.apache.ibatis.scripting.xmltags.XMLLanguageDriver来解析我们的
* sql脚本对象 . 解析SqlNode. 注意, 只是解析成一个个的SqlNode, 并不会完全解析sql,因为这个时候参数都没确定,动态sql无法解析
*/
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
public SqlSource parseScriptNode() {
/**
* 递归解析-组合设计模式 selectById这个sql元素会解析成
* 1层 MixedSqlNode <SELECT>
* 2层 WhereSqlNode <WHERE>
* 3层 IfSqlNode <IF>
* test="条件表达式"
*
* contexts= sql语句分: 1.TextSqlNode 带${} 2.StaticTextSqlNode
*/
MixedSqlNode rootSqlNode = parseDynamicTags(context);
SqlSource sqlSource;
if (isDynamic) {
// 动态Sql源
// 动态Sql 就是还需要后续执行时根据传入参数动态解析Sql(因为有<if>等,还要拼接${}sql)
// 和参数ParameterMappings 也会在后续执行解析,因为动态条件肯定会有动态参数
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
// 静态Sql源 如果没有动态标签(<if>、<where>等) 以及 没有${} 就是静态Sql源
// 静态Sql 就是在这里就解析了Sql 和参数ParameterMappings 后续执行就不用解析了
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
// 其实他们的区别就是动态sql 需要在查询的时候解析 因为有动态sql 和拼接${}
// 静态sql 已经在这里确定好sql. 和参数ParameterMapping,
return sqlSource;
}
在这里分别生成DynamicSqlSource和RawSqlSource,最终生成SqlSource进行保存。
二、动态SqlSource的解析成BoundSQL
因为动态SqlSource的解析需要通过参数来确定具体的执行的是哪个SQL,所以参数需要发挥作用。
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//获取sql
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
对于查询来说:
public BoundSql getBoundSql(Object parameterObject) {
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}
// check for nested result maps in parameter mappings (issue #30)
for (ParameterMapping pm : boundSql.getParameterMappings()) {
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = configuration.getResultMap(rmId);
if (rm != null) {
hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
return boundSql;
}
public BoundSql getBoundSql(Object parameterObject) {
// 根据参数创建一个动态解析上下文
DynamicContext context = new DynamicContext(configuration, parameterObject);
// 1归 责任链 处理一个个SqlNode 编译出一个完整sql
rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
// 2.接下来处理 处理sql中的#{...}
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
// 怎么处理呢? 很简单,就是拿到#{}中的内容 封装为parameterMapper, 替换成?
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
context.getBindings().forEach(boundSql::setAdditionalParameter);
return boundSql;
}
各种各样的标签解析,这里不做过多赘述。
三、完成的增删改查解析生成MappedStatement
上面只是针对
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
这里做了个解析,但是
从理论中来,到实践中去,最终回归理论