聊聊Mybatis的动态Sql之SqlSource

小咪咪 2024-04-08 10:46 142阅读 0赞

聊聊Mybatis的动态Sql之SqlSource

构建SqlSource对象

当Mapper.xml的各个标签被解析后SqlNode,然后SqlSourceBuilder进一步处理,

  1. public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
  2. ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
  3. GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
  4. String sql;
  5. if (configuration.isShrinkWhitespacesInSql()) {
  6. sql = parser.parse(removeExtraWhitespaces(originalSql));
  7. } else {
  8. sql = parser.parse(originalSql);
  9. }
  10. return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
  11. }
  1. ParameterMappingTokenHandler是静态内部类,它用来保存每个占位符参数解析后的结果
  2. 创建识别#{}占位符的解析器GenericTokenParser,解析sql,最终形成StaticSqlSource对象

SqlSource接口

SqlSource接口是用来创建被数据库执行的sql,它只有一个getBoundSql()方法

  1. public interface SqlSource {
  2. BoundSql getBoundSql(Object parameterObject);
  3. }

实现类有DynamicSqlSource、StaticSqlSource、RawSqlSource、ProviderSqlSource、VelocitySqlSource,这里重点说一下前三个实现类

解析动态Sql类

DynamicSqlSource是解析动态sql的类

  1. public class DynamicSqlSource implements SqlSource {
  2. private final Configuration configuration;
  3. private final SqlNode rootSqlNode;
  4. public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
  5. this.configuration = configuration;
  6. this.rootSqlNode = rootSqlNode;
  7. }
  8. @Override
  9. public BoundSql getBoundSql(Object parameterObject) {
  10. DynamicContext context = new DynamicContext(configuration, parameterObject);
  11. rootSqlNode.apply(context);
  12. SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
  13. Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
  14. SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
  15. BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
  16. context.getBindings().forEach(boundSql::setAdditionalParameter);
  17. return boundSql;
  18. }
  19. }

它的getBoundSql()方法中:

  1. 创建DynamicContext对象
  2. 调用SqlNode的apply()方法完成对sql片段的解析
  3. 创建SqlSourceBuilder对象,调用parse()方法进行解析#{},替换成?占位符,返回StaticSqlSource对象,
  4. 调用StaticSqlSource的getBoundSql()方法,返回BoundSql对象,这里存储着sql语句的相关信息
  5. 返回BoundSql对象

解析静态SQL类

RawSqlSource是解析静态sql文件的,在程序启动的时候就解析了

  1. public class RawSqlSource implements SqlSource {
  2. private final SqlSource sqlSource;
  3. public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
  4. this(configuration, getSql(configuration, rootSqlNode), parameterType);
  5. }
  6. public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
  7. SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
  8. Class<?> clazz = parameterType == null ? Object.class : parameterType;
  9. sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
  10. }
  11. private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
  12. DynamicContext context = new DynamicContext(configuration, null);
  13. rootSqlNode.apply(context);
  14. return context.getSql();
  15. }
  16. @Override
  17. public BoundSql getBoundSql(Object parameterObject) {
  18. return sqlSource.getBoundSql(parameterObject);
  19. }
  20. }

它的构造方法调用了getSql()方法,这个方法里调用SqlSource的apply()方法组装成完整sql,然后通过SqlSourceBuilder调用parse()方法处理#{}占位符,返回StaticSqlSource对象

StaticSqlSource

StaticSqlSource类:

  1. public class StaticSqlSource implements SqlSource {
  2. @Override
  3. public BoundSql getBoundSql(Object parameterObject) {
  4. return new BoundSql(configuration, sql, parameterMappings, parameterObject);
  5. }
  6. }

它的getBoundSql()方法就是创建BoundSql对象

总结

这篇文章讲了SqlSource的接口和它的几个实现类,其中DynamicSqlSource类和RawSqlSource类最最终生成StaticSqlSource类,由StaticSqlSource类调用getBoundSql()方法来创建BoundSql类,DynamicSqlSource是解析动态sql的类,RawSqlSource是解析静态sql的类,在程序启动的时候就生成sql了

发表评论

表情:
评论列表 (有 0 条评论,142人围观)

还没有评论,来说两句吧...

相关阅读