目标
进入该系列的第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改写的实现