mapper.xml中的sql会在MybatisAutoConfiguration中构建SqlSessionFactory时得到解析,如果有where if之类的标签会被解析成DynamicSqlSource,如果是普通的查询语句(select * from departments where department_id=#{depId})则会被解析成RawSqlSource,这个属性会被存在configuration的mappedstatements属性中,属性名称为sqlSource。然后执行查询时,会从sqlSource中拿到对应的原始sql,然后再进行解析,得到完整的sql。方法就是对应的SqlSource类的getBoundsql方法。
查询为例
查询方法由SqlSessionTemplate中创建DefaultSqlSession,然后执行DefaultSqlSession的selectList方法。
然后继续执行查询方法
执行executor.query方法,executor为CachingExecutor类型。
如果引入了pagehelper依赖,这里执行前会进入分页拦截器
关键方法是这个getBoundsql,就是通过这个方法获取到sql的。
BoundSql
public class BoundSql {
// 一个完整的 SQL 语句,可能会包含问号 ? 占位符
private final String sql;
// 参数映射列表,SQL 中的每个 #{xxx} 占位符都会被解析成相应的 ParameterMapping 对象
private final List<ParameterMapping> parameterMappings;
// 运行时参数,即用户传入的参数,比如 Article 对象,或是其他的参数
private final Object parameterObject;
// 附加参数集合,用于存储一些额外的信息,比如 datebaseId 等
private final Map<String, Object> additionalParameters;
// additionalParameters 的元信息对象
private final MetaObject metaParameters;
}
getBoundSql方法
public BoundSql getBoundSql(Object parameterObject) {
//获取BoundSql
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;
}
1.动态解析
这里如果参数是${}这样的,就会走DynamicSqlSource类的getBoundSql
然后执行MixedSqlNode类apply方法把 ${}的内容替换成真实数据
传入的是原始sql
TextSqlNode类的apply方法
GenericTokenParser类的parse方法
关键方法
TextSqlNode类的handleToken方法
从context拿到值,根据传进来的key
这时拿到的sql就是完整的sql了
解析if where 动态标签
DynamicSqlSource类的getBoundSql方法
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(configuration, parameterObject);
//拿到原始sql 解析if标签 如果判断正确 会拼装if标签下的sql 解析${} 但不会解析#{}
rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
//调用SqlSourceBuilder的parse方法 解析#{}变成? 这和一开始解析mapper中普通sql时的#{}是相同的方法
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
//拿到BoundSql 并返回 这时只有#{}中的内容还是?后面要处理
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
context.getBindings().forEach(boundSql::setAdditionalParameter);
return boundSql;
}
拿到sql继续执行查询操作
关键方法
rootSqlNode.apply(context)
2.静态解析
如果是#{}
则会走StaticSqlSource类的getBoundSql方法
执行完参数还是 问号?
然后继续执行查询方法,应该是在最终查询时将实际数据传入的。
继续CachingExecutor的query方法
然后执行BaseExecutor类的query方法
没有缓存的话去数据库查询
继续BaseExecutor类的queryFromDatabase方法
先把key放入缓存,然后继续执行doQuery方法
SimpleExecutor类的doQuery方法
关键方法 prepareStatement() 方法
SimpleExecutor类的prepareStatement方法
关键方法parameterize
就是这个方法给 ?赋的值
RoutingStatementHandler类的parameterize方法
DefaultParameterHandler类的setParameters方法
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
//从boundSql拿到参数的标识
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
//拿到属性名
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
//parameterObject就是传入的值
MetaObject metaObject = configuration.newMetaObject(parameterObject);
//根据key拿到value 这样就拿到了我们传入的值
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
// 由类型处理器 typeHandler 向 ParameterHandler 设置参数
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
值不是拼到sql 而是保存在columnMap中,,也就是不拼接sql,而是采用传参的方式。
文章来源:https://www.toymoban.com/news/detail-429051.html
至此,${} 和#{}的赋值就完成了,还需要分析的是《where》这种标签是怎么解析的,最后就是执行最终的查询了。
todo 查询后对结果进行解析文章来源地址https://www.toymoban.com/news/detail-429051.html
到了这里,关于执行增删改查时获取SQL语句的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!