目录
一、执行原理
使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼接 sql,以此来完成动态 sql 的功能。
二、主要类源码
MyBatis 中一些关于动态 SQL 的接口和类。
1、SqlNode 接口
简单理解就是 xml 中的每个标签,比如 sql 的 update、if 标签:
public interface SqlNode {
boolean apply(DynamicContext context);
}
2、SqlSource
Sql 源接口,代表从 xml 文件或注解映射的 sql 内容,主要就是用于创建 BoundSql,有实现类 DynamicSqlSource(动态 Sql 源),StaticSqlSource(静态 Sql 源)等:
public interface SqlSource {
BoundSql getBoundSql(Object parameterObject);
}
3、BoundSql 类
封装 mybatis 最终产生 sql 的类,包括 sql 语句,参数,参数源数据等参数:
public class BoundSql {
private final String sql;
private final List<ParameterMapping> parameterMappings;
private final Object parameterObject;
private final Map<String, Object> additionalParameters;
private final MetaObject metaParameters;
}
4、BaseBuilder 接口及其实现类
这些 Builder 的作用就是用于构造 sql。
分析下其中4个 Builder:
1)XMLConfigBuilder
解析 mybatis 中 configLocation 属性中的全局 xml 文件,内部会使用 XMLMapperBuilder 解析各个 xml 文件。
2)XMLMapperBuilder
遍历 mybatis 中 mapperLocations 属性中的 xml 文件中每个节点的 Builder,比如 user.xml,内部会使用 XMLStatementBuilder 处理 xml 中的每个节点。
3)XMLStatementBuilder
解析 xml 文件中各个节点,比如 select、insert、update、delete 节点,内部会使用 XMLScriptBuilder 处理节点的 sql 部分,遍历产生的数据会丢到 Configuration 的 mappedStatements 中。
4)XMLScriptBuilder
解析 xml 中各个节点 sql 部分的 Builder。
LanguageDriver 接口及其实现类,该接口主要的作用就是构造 sql。
三、源码刨析
1、 XMLMapperBuilder 中的 parse 方法:
public void parse() {
// 判断当前 Mapper 是否已经加载过
if (!configuration.isResourceLoaded(resource)) {
// 解析 '<mapper />' 节点,根据 Xpath 解析 mapper 节点
configurationElement(parser.evalNode("/mapper"));
// 标记该 Mapper 已经加载过
configuration.addLoadedResource(resource);
// 绑定 Mapper
bindMapperForNamespace();
}
// 解析待定的 <resultMap /> 节点
parsePendingResultMaps();
// 解析待定的 <cache-ref /> 节点
parsePendingCacheRefs();
// 解析待定的 SQL 语句的节点
parsePendingStatements();
}
// 解析 mapper 节点,根据 Xpath 解析 mapper 节点
private void configurationElement(XNode context) {
try {
// 获取 mapper 节点的 namespace 属性,如 com.lagou.mapper.UserMapper
String namespace = context.getStringAttribute(