增删改查标签解析生成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>

这里做了个解析,但是

posted @ 2022-09-04 10:59  雩娄的木子  阅读(105)  评论(0编辑  收藏  举报