前言

上文ShardingSphere-JDBC分片路由引擎中介绍了分片流程中的路由引擎,最终获取了路由后果;本文要介绍的改写引擎须要应用路由后果来对SQL进行改写,改写成能够被正确的分库分表可能执行的SQL;这外面波及对各种SQL改写的状况泛滥,接下来本文会进行一一剖析。

改写装璜器

重写引擎同样应用了装璜器模式,提供了接口类SQLRewriteContextDecorator,实现类包含:

  • ShardingSQLRewriteContextDecorator:分片SQL改写装璜器;
  • ShadowSQLRewriteContextDecorator:影子库SQL改写装璜器;
  • EncryptSQLRewriteContextDecorator:数据加密SQL改写装璜器;

默认加载ShardingSQLRewriteContextDecoratorEncryptSQLRewriteContextDecorator,应用java.util.ServiceLoader来加载重写装璜器,须要在META-INF/services/中指定具体的实现类:

org.apache.shardingsphere.sharding.rewrite.context.ShardingSQLRewriteContextDecoratororg.apache.shardingsphere.encrypt.rewrite.context.EncryptSQLRewriteContextDecorator

装璜器能够叠加,所以提供了优先级性能OrderAware,同时每个装璜器都有对应的规定,大抵如下所示:

装璜器-SQLRewriteContextDecorator规定-BaseRule优先级-Order
ShardingSQLRewriteContextDecoratorShardingRule0
EncryptSQLRewriteContextDecoratorEncryptRule20
ShadowSQLRewriteContextDecoratorShadowRule30

只有在配置了相干BaseRule,对应的SQLRewriteContextDecorator能力失效,最常见的是ShardingSQLRewriteContextDecorator,上面重点介绍此装璜器;

改写引擎

不同的SQL语句,须要解决的改写都不一样,改写引擎的整体构造划分如下图所示(来自官网):

执行改写引擎之前须要做一些筹备工作,整个改写流程大抵分为以下几步:

  • 依据不同的改写装璜器结构不同的SQLTokenGenerator列表;
  • 依据SQLTokenGenerator生成对应的SQLToken
  • 改写引擎依据SQLToken执行改写操作;

结构SQLTokenGenerator

不同的装璜器须要结构不同的SQLTokenGenerator列表,以最常见的ShardingSQLRewriteContextDecorator为例,会筹备如下13种SQLTokenGenerator

    private Collection<SQLTokenGenerator> buildSQLTokenGenerators() {        Collection<SQLTokenGenerator> result = new LinkedList<>();        addSQLTokenGenerator(result, new TableTokenGenerator());        addSQLTokenGenerator(result, new DistinctProjectionPrefixTokenGenerator());        addSQLTokenGenerator(result, new ProjectionsTokenGenerator());        addSQLTokenGenerator(result, new OrderByTokenGenerator());        addSQLTokenGenerator(result, new AggregationDistinctTokenGenerator());        addSQLTokenGenerator(result, new IndexTokenGenerator());        addSQLTokenGenerator(result, new OffsetTokenGenerator());        addSQLTokenGenerator(result, new RowCountTokenGenerator());        addSQLTokenGenerator(result, new GeneratedKeyInsertColumnTokenGenerator());        addSQLTokenGenerator(result, new GeneratedKeyForUseDefaultInsertColumnsTokenGenerator());        addSQLTokenGenerator(result, new GeneratedKeyAssignmentTokenGenerator());        addSQLTokenGenerator(result, new ShardingInsertValuesTokenGenerator());        addSQLTokenGenerator(result, new GeneratedKeyInsertValuesTokenGenerator());        return result;    }

以上实现类的公共接口类为SQLTokenGenerator,提供了是否失效的公共办法:

public interface SQLTokenGenerator {    boolean isGenerateSQLToken(SQLStatementContext sqlStatementContext);}

各种不同的SQLTokenGenerator并不是每次都能失效的,须要依据不同SQL语句进行判断,SQL语句在解析引擎中曾经被解析为SQLStatementContext,这样能够通过SQLStatementContext参数进行判断;

TableTokenGenerator

TableToken生成器,次要用于对表名的改写;

    public boolean isGenerateSQLToken(final SQLStatementContext sqlStatementContext) {        return true;    }

能够发现这里对是否生成SQLToken没有任何条件,间接返回true;然而在生成TableToken的时候是会查看是否存在表信息以及是否配置相干表TableRule

DistinctProjectionPrefixTokenGenerator

DistinctProjectionPrefixToken生成器,次要对聚合函数和去重的解决:

    public boolean isGenerateSQLToken(final SQLStatementContext sqlStatementContext) {        return sqlStatementContext instanceof SelectStatementContext && !((SelectStatementContext) sqlStatementContext).getProjectionsContext().getAggregationDistinctProjections().isEmpty();    }

首先必须是select语句,同时蕴含:聚合函数和Distinct去重,比方上面的SQL:

select sum(distinct user_id) from t_order where order_id = 101

改写之后的SQL如下所示:

Actual SQL: ds0 ::: select DISTINCT user_id AS AGGREGATION_DISTINCT_DERIVED_0 from t_order1 where order_id = 101Actual SQL: ds1 ::: select DISTINCT user_id AS AGGREGATION_DISTINCT_DERIVED_0 from t_order1 where order_id = 101

ProjectionsTokenGenerator

ProjectionsToken生成器,聚合函数须要做派生解决,比方AVG函数

    public boolean isGenerateSQLToken(final SQLStatementContext sqlStatementContext) {        return sqlStatementContext instanceof SelectStatementContext && !getDerivedProjectionTexts((SelectStatementContext) sqlStatementContext).isEmpty();    }        private Collection<String> getDerivedProjectionTexts(final SelectStatementContext selectStatementContext) {        Collection<String> result = new LinkedList<>();        for (Projection each : selectStatementContext.getProjectionsContext().getProjections()) {            if (each instanceof AggregationProjection && !((AggregationProjection) each).getDerivedAggregationProjections().isEmpty()) {                result.addAll(((AggregationProjection) each).getDerivedAggregationProjections().stream().map(this::getDerivedProjectionText).collect(Collectors.toList()));            } else if (each instanceof DerivedProjection) {                result.add(getDerivedProjectionText(each));            }        }        return result;    }

首先必须是select语句,其次能够是:

  • 聚合函数,同时须要派生新的函数,比方avg函数;
  • 派生关键字,比方order by,group by等;

比方avg函数应用:

select avg(user_id) from t_order where order_id = 101

改写的SQL如下所示:

Actual SQL: ds0 ::: select avg(user_id) , COUNT(user_id) AS AVG_DERIVED_COUNT_0 , SUM(user_id) AS AVG_DERIVED_SUM_0 from t_order1 where order_id = 101Actual SQL: ds1 ::: select avg(user_id) , COUNT(user_id) AS AVG_DERIVED_COUNT_0 , SUM(user_id) AS AVG_DERIVED_SUM_0 from t_order1 where order_id = 101

比方order by应用:

select user_id from t_order order by order_id

改写的SQL如下所示:

Actual SQL: ds0 ::: select user_id , order_id AS ORDER_BY_DERIVED_0 from t_order0 order by order_idActual SQL: ds0 ::: select user_id , order_id AS ORDER_BY_DERIVED_0 from t_order1 order by order_idActual SQL: ds1 ::: select user_id , order_id AS ORDER_BY_DERIVED_0 from t_order0 order by order_idActual SQL: ds1 ::: select user_id , order_id AS ORDER_BY_DERIVED_0 from t_order1 order by order_id

须要order by中指定的排序字段在select前面没有,这时候会派生一个;

OrderByTokenGenerator

OrderByToken生成器,主动生成order by,失效条件:

    public boolean isGenerateSQLToken(final SQLStatementContext sqlStatementContext) {        return sqlStatementContext instanceof SelectStatementContext && ((SelectStatementContext) sqlStatementContext).getOrderByContext().isGenerated();    }

首先必须是select语句,其次有主动生成的order by内容;

比方如下SQL:

select distinct user_id from t_order

改写的SQL如下所示,改写主动增加了ORDER BY

Actual SQL: ds0 ::: select distinct user_id from t_order0 ORDER BY user_id ASC Actual SQL: ds0 ::: select distinct user_id from t_order1 ORDER BY user_id ASC Actual SQL: ds1 ::: select distinct user_id from t_order0 ORDER BY user_id ASC Actual SQL: ds1 ::: select distinct user_id from t_order1 ORDER BY user_id ASC 

AggregationDistinctTokenGenerator

AggregationDistinctToken生成器,相似DistinctProjectionPrefixTokenGenerator

    public boolean isGenerateSQLToken(final SQLStatementContext sqlStatementContext) {        return sqlStatementContext instanceof SelectStatementContext;    }

是否生成SQLToken没做查看,然而在生成SQLToken的时候会查看是否有聚合函数和Distinct去重;

IndexTokenGenerator

IndexToken生成器,次要用在应用索引的中央,对索引名称重命名,用在sql server,PostgreSQL中,Mysql并没有应用;

    public boolean isGenerateSQLToken(final SQLStatementContext sqlStatementContext) {        return sqlStatementContext instanceof IndexAvailable && !((IndexAvailable) sqlStatementContext).getIndexes().isEmpty();    }

OffsetTokenGenerator

OffsetToken生成器,次要作用于分页,对应limitoffset关键字

    public boolean isGenerateSQLToken(final SQLStatementContext sqlStatementContext) {        return sqlStatementContext instanceof SelectStatementContext                && ((SelectStatementContext) sqlStatementContext).getPaginationContext().getOffsetSegment().isPresent()                && ((SelectStatementContext) sqlStatementContext).getPaginationContext().getOffsetSegment().get() instanceof NumberLiteralPaginationValueSegment;    }

如下通过limit实现分页查问:

SELECT * FROM t_order LIMIT 1,2

改写的SQL如下所示,分页参数改写成了0,3

Actual SQL: ds0 ::: SELECT * FROM t_order0 LIMIT 0,3Actual SQL: ds0 ::: SELECT * FROM t_order1 LIMIT 0,3Actual SQL: ds1 ::: SELECT * FROM t_order0 LIMIT 0,3Actual SQL: ds1 ::: SELECT * FROM t_order1 LIMIT 0,3

RowCountTokenGenerator

RowCountToken生成器,同样作用于分页,对应limitcount关键字

    public boolean isGenerateSQLToken(final SQLStatementContext sqlStatementContext) {        return sqlStatementContext instanceof SelectStatementContext                && ((SelectStatementContext) sqlStatementContext).getPaginationContext().getRowCountSegment().isPresent()                && ((SelectStatementContext) sqlStatementContext).getPaginationContext().getRowCountSegment().get() instanceof NumberLiteralPaginationValueSegment;    }

实例和下面的OffsetTokenGenerator统一;

GeneratedKeyForUseDefaultInsertColumnsTokenGenerator

UseDefaultInsertColumnsToken生成器,insert的sql中并未蕴含表的列名称

    public final boolean isGenerateSQLToken(final SQLStatementContext sqlStatementContext) {        return sqlStatementContext instanceof InsertStatementContext && ((InsertStatementContext) sqlStatementContext).getGeneratedKeyContext().isPresent()                && ((InsertStatementContext) sqlStatementContext).getGeneratedKeyContext().get().isGenerated() && isGenerateSQLToken(((InsertStatementContext) sqlStatementContext).getSqlStatement());    }        protected boolean isGenerateSQLToken(final InsertStatement insertStatement) {        return insertStatement.useDefaultColumns();    }

应用以上TokenGenerator须要多个条件包含:

  • 必须是insert语句;
  • 配置了KeyGeneratorConfiguration,如下所示:

    orderTableRuleConfig.setKeyGeneratorConfig(new KeyGeneratorConfiguration("SNOWFLAKE", "id"));
  • 主键是主动生成的也就是用户没有被动生成主键;
  • 应用默认的字段,insert的sql中并未蕴含表的列名称;

上面看一条insert sql的实例:

insert into t_order values (1,1)

改写的SQL如下所示:

Actual SQL: ds1 ::: insert into t_order1(user_id, order_id, id) values (1, 1, 600986707608731648)

GeneratedKeyInsertColumnTokenGenerator

GeneratedKeyInsertColumnToken生成器,insert的sql中蕴含表的列名称,其余根本同上

    protected boolean isGenerateSQLToken(final InsertStatement insertStatement) {        Optional<InsertColumnsSegment> sqlSegment = insertStatement.getInsertColumns();        return sqlSegment.isPresent() && !sqlSegment.get().getColumns().isEmpty();    }

上面看一条insert sql的实例:

insert into t_order (user_id,order_id) values (1,1)

改写的SQL如下所示:

Actual SQL: ds1 ::: insert into t_order1 (user_id,order_id, id) values (1, 1, 600988204400640000)

GeneratedKeyAssignmentTokenGenerator

GeneratedKeyAssignmentToken生成器,次要用于insert...set操作,同上无需指定主键

    protected boolean isGenerateSQLToken(final InsertStatement insertStatement) {        return insertStatement.getSetAssignment().isPresent();    }

上面看一条insert set的实例:

insert into t_order set user_id = 111,order_id=111

改写的SQL如下所示:

Actual SQL: ds1 ::: insert into t_order1 set user_id = 111,order_id=111, id = 600999588391813120

ShardingInsertValuesTokenGenerator

InsertValuesToken生成器,有插入值做分片解决

    public boolean isGenerateSQLToken(final SQLStatementContext sqlStatementContext) {        return sqlStatementContext instanceof InsertStatementContext && !(((InsertStatementContext) sqlStatementContext).getSqlStatement()).getValues().isEmpty();    }

上面看一条insert sql实例:

insert into t_order values (95,1,1),(96,2,2)

改写的SQL如下所示:

Actual SQL: ds1 ::: insert into t_order1 values (95, 1, 1)Actual SQL: ds0 ::: insert into t_order0 values (96, 2, 2)

GeneratedKeyInsertValuesTokenGenerator

InsertValuesToken生成器,只有有插入值,就会存在此生成器,前提不能指定主键,应用主动生成

    protected boolean isGenerateSQLToken(final InsertStatement insertStatement) {        return !insertStatement.getValues().isEmpty();    }

上面看一条insert sql实例:

insert into t_order values (1,1),(2,2)

改写的SQL如下所示:

Actual SQL: ds1 ::: insert into t_order1(user_id, order_id, id) values (1, 1, 601005570564030465)Actual SQL: ds0 ::: insert into t_order0(user_id, order_id, id) values (2, 2, 601005570564030464)

生成SQLToken

以上别离介绍了常见的几种TokenGenerator,以及在哪种条件下能够生成SQLToken,本节重点看一下是如何生成SQLToken的,别离提供了两个接口类:

  • CollectionSQLTokenGenerator:对应的TokenGenerator会生成SQLToken列表;实现类包含:AggregationDistinctTokenGeneratorTableTokenGeneratorIndexTokenGenerator
  • OptionalSQLTokenGenerator:对应的TokenGenerator会生成惟一的SQLToken;实现类包含:DistinctProjectionPrefixTokenGeneratorProjectionsTokenGeneratorOrderByTokenGeneratorOffsetTokenGeneratorRowCountTokenGeneratorGeneratedKeyForUseDefaultInsertColumnsTokenGeneratorGeneratedKeyInsertColumnTokenGeneratorGeneratedKeyAssignmentTokenGeneratorShardingInsertValuesTokenGeneratorGeneratedKeyInsertValuesTokenGenerator等;
public interface CollectionSQLTokenGenerator<T extends SQLStatementContext> extends SQLTokenGenerator {    Collection<? extends SQLToken> generateSQLTokens(T sqlStatementContext);}public interface OptionalSQLTokenGenerator<T extends SQLStatementContext> extends SQLTokenGenerator {    SQLToken generateSQLToken(T sqlStatementContext);}

上面重点剖析一下每种生成器是如何生成对应的SQLToken的;

TableTokenGenerator

首先从解析引擎中获取的SQLStatementContext中获取所有表信息SimpleTableSegment,而后查看以后表是否配置了TableRule,如果都能满足那么会创立一个TableToken

public final class TableToken extends SQLToken implements Substitutable, RouteUnitAware {    @Getter    private final int stopIndex;//完结地位,开始地位在父类SQLToken中    private final IdentifierValue identifier; //表标识符信息     private final SQLStatementContext sqlStatementContext;//SQLStatement上下文    private final ShardingRule shardingRule;//定义的分片规定}

如果SQL中存在多个表信息,这里会生成TableToken列表;因为须要做表名称替换解决,所以须要如上定义的相干参数,不便后续做重写解决;

DistinctProjectionPrefixTokenGenerator

去重解决改写后的SQL须要在指定地位增加DISTINCT关键字:

public final class DistinctProjectionPrefixToken extends SQLToken implements Attachable {    public DistinctProjectionPrefixToken(final int startIndex) {        super(startIndex);    }}

这里只须要提供一个开始地位即可,也就是开始插入DISTINCT关键字的地位;

ProjectionsTokenGenerator

聚合函数和派生关键字生成器,能够查看枚举类DerivedColumn

public enum DerivedColumn {    AVG_COUNT_ALIAS("AVG_DERIVED_COUNT_"),     AVG_SUM_ALIAS("AVG_DERIVED_SUM_"),     ORDER_BY_ALIAS("ORDER_BY_DERIVED_"),     GROUP_BY_ALIAS("GROUP_BY_DERIVED_"),    AGGREGATION_DISTINCT_DERIVED("AGGREGATION_DISTINCT_DERIVED_");}

比方下面介绍的实例中avg会派生成:

  • COUNT(user_id) AS AVG_DERIVED_COUNT_0 ;
  • SUM(user_id) AS AVG_DERIVED_SUM_0 ;

最初所有派生保留到一个文本中,封装到ProjectionsToken中:

public final class ProjectionsToken extends SQLToken implements Attachable {    private final Collection<String> projections;//派生列表    public ProjectionsToken(final int startIndex, final Collection<String> projections) {        super(startIndex);        this.projections = projections;    }}

OrderByTokenGenerator

重点是生成OrderByContext类,具体能够查看OrderByContextEngine,其中会判断isDistinctRow;最终会把所有须要生成order by的字段和排序形式保留到OrderByToken中:

public final class OrderByToken extends SQLToken implements Attachable {    private final List<String> columnLabels = new LinkedList<>();  //order by字段    private final List<OrderDirection> orderDirections = new LinkedList<>();//排序形式    public OrderByToken(final int startIndex) {        super(startIndex);    }}

AggregationDistinctTokenGenerator

条件和DistinctProjectionPrefixTokenGenerator统一,前者次要通过DistinctProjectionPrefixToken生成DISTINCT关键字;而此生成器是给相干字段增加派生的别名,如下面实例中的AGGREGATION_DISTINCT_DERIVED_0

select DISTINCT user_id AS AGGREGATION_DISTINCT_DERIVED_0 from t_order1 where order_id = 101
public final class AggregationDistinctToken extends SQLToken implements Substitutable {    private final String columnName;//字段名称    private final String derivedAlias;//别名}

IndexTokenGenerator

如果SQL是一个IndexAvailable,并且蕴含索引信息,则会生成一个IndexToken,其中信息和TableToken统一;常见的IndexAvailable蕴含:AlterIndexStatementContextCreateIndexStatementContextCreateTableStatementContextDropIndexStatementContext

OffsetTokenGenerator

次要是对limit关键字中的offset值进行重置解决,解决的信息蕴含在OffsetToken中:

public final class OffsetToken extends SQLToken implements Substitutable {    @Getter    private final int stopIndex;    private final long revisedOffset; //订正过的offset}

RowCountTokenGenerator

次要是对limit关键字中的count值进行重置解决,解决的信息蕴含在RowCountToken中:

public final class RowCountToken extends SQLToken implements Substitutable {    @Getter    private final int stopIndex;    private final long revisedRowCount; //订正过的rowcout}

GeneratedKeyForUseDefaultInsertColumnsTokenGenerator

因为配置了KeyGeneratorConfiguration,insert语句中会主动生成组件,解析的时候会在InsertStatementContext中生成GeneratedKeyContext,外面蕴含了主键字段,以及主键值;此生成器是在insert中没有指定字段的状况,所有字段会被保留到UseDefaultInsertColumnsToken中,字段列表是有序的,须要将生成的id挪动到列表的开端;

public final class UseDefaultInsertColumnsToken extends SQLToken implements Attachable {    private final List<String> columns;//字段列表:user_id,order_id,id}

GeneratedKeyInsertColumnTokenGenerator

此生成器是在insert中指定了字段的状况,须要改写的中央是增加主键字段名称即可,保留到GeneratedKeyInsertColumnToken中:

public final class GeneratedKeyInsertColumnToken extends SQLToken implements Attachable {    private final String column;//主键字段:id}

GeneratedKeyAssignmentTokenGenerator

此生成器是在insert set中应用,须要增加主键、值,然而因为能够由用户指定parameter,所以这里会依据是否配置了parameter来生成不同的Token:

  • LiteralGeneratedKeyAssignmentToken:没有parameter的状况,提供主键名称和主键值:

    public final class LiteralGeneratedKeyAssignmentToken extends GeneratedKeyAssignmentToken {    private final Object value;//主键值    public LiteralGeneratedKeyAssignmentToken(final int startIndex, final String columnName, final Object value) {        super(startIndex, columnName);//开始地位和主键名称        this.value = value;    }
  • ParameterMarkerGeneratedKeyAssignmentToken:指定了parameter,只须要提供主键名称即可,值从parameter中获取:

    public final class ParameterMarkerGeneratedKeyAssignmentToken extends GeneratedKeyAssignmentToken {    public ParameterMarkerGeneratedKeyAssignmentToken(final int startIndex, final String columnName) {        super(startIndex, columnName);//开始地位和主键名称    }}

ShardingInsertValuesTokenGenerator

插入的值能够是一条也能够是多条,每条数据都会和DataNode绑定,也就是属于哪个库属于哪个表,这里数据和DataNode的绑定被包装到了ShardingInsertValue中:

public final class ShardingInsertValue {    private final Collection<DataNode> dataNodes;//数据节点信息    private final List<ExpressionSegment> values;//数据信息}

最初所有数据包装到ShardingInsertValuesToken中;

GeneratedKeyInsertValuesTokenGenerator

此生成器会对后面ShardingInsertValuesTokenGenerator生成的ShardingInsertValue进行再加工解决,次要针对没有指定主键的状况,对其中的values减少一个ExpressionSegment保留主键信息;

执行改写

通过以上两步曾经筹备好了所有SQLToken,上面就能够执行改写操作了,提供了改写引擎SQLRouteRewriteEngine,两个重要的参数别离是:

  • SQLRewriteContext:SQL改写上下文,生成的SQLToken都存在上下文中;
  • RouteResult:路由引擎产生的后果;

有了以上两个外围参数就能够执行改写操作了:

    public Map<RouteUnit, SQLRewriteResult> rewrite(final SQLRewriteContext sqlRewriteContext, final RouteResult routeResult) {        Map<RouteUnit, SQLRewriteResult> result = new LinkedHashMap<>(routeResult.getRouteUnits().size(), 1);        for (RouteUnit each : routeResult.getRouteUnits()) {            result.put(each, new SQLRewriteResult(new RouteSQLBuilder(sqlRewriteContext, each).toSQL(), getParameters(sqlRewriteContext.getParameterBuilder(), routeResult, each)));        }        return result;    }

遍历每个路由单元RouteUnit,每个路由单元都对应一条SQL语句;依据路由单元和SQLToken列表生成改写这条SQL;能够发现这里执行了RouteSQLBuilder中的toSQL办法:

    public final String toSQL() {        if (context.getSqlTokens().isEmpty()) {            return context.getSql();        }        Collections.sort(context.getSqlTokens());        StringBuilder result = new StringBuilder();        result.append(context.getSql().substring(0, context.getSqlTokens().get(0).getStartIndex()));        for (SQLToken each : context.getSqlTokens()) {            result.append(getSQLTokenText(each));            result.append(getConjunctionText(each));        }        return result.toString();    }        protected String getSQLTokenText(final SQLToken sqlToken) {        if (sqlToken instanceof RouteUnitAware) {            return ((RouteUnitAware) sqlToken).toString(routeUnit);        }        return sqlToken.toString();    }        private String getConjunctionText(final SQLToken sqlToken) {        return context.getSql().substring(getStartIndex(sqlToken), getStopIndex(sqlToken));    }

首先对SQLToken列表进行排序,排序依据startIndex从小到大排序;而后从截取从0地位到第一个SQLToken的开始地位,这一部分的sql是无需任何改变的;接下来就是遍历SQLToken,会判断SQLToken是否为RouteUnitAware,如果是会做路由替换,比方物理表替换逻辑表;最初在截取两个SQLToken的两头局部,遍历所有SQLToken,返回从新拼接的SQL;

以一条查问SQL为例:

select user_id,order_id from t_order where order_id = 101

第一次截取从0地位到第一个SQLToken的开始地位,后果为:

select user_id,order_id from 

而后遍历SQLToken,以后只有一个TableToken,并且是一个RouteUnitAware,表格会被替换解决:

t_order->t_order1

截取最初残余的局部:

 where order_id = 101

而后把每个局部拼接到一起,组成能被数据库执行的SQL:

select user_id,order_id from t_order1 where order_id = 101

上面简略介绍一下每种SQLToken是如何执行改写操作的;

TableToken

TableToken是一个RouteUnitAware,所以在重写的时候传入了路由单元RouteUnit,须要依据路由单元来决定逻辑表应该改写为哪个物理表;

DistinctProjectionPrefixToken

次要对聚合函数和去重的解决,这里只是把以后Token,改写成一个DISTINCT ,这里其实只是做了局部解决,聚合函数并没有体现,所以整个流程中还有归并这流程,很多聚合函数都须要在归并流程中解决;

ProjectionsToken

聚合函数须要做派生解决,只须要把派生的字符,拼接起来即可,比方:AVG函数派生的AVG_DERIVED_COUNTAVG_DERIVED_SUM,同样AVG聚合函数同样须要到归并流程中解决;

OrderByToken

对外面保留的order by字段和对应的排序形式,进行遍历解决,后果相似如下:

order by column1 desc,column2 desc...

AggregationDistinctToken

次要对聚合函数和去重的解决,DistinctProjectionPrefixToken拼接DISTINCT 关键字,此Token拼接别名;组合起来如下所示:

select DISTINCT user_id AS AGGREGATION_DISTINCT_DERIVED_0 

IndexToken

IndexToken是一个RouteUnitAware,依据路由单元改写索引名称;

OffsetToken

通过订正过的offset,改写limit中的offset;

RowCountToken

通过订正过的RowCount,改写limit中的count;

GeneratedKeyInsertColumnToken

这个改写只是针对字段名称,并不是值,值是须要做路由解决的,即在现有的字段列表中增加主键名称;

(user_id)->(user_id,id)

UseDefaultInsertColumnsToken

此种模式是在插入数据的时候没有指定任何字段,所有这里会生成所有字段名称,同时会增加主键名称

()->(user_id,id)

GeneratedKeyAssignmentToken

上大节中介绍了此Token蕴含两个子类,别离对应insert set有参数和无参数的状况;这里的改写是蕴含名称和值的

set user_id = 11,id = 1234567

InsertValuesToken

这里区别以上GeneratedKeyInsertColumnTokenUseDefaultInsertColumnsToken对字段的解决,此Token是对值的解决,值必定要路由解决,所有此Token也是RouteUnitAware

总结

本文重点介绍了须要改写SQL的13种状况,其实就是找出所有须要被改写的SQL,而后记录到不同的Token中,同时还会记录以后Token在原SQL中对应的startIndex和stopIndex,这样能够做改写解决,替换到原理地位的SQL;通过遍历整个Token列表最终达到改写SQL的所有部位;当然整个改写有些中央是扭转了原来SQL的含意比方聚合函数,所以ShardingSphere-JDBC还提供了专门的归并引擎,用来保障SQL的完整性。

参考

https://shardingsphere.apache...

感激关注

能够关注微信公众号「回滚吧代码」,第一工夫浏览,文章继续更新;专一Java源码、架构、算法和面试。