与4.1.1不同,本次debug的项目没有使用mybatis,为了去除第三方框架带来的源码干扰,本次采用的原生JDBC操作原生ShardingSphereAPI的读写分离+分片
以下的Debug会深刻的了解为啥ShardingJDBC是嵌入式代码,其究竟如何复写的JDBC代码。


设置断点

随着路由信息收集的完成,debug来到了SQLRewriteResult rewriteResult = rewrite(logicSQL, metaData, props, routeContext);将断点设置到这里,显然,这里就是SQL语句该写的开始。其源码如下:

  1. private SQLRewriteResult rewrite(final LogicSQL logicSQL, final ShardingSphereMetaData metaData, final ConfigurationProperties props, final RouteContext routeContext) {
  2. return new SQLRewriteEntry(
  3. metaData.getSchema(), props, metaData.getRuleMetaData().getRules()).rewrite(logicSQL.getSql(), logicSQL.getParameters(), logicSQL.getSqlStatementContext(), routeContext);
  4. }

开始重写

rewrite中做的第一件事情就是SQLRewriteEntry对象的创建:

  1. public SQLRewriteEntry(final ShardingSphereSchema schema, final ConfigurationProperties props, final Collection<ShardingSphereRule> rules) {
  2. this.schema = schema;
  3. this.props = props;
  4. decorators = OrderedSPIRegistry.getRegisteredServices(rules, SQLRewriteContextDecorator.class);
  5. }

进行一些初始化后调用其rewrite方法:

  1. public SQLRewriteResult rewrite(final String sql, final List<Object> parameters, final SQLStatementContext<?> sqlStatementContext, final RouteContext routeContext) {
  2. SQLRewriteContext sqlRewriteContext = createSQLRewriteContext(sql, parameters, sqlStatementContext, routeContext);
  3. return routeContext.getRouteUnits().isEmpty()
  4. ? new GenericSQLRewriteEngine().rewrite(sqlRewriteContext) : new RouteSQLRewriteEngine().rewrite(sqlRewriteContext, routeContext);
  5. }

创建改写上下文

  1. private SQLRewriteContext createSQLRewriteContext(final String sql, final List<Object> parameters, final SQLStatementContext<?> sqlStatementContext, final RouteContext routeContext) {
  2. SQLRewriteContext result = new SQLRewriteContext(schema, sqlStatementContext, sql, parameters);
  3. decorate(decorators, result, routeContext);
  4. result.generateSQLTokens();
  5. return result;
  6. }

追踪:

  1. public SQLRewriteContext(final ShardingSphereSchema schema, final SQLStatementContext<?> sqlStatementContext, final String sql, final List<Object> parameters) {
  2. this.schema = schema;
  3. this.sqlStatementContext = sqlStatementContext;
  4. this.sql = sql;
  5. this.parameters = parameters;
  6. addSQLTokenGenerators(new DefaultTokenGeneratorBuilder().getSQLTokenGenerators());
  7. parameterBuilder = sqlStatementContext instanceof InsertStatementContext
  8. ? new GroupedParameterBuilder(
  9. ((InsertStatementContext) sqlStatementContext).getGroupedParameters(), ((InsertStatementContext) sqlStatementContext).getOnDuplicateKeyUpdateParameters())
  10. : new StandardParameterBuilder(parameters);
  11. }

快进到获取完上下文:
image.png

执行重写

根据路由上下文和路径信息开始执行路由重写引擎的rewrite方法,其源码如下:

  1. public RouteSQLRewriteResult rewrite(final SQLRewriteContext sqlRewriteContext, final RouteContext routeContext) {
  2. Map<RouteUnit, SQLRewriteUnit> result = new LinkedHashMap<>(routeContext.getRouteUnits().size(), 1);
  3. for (RouteUnit each : routeContext.getRouteUnits()) {
  4. result.put(each, new SQLRewriteUnit(new RouteSQLBuilder(sqlRewriteContext, each).toSQL(), getParameters(sqlRewriteContext.getParameterBuilder(), routeContext, each)));
  5. }
  6. return new RouteSQLRewriteResult(result);
  7. }

这里的具体逻辑位于toSQL方法中:

  1. @Override
  2. public final String toSQL() {
  3. if (context.getSqlTokens().isEmpty()) {
  4. return context.getSql();
  5. }
  6. Collections.sort(context.getSqlTokens());
  7. StringBuilder result = new StringBuilder();
  8. result.append(context.getSql(), 0, context.getSqlTokens().get(0).getStartIndex());
  9. for (SQLToken each : context.getSqlTokens()) {
  10. result.append(getSQLTokenText(each));
  11. result.append(getConjunctionText(each));
  12. }
  13. return result.toString();
  14. }

具体的路由获取位于getSQLTokenText和getConjunctionText:

  1. protected String getSQLTokenText(final SQLToken sqlToken) {
  2. if (sqlToken instanceof RouteUnitAware) {
  3. return ((RouteUnitAware) sqlToken).toString(routeUnit);
  4. }
  5. return sqlToken.toString();
  6. }

这样真实的SQL语句就放入到了SQLRewriteResult:
image.png