ShardingSphere源码分析4-改写引擎

660 阅读2分钟

目标

进入该系列的第4篇,分析ShardingSphere中的改写引擎。

关于改写引擎

通过官方文档,我们了解到:改写引擎是用于将逻辑 SQL 改写为在真实数据库中可以正确执行的 SQL。

那么接下来,我们就通过源码,来探究下改写的工作过程。

源码解析

1. 调用入口:KernelProcessor

// 创建执行上下文
public ExecutionContext generateExecutionContext(final LogicSQL logicSQL, final ShardingSphereMetaData metaData, final ConfigurationProperties props) {
    // 路由
    RouteContext routeContext = route(logicSQL, metaData, props);
    // 改写(基于上一步得到的路由上下文)
    SQLRewriteResult rewriteResult = rewrite(logicSQL, metaData, props, routeContext);
    // 执行
    ExecutionContext result = createExecutionContext(logicSQL, metaData, routeContext, rewriteResult);
    // 日志打印
    logSQL(logicSQL, props, result);
    return result;
}

2. SQL改写入口:SQLRewriteEntry

private SQLRewriteResult rewrite(final LogicSQL logicSQL, final ShardingSphereMetaData metaData, final ConfigurationProperties props, final RouteContext routeContext) {
    /**
     * 这里分几步:
     * 1、根据元信息里的schema、配置信息、路由规则,创建SQL改写入口
     * 2、入口调用rewrite方法,返回SQL改写结果
     */
    return new SQLRewriteEntry(
            metaData.getSchema(), props, metaData.getRuleMetaData().getRules()).rewrite(logicSQL.getSql(), logicSQL.getParameters(), logicSQL.getSqlStatementContext(), routeContext);
}

3. SQL改写引擎

public SQLRewriteResult rewrite(final String sql, final List<Object> parameters, final SQLStatementContext<?> sqlStatementContext, final RouteContext routeContext) {
    // 根据逻辑SQL、逻辑SQL的参数、逻辑SQL的上下文信息、路由上下文,创建SQL改写上下文
    SQLRewriteContext sqlRewriteContext = createSQLRewriteContext(sql, parameters, sqlStatementContext, routeContext);
    /**
     * 路由上下文里的路由单元为空时,创建通用的sql改写引擎,调用rewrite方法,返回SQL重写结果
     * 否则,调用路由SQL改写引擎,调用rewrite方法,返回SQL重写结果
     */
    return routeContext.getRouteUnits().isEmpty()
            ? new GenericSQLRewriteEngine().rewrite(sqlRewriteContext) : new RouteSQLRewriteEngine().rewrite(sqlRewriteContext, routeContext);
}

根据路由单元的不同,区分通用和路由两类引擎

3.1. 通用SQL改写引擎

public final class GenericSQLRewriteEngine {

    public GenericSQLRewriteResult rewrite(final SQLRewriteContext sqlRewriteContext) {
        // 生成默认的SQL建造者,调用toSQL方法,改写sql
        // 调参数建造者,获取参数信息
        return new GenericSQLRewriteResult(new SQLRewriteUnit(new DefaultSQLBuilder(sqlRewriteContext).toSQL(), sqlRewriteContext.getParameterBuilder().getParameters()));
    }
}

3.2. 路由SQL改写引擎

public final class RouteSQLRewriteEngine {
    
    public RouteSQLRewriteResult rewrite(final SQLRewriteContext sqlRewriteContext, final RouteContext routeContext) {
        // 根据路由上下文中包含的routeUnit数量,创建 LinkedHashMap
        Map<RouteUnit, SQLRewriteUnit> result = new LinkedHashMap<>(routeContext.getRouteUnits().size(), 1);
        for (RouteUnit each : routeContext.getRouteUnits()) {
            /**
             * 1、根据sqlRewriteContext、routeUnit创建RouteSQLBuilder,再改写SQL
             * 2、根据sqlRewriteContext、routeContext、routeUnit,获取parameter
             * 3、改写后的SQL、parameter,跟对应的路由单元一起封装
             */
            result.put(each, new SQLRewriteUnit(new RouteSQLBuilder(sqlRewriteContext, each).toSQL(), getParameters(sqlRewriteContext.getParameterBuilder(), routeContext, each)));
        }
        return new RouteSQLRewriteResult(result);
    }

4. 改写SQL

public final String toSQL() {
    if (context.getSqlTokens().isEmpty()) {
        return context.getSql();
    }
    // 对sqlToken排序
    Collections.sort(context.getSqlTokens());
    StringBuilder result = new StringBuilder();
    result.append(context.getSql(), 0, context.getSqlTokens().get(0).getStartIndex());
    for (SQLToken each : context.getSqlTokens()) {
        result.append(each instanceof ComposableSQLToken ? getComposableSQLTokenText((ComposableSQLToken) each) : getSQLTokenText(each));
        result.append(getConjunctionText(each));
    }
    return result.toString();
}

5. 获取参数

通过参数建造者获取参数,其本身是个接口,有两类实现:分组、标准,其中分组是在方法中遍历调用标准来实现

5.1. 分组参数建造者模式

public List<Object> getParameters() {
    List<Object> result = new LinkedList<>();
    for (int i = 0; i < parameterBuilders.size(); i++) {
        // 遍历标准参数建造者,调用getParameters()方法,获取参数
        result.addAll(getParameters(i));
    }
    result.addAll(genericParameterBuilder.getParameters());
    return result;
}

5.2. 标准参数建造者模式

public List<Object> getParameters() {
    List<Object> result = new LinkedList<>(originalParameters);
    for (Entry<Integer, Object> entry : replacedIndexAndParameters.entrySet()) {
        // 已替换的参数,在结果中重置
        result.set(entry.getKey(), entry.getValue());
    }
    for (Entry<Integer, Collection<Object>> entry : ((TreeMap<Integer, Collection<Object>>) addedIndexAndParameters).descendingMap().entrySet()) {
        if (entry.getKey() > result.size()) {
            result.addAll(entry.getValue());
        } else {
            result.addAll(entry.getKey(), entry.getValue());
        }
    }
    for (int index : removeIndexAndParameters) {
        // 移除已失效的参数
        result.remove(index);
    }
    return result;
}

总结

初步探索了SQL改写的实现