水煮MyBatis(三)- SQL解析

前言

在Mapper接口中,有注解的方法,也有xml配置的方法,这一章我们主要介绍前者。

本文介绍的Mapper方法

@Select("select * from tb_image where md5 = #{md5}")
ImageInfo byMd5(@Param(value = "md5") String md5);
    @Select("select * from tb_image where md5 = #{md5}")
    ImageInfo byMd5(@Param(value = "md5") String md5);
@Select("select * from tb_image where md5 = #{md5}") ImageInfo byMd5(@Param(value = "md5") String md5);

序列图

image.png

注册Mapper

注册Mapper,将Mapper注册到knownMappers中,注意后面的是一个代理类,后续再开个单章介绍。同时将Mapper接口解析,代码中的config参数是Configuration的实体,主要是mybatis的配置信息。

public <T> void addMapper(Class<T> type) {
// 注册
knownMappers.put(type, new MapperProxyFactory<>(type));
// 主要代码就是这两行
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
// 解析
parser.parse();
}
  public <T> void addMapper(Class<T> type) {
        // 注册
        knownMappers.put(type, new MapperProxyFactory<>(type));
        // 主要代码就是这两行
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        // 解析
        parser.parse();
  }
public <T> void addMapper(Class<T> type) { // 注册 knownMappers.put(type, new MapperProxyFactory<>(type)); // 主要代码就是这两行 MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); // 解析 parser.parse(); }

image.png

解析Mapper

Mapper里的每个方法都会创建一个statement,供后续执行使用。

解析Mapper中的方法

public void parse() {
String resource = type.toString();
// 加载xml配置,在XMLMapperBuilder中解析xml中的方法,这里忽略不讲
loadXmlResource();
// 每个Mapper加载一次
configuration.addLoadedResource(resource);
// 当前Mapper的命名空间
assistant.setCurrentNamespace(type.getName());
// 缓存注解解析,见后续单章
parseCache();
parseCacheRef();
// 每个方法都需要生成一个statement
for (Method method : type.getMethods()) {
// 忽略不能解析的方法,比如抽象、静态等
if (!canHaveStatement(method)) {
continue;
}
// 生成statement,保存到configuration
parseStatement(method);
}
}
  public void parse() {
    String resource = type.toString();
    // 加载xml配置,在XMLMapperBuilder中解析xml中的方法,这里忽略不讲
    loadXmlResource();
    // 每个Mapper加载一次
    configuration.addLoadedResource(resource);
    // 当前Mapper的命名空间
    assistant.setCurrentNamespace(type.getName());
    // 缓存注解解析,见后续单章
    parseCache();
    parseCacheRef();
    // 每个方法都需要生成一个statement
    for (Method method : type.getMethods()) {
      // 忽略不能解析的方法,比如抽象、静态等
      if (!canHaveStatement(method)) {
          continue;
      }
      // 生成statement,保存到configuration
      parseStatement(method);
    }
  }
public void parse() { String resource = type.toString(); // 加载xml配置,在XMLMapperBuilder中解析xml中的方法,这里忽略不讲 loadXmlResource(); // 每个Mapper加载一次 configuration.addLoadedResource(resource); // 当前Mapper的命名空间 assistant.setCurrentNamespace(type.getName()); // 缓存注解解析,见后续单章 parseCache(); parseCacheRef(); // 每个方法都需要生成一个statement for (Method method : type.getMethods()) { // 忽略不能解析的方法,比如抽象、静态等 if (!canHaveStatement(method)) { continue; } // 生成statement,保存到configuration parseStatement(method); } }

主要流程分为三个步骤:

  • 解析Mapper对应的xml文件,并为xml文件里的每个方法都生成一个statement;
  • 解析缓存注解,后续介绍;
  • 解析注解方法;

生成statement

public MappedStatement addMappedStatement(...) {
// 是否查询方法
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
// 生成statement
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
.resource(resource)
.fetchSize(fetchSize)
.timeout(timeout)
.statementType(statementType)
.keyGenerator(keyGenerator)
.keyProperty(keyProperty)
.keyColumn(keyColumn)
.databaseId(databaseId)
.lang(lang)
.resultOrdered(resultOrdered)
.resultSets(resultSets)
.resultMaps(getStatementResultMaps(resultMap, resultType, id))
.resultSetType(resultSetType)
.flushCacheRequired(valueOrDefault(flushCache, !isSelect))
.useCache(valueOrDefault(useCache, isSelect))
.cache(currentCache);
// 语句的参数配置
ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}
// Mapper里每个方法都会生成一个statement
MappedStatement statement = statementBuilder.build();
// 将statement放到mybatis上下文
configuration.addMappedStatement(statement);
return statement;
}
    public MappedStatement addMappedStatement(...) {
    // 是否查询方法
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    // 生成statement
    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
        .resource(resource)
        .fetchSize(fetchSize)
        .timeout(timeout)
        .statementType(statementType)
        .keyGenerator(keyGenerator)
        .keyProperty(keyProperty)
        .keyColumn(keyColumn)
        .databaseId(databaseId)
        .lang(lang)
        .resultOrdered(resultOrdered)
        .resultSets(resultSets)
        .resultMaps(getStatementResultMaps(resultMap, resultType, id))
        .resultSetType(resultSetType)
        .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
        .useCache(valueOrDefault(useCache, isSelect))
        .cache(currentCache);
    // 语句的参数配置
    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) {
      statementBuilder.parameterMap(statementParameterMap);
    }
    // Mapper里每个方法都会生成一个statement
    MappedStatement statement = statementBuilder.build();
    // 将statement放到mybatis上下文
    configuration.addMappedStatement(statement);
    return statement;
  }
public MappedStatement addMappedStatement(...) { // 是否查询方法 boolean isSelect = sqlCommandType == SqlCommandType.SELECT; // 生成statement MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType) .resource(resource) .fetchSize(fetchSize) .timeout(timeout) .statementType(statementType) .keyGenerator(keyGenerator) .keyProperty(keyProperty) .keyColumn(keyColumn) .databaseId(databaseId) .lang(lang) .resultOrdered(resultOrdered) .resultSets(resultSets) .resultMaps(getStatementResultMaps(resultMap, resultType, id)) .resultSetType(resultSetType) .flushCacheRequired(valueOrDefault(flushCache, !isSelect)) .useCache(valueOrDefault(useCache, isSelect)) .cache(currentCache); // 语句的参数配置 ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id); if (statementParameterMap != null) { statementBuilder.parameterMap(statementParameterMap); } // Mapper里每个方法都会生成一个statement MappedStatement statement = statementBuilder.build(); // 将statement放到mybatis上下文 configuration.addMappedStatement(statement); return statement; }

将statement注册到Configuration里的mappedStatements结构中,后续执行的时候,直接从这里获取对应方法的statement

public void addMappedStatement(MappedStatement ms) {
mappedStatements.put(ms.getId(), ms);
}
public void addMappedStatement(MappedStatement ms) {
    mappedStatements.put(ms.getId(), ms);
  }
public void addMappedStatement(MappedStatement ms) { mappedStatements.put(ms.getId(), ms); }

替换sql中的占位符

在SqlSourceBuilder类中,对sql中的参数占位符进行替换

public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
String sql;
if (configuration.isShrinkWhitespacesInSql()) {
sql = parser.parse(removeExtraWhitespaces(originalSql));
} else {
sql = parser.parse(originalSql);
}
// 从handler中获取请求参数map
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}
  public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
    ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
    GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
    String sql;
    if (configuration.isShrinkWhitespacesInSql()) {
      sql = parser.parse(removeExtraWhitespaces(originalSql));
    } else {
      sql = parser.parse(originalSql);
    }
    // 从handler中获取请求参数map
    return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
  }
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) { ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters); GenericTokenParser parser = new GenericTokenParser("#{", "}", handler); String sql; if (configuration.isShrinkWhitespacesInSql()) { sql = parser.parse(removeExtraWhitespaces(originalSql)); } else { sql = parser.parse(originalSql); } // 从handler中获取请求参数map return new StaticSqlSource(configuration, sql, handler.getParameterMappings()); }

例子中的请求参数:
ParameterMapping{property='md5', mode=IN, javaType=class java.lang.String, jdbcType=null, numericScale=null, resultMapId='null', jdbcTypeName='null', expression='null'}
替换效果如下:
image.png

最终Statement信息

image.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MY233i4t' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片