前言
本章分析ShardingJDBC的核心步骤:重写。
一、SQL重写入口
回到BasePrepareEngine#prepare,经过路由处理后最终得到RouteContext,进入executeRewrite重写流程。
public ExecutionContext prepare(final String sql, final List parameters) {
List clonedParameters = cloneParameters(parameters);// 解析 & 路由
RouteContext routeContext = executeRoute(sql, clonedParameters);
ExecutionContext result = new ExecutionContext(routeContext.getSqlStatementContext());// 重写
Collection executionUnits = executeRewrite(sql, clonedParameters, routeContext);
result.getExecutionUnits().addAll(executionUnits);// 打印SQLif (properties.getValue(ConfigurationPropertyKey.SQL_SHOW)) {
SQLLogger.logSQL(sql, properties.getValue(ConfigurationPropertyKey.SQL_SIMPLE), result.getSqlStatementContext(), result.getExecutionUnits());
}return result;
}
BasePrepareEngine#executeRewrite
重写流程分为三步:
1. 注册SQLRewriteContextDecorator到SQLRewriteEntry。
2. SQLRewriteEntry创建SQLRewriteContext,重写参数列表,创建SQLToken。
3. 执行重写引擎SQLRouteRewriteEngine,重写sql,拼装参数列表。
private Collection executeRewrite(final String sql, final List parameters, final RouteContext routeContext) {
// 注册 BaseRule(ShardingRule)和对应SQL重写处理类 SQLRewriteContextDecorator 到SQLRewriteEntry(rewriter)
registerRewriteDecorator();
// 创建SQLRewriteContext,重写参数列表,创建SQLToken
SQLRewriteContext sqlRewriteContext = rewriter.createSQLRewriteContext(sql, parameters, routeContext.getSqlStatementContext(), routeContext);
if (routeContext.getRouteResult().getRouteUnits().isEmpty()) {
// 路由结果是空 比如走了ShardingIgnoreRoutingEngine(use xxx)
return rewrite(sqlRewriteContext);
} else {
// SQLRouteRewriteEngine 重写引擎执行(通常走这里)
return rewrite(routeContext, sqlRewriteContext);
}
}
二、注册装饰器
BasePrepareEngine#executeRewrite的第一步,就是将SQLRewriteContextDecorator注册到SQLRewriteEntry。这里一步类似于路由流程中BasePrepareEngine#registerRouteDecorator注册RouteDecorator到DataNodeRouter。
private void registerRewriteDecorator() {
for (Class extends SQLRewriteContextDecorator> each : OrderedRegistry.getRegisteredClasses(SQLRewriteContextDecorator.class)) {
SQLRewriteContextDecorator rewriteContextDecorator = each.newInstance();
Class> ruleClass = (Class>) rewriteContextDecorator.getType();
rules.stream().filter(rule -> rule.getClass() == ruleClass || rule.getClass().getSuperclass() == ruleClass).collect(Collectors.toList())
// 放入SQLRewriteEntry的Map
.forEach(rule -> rewriter.registerDecorator(rule, rewriteContextDecorator));
}
}
三、SQLRewriteEntry
SQLRewriteEntry负责创建SQLRewriteContextsql重写上下文,重写参数列表,创建SQLToken。
public final class SQLRewriteEntry {
// 表的元数据信息
private final SchemaMetaData schemaMetaData;
// 配置
private final ConfigurationProperties properties;
// BaseRule - SQLRewriteContextDecorator
private final Map decorators = new LinkedHashMap<>();
}
暴露两个公共方法:
- registerDecorator方法:注册SQLRewriteContextDecorator,这个在BasePrepareEngine#executeRewrite的第一步执行了。
private final Map decorators = new LinkedHashMap<>();public void registerDecorator(final BaseRule rule, final SQLRewriteContextDecorator decorator) {
decorators.put(rule, decorator);
}
- createSQLRewriteContext方法:创建SQLRewriteContext并执行所有SQLRewriteContextDecorator,创建SQLToken,这是BasePrepareEngine#executeRewrite的第二步。
public SQLRewriteContext createSQLRewriteContext(final String sql, final List parameters, final SQLStatementContext sqlStatementContext, final RouteContext routeContext) {
SQLRewriteContext result = new SQLRewriteContext(schemaMetaData, sqlStatementContext, sql, parameters);
// 执行所有SQLRewriteContextDecorator,其中重写参数列表
decorate(decorators, result, routeContext);
// 创建SQLToken
result.generateSQLTokens();
return result;
}
private void decorate(final Map decorators, final SQLRewriteContext sqlRewriteContext, final RouteContext routeContext) {
for (Entry entry : decorators.entrySet()) {
BaseRule rule = entry.getKey();
SQLRewriteContextDecorator decorator = entry.getValue();if (decorator instanceof RouteContextAware) {
((RouteContextAware) decorator).setRouteContext(routeContext);
}
decorator.decorate(rule, properties, sqlRewriteContext);
}
}
SQLRewriteContextDecorator
SQLRewriteContextDecorator,一般情况下要做两个事情:
- 参数重写,执行ParameterRewriter集合,将重写相关信息保存到SQLRewriteContext#parameterBuilder中
- 创建SQLTokenGenerator集合,保存到SQLRewriteContext#sqlTokenGenerators中
SQLRewriteContextDecorator有三个实现:
- EncryptSQLRewriteContextDecorator负责数据脱敏。