共计 19197 个字符,预计需要花费 48 分钟才能阅读完成。
前言
上文 ShardingSphere-JDBC 分片路由引擎中介绍了分片流程中的路由引擎,最终获取了路由后果;本文要介绍的改写引擎须要应用路由后果来对 SQL 进行改写,改写成能够被正确的分库分表可能执行的 SQL;这外面波及对各种 SQL 改写的状况泛滥,接下来本文会进行一一剖析。
改写装璜器
重写引擎同样应用了装璜器模式,提供了接口类SQLRewriteContextDecorator
,实现类包含:
- ShardingSQLRewriteContextDecorator:分片 SQL 改写装璜器;
- ShadowSQLRewriteContextDecorator:影子库 SQL 改写装璜器;
- EncryptSQLRewriteContextDecorator:数据加密 SQL 改写装璜器;
默认加载 ShardingSQLRewriteContextDecorator
和EncryptSQLRewriteContextDecorator
,应用 java.util.ServiceLoader
来加载重写装璜器,须要在 META-INF/services/
中指定具体的实现类:
org.apache.shardingsphere.sharding.rewrite.context.ShardingSQLRewriteContextDecorator
org.apache.shardingsphere.encrypt.rewrite.context.EncryptSQLRewriteContextDecorator
装璜器能够叠加,所以提供了优先级性能OrderAware
,同时每个装璜器都有对应的规定,大抵如下所示:
装璜器 -SQLRewriteContextDecorator | 规定 -BaseRule | 优先级 -Order |
---|---|---|
ShardingSQLRewriteContextDecorator | ShardingRule | 0 |
EncryptSQLRewriteContextDecorator | EncryptRule | 20 |
ShadowSQLRewriteContextDecorator | ShadowRule | 30 |
只有在配置了相干 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 = 101
Actual 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 = 101
Actual 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_id
Actual SQL: ds0 ::: select user_id , order_id AS ORDER_BY_DERIVED_0 from t_order1 order by order_id
Actual SQL: ds1 ::: select user_id , order_id AS ORDER_BY_DERIVED_0 from t_order0 order by order_id
Actual 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
生成器,次要作用于分页,对应 limit
的offset
关键字
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,3
Actual SQL: ds0 ::: SELECT * FROM t_order1 LIMIT 0,3
Actual SQL: ds1 ::: SELECT * FROM t_order0 LIMIT 0,3
Actual SQL: ds1 ::: SELECT * FROM t_order1 LIMIT 0,3
RowCountTokenGenerator
RowCountToken
生成器,同样作用于分页,对应 limit
的count
关键字
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
列表;实现类包含:AggregationDistinctTokenGenerator
、TableTokenGenerator
、IndexTokenGenerator
等 - OptionalSQLTokenGenerator:对应的
TokenGenerator
会生成惟一的SQLToken
;实现类包含:DistinctProjectionPrefixTokenGenerator
、ProjectionsTokenGenerator
、OrderByTokenGenerator
、OffsetTokenGenerator
、RowCountTokenGenerator
、GeneratedKeyForUseDefaultInsertColumnsTokenGenerator
、GeneratedKeyInsertColumnTokenGenerator
、GeneratedKeyAssignmentTokenGenerator
、ShardingInsertValuesTokenGenerator
、GeneratedKeyInsertValuesTokenGenerator
等;
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
蕴含:AlterIndexStatementContext
、CreateIndexStatementContext
、CreateTableStatementContext
、DropIndexStatementContext
;
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_COUNT
、AVG_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
这里区别以上 GeneratedKeyInsertColumnToken
和UseDefaultInsertColumnsToken
对字段的解决,此 Token 是对值的解决,值必定要路由解决,所有此 Token 也是RouteUnitAware
;
总结
本文重点介绍了须要改写 SQL 的 13 种状况,其实就是找出所有须要被改写的 SQL,而后记录到不同的 Token 中,同时还会记录以后 Token 在原 SQL 中对应的 startIndex 和 stopIndex,这样能够做改写解决,替换到原理地位的 SQL;通过遍历整个 Token 列表最终达到改写 SQL 的所有部位;当然整个改写有些中央是扭转了原来 SQL 的含意比方聚合函数,所以 ShardingSphere-JDBC 还提供了专门的归并引擎,用来保障 SQL 的完整性。
参考
https://shardingsphere.apache…
感激关注
能够关注微信公众号「回滚吧代码」,第一工夫浏览,文章继续更新;专一 Java 源码、架构、算法和面试。