Mybatis的SqlSource & SqlNode & BoundSql

学习链接

MyBatis SqlSource解析

【Mybatis】Mybatis源码之SqlSource#getBoundSql获取预编译SQL

Mybatis中SqlSource解析流程详解

Mybatis TypeHandler解析

图解

Mybatis的SqlSource&SqlNode - processon

Mybatis的SqlSource & SqlNode & BoundSql_第1张图片

DynamicSqlSource#getBoundSql

/**
 * 获取一个BoundSql对象
 *
 * DynamicSqlSource和 RawSqlSource都会转化为 StaticSqlSource,然后才能给出一个 BoundSql对象。
 *
 * @param parameterObject 参数对象
 * @return BoundSql对象
 */
@Override
public BoundSql getBoundSql(Object parameterObject) {
    // 创建DynamicSqlSource的辅助类,用来记录DynamicSqlSource解析出来的SQL片段信息和参数信息
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    // 这里会从根节点开始,对节点逐层调用apply方法,经过这一步后,动态节点"${}"都被替换,这样 DynamicSqlSource便不再是动态的,而是静态的。
    rootSqlNode.apply(context);
    // 处理占位符,汇总参数信息
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    // 使用SqlSourceBuilder处理"#{}",将其转化为"?",然后创建ParameterMapping,最终生成了StaticSqlSource对象
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    // 把context.getBindings()的参数放到boundSql的metaParameters中进行保存
    context.getBindings().forEach(boundSql::setAdditionalParameter);
    return boundSql;
}

GenericTokenParser#parse

/**
 * 该方法主要 完成占位符的定位工作,然后把占位符的替换工作交给与其关联的 TokenHandler 处理
 * @param text
 * @return
 */
public String parse(String text) {
    if (text == null || text.isEmpty()) {
        return "";
    }
    // search open token
    // 查找openToken的位置
    int start = text.indexOf(openToken);
    if (start == -1) {
        return text;
    }
    char[] src = text.toCharArray();
    int offset = 0;
    final StringBuilder builder = new StringBuilder();
    StringBuilder expression = null;
    // 当存在openToken时,才继续处理
    while (start > -1) {
        if (start > 0 && src[start - 1] == '\\') {
            // this open token is escaped. remove the backslash and continue.
            builder.append(src, offset, start - offset - 1).append(openToken);
            offset = start + openToken.length();
        } else {
            // found open token. let's search close token.
            if (expression == null) {
                expression = new StringBuilder();
            } else {
                expression.setLength(0);
            }
            // 拼接从0到openToken之前的字符
            builder.append(src, offset, start - offset);
            // 设置offset值为openToken结束的位置
            offset = start + openToken.length();
            // 从offset值之后开始找第一个closeToken的位置
            int end = text.indexOf(closeToken, offset);
            // 如果存在,则继续处理
            while (end > -1) {
                if (end > offset && src[end - 1] == '\\') {
                    // this close token is escaped. remove the backslash and continue.
                    expression.append(src, offset, end - offset - 1).append(closeToken);
                    offset = end + closeToken.length();
                    // 继续查找当前closeToken之后的closeToken
                    end = text.indexOf(closeToken, offset);
                } else {
                    expression.append(src, offset, end - offset);
                    break;
                }
            }
            // 如果不存在
            if (end == -1) {
                // close token was not found.
                // 拼接剩余的字符
                builder.append(src, start, src.length - start);
                // 设置offset为字符数组的长度
                offset = src.length;
            } else {
                /**
                 * DynamicCheckerTokenParser:如果存在,则设置当前SQL为动态的
                 * BindingTokenParser:获取$变量的值
                 * ParameterMappingTokenHandler:将#替换为?,并构建参数映射ParameterMapping
                 */
                builder.append(handler.handleToken(expression.toString()));
                // 设置offset值为closeToken结束的位置
                offset = end + closeToken.length();
            }
        }
        start = text.indexOf(openToken, offset);
    }
    // 拼接剩余的字符
    if (offset < src.length) {
        builder.append(src, offset, src.length - offset);
    }
    return builder.toString();
}

你可能感兴趣的:(#,mybatis,mybatis)