与4.1.1不同,本次debug的项目没有使用mybatis,为了去除第三方框架带来的源码干扰,本次采用的原生JDBC操作原生ShardingSphereAPI的读写分离+分片。
以下的Debug会深刻的了解为啥ShardingJDBC是嵌入式代码,其究竟如何复写的JDBC代码。
设置断点
随着路由信息收集的完成,debug来到了SQLRewriteResult rewriteResult = rewrite(logicSQL, metaData, props, routeContext);将断点设置到这里,显然,这里就是SQL语句该写的开始。其源码如下:
private SQLRewriteResult rewrite(final LogicSQL logicSQL, final ShardingSphereMetaData metaData, final ConfigurationProperties props, final RouteContext routeContext) {return new SQLRewriteEntry(metaData.getSchema(), props, metaData.getRuleMetaData().getRules()).rewrite(logicSQL.getSql(), logicSQL.getParameters(), logicSQL.getSqlStatementContext(), routeContext);}
开始重写
rewrite中做的第一件事情就是SQLRewriteEntry对象的创建:
public SQLRewriteEntry(final ShardingSphereSchema schema, final ConfigurationProperties props, final Collection<ShardingSphereRule> rules) {this.schema = schema;this.props = props;decorators = OrderedSPIRegistry.getRegisteredServices(rules, SQLRewriteContextDecorator.class);}
进行一些初始化后调用其rewrite方法:
public SQLRewriteResult rewrite(final String sql, final List<Object> parameters, final SQLStatementContext<?> sqlStatementContext, final RouteContext routeContext) {SQLRewriteContext sqlRewriteContext = createSQLRewriteContext(sql, parameters, sqlStatementContext, routeContext);return routeContext.getRouteUnits().isEmpty()? new GenericSQLRewriteEngine().rewrite(sqlRewriteContext) : new RouteSQLRewriteEngine().rewrite(sqlRewriteContext, routeContext);}
创建改写上下文
private SQLRewriteContext createSQLRewriteContext(final String sql, final List<Object> parameters, final SQLStatementContext<?> sqlStatementContext, final RouteContext routeContext) {SQLRewriteContext result = new SQLRewriteContext(schema, sqlStatementContext, sql, parameters);decorate(decorators, result, routeContext);result.generateSQLTokens();return result;}
追踪:
public SQLRewriteContext(final ShardingSphereSchema schema, final SQLStatementContext<?> sqlStatementContext, final String sql, final List<Object> parameters) {this.schema = schema;this.sqlStatementContext = sqlStatementContext;this.sql = sql;this.parameters = parameters;addSQLTokenGenerators(new DefaultTokenGeneratorBuilder().getSQLTokenGenerators());parameterBuilder = sqlStatementContext instanceof InsertStatementContext? new GroupedParameterBuilder(((InsertStatementContext) sqlStatementContext).getGroupedParameters(), ((InsertStatementContext) sqlStatementContext).getOnDuplicateKeyUpdateParameters()): new StandardParameterBuilder(parameters);}
执行重写
根据路由上下文和路径信息开始执行路由重写引擎的rewrite方法,其源码如下:
public RouteSQLRewriteResult rewrite(final SQLRewriteContext sqlRewriteContext, final RouteContext routeContext) {Map<RouteUnit, SQLRewriteUnit> result = new LinkedHashMap<>(routeContext.getRouteUnits().size(), 1);for (RouteUnit each : routeContext.getRouteUnits()) {result.put(each, new SQLRewriteUnit(new RouteSQLBuilder(sqlRewriteContext, each).toSQL(), getParameters(sqlRewriteContext.getParameterBuilder(), routeContext, each)));}return new RouteSQLRewriteResult(result);}
这里的具体逻辑位于toSQL方法中:
@Overridepublic final String toSQL() {if (context.getSqlTokens().isEmpty()) {return context.getSql();}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(getSQLTokenText(each));result.append(getConjunctionText(each));}return result.toString();}
具体的路由获取位于getSQLTokenText和getConjunctionText:
protected String getSQLTokenText(final SQLToken sqlToken) {if (sqlToken instanceof RouteUnitAware) {return ((RouteUnitAware) sqlToken).toString(routeUnit);}return sqlToken.toString();}
这样真实的SQL语句就放入到了SQLRewriteResult:
