最後の記事ではルーティングエンジンを説明し、「ShardingSphereソースルーティングエンジン(g)を解決するために、」私たちは、そのSQLの書き換え(リライト)にShardingSphereシャード方法BaseShardingEngine内の別の重要な概念を見ました。開発者は論理と論理ライブラリのテーブル書き込みSQLに直面しているため、ルーティングの後に位置するSQL書き換えは、また、サブライブラリーサブテーブルの枠組み全体SQL実行プロセスの重要な側面は、通常れる、直接実際のデータベースで実行することができませんSQLデータベースは、実際の右に実行することができるSQLを書き換えるために、SQLロジックを書き換えます。
私たちは書き換えロジックを実行するためのrewriteAndConvert BaseShardingEngine方法を見てみましょう:
プライベートコレクション<RouteUnit> rewriteAndConvert(最終文字列のSQL 、最終リストの<Object> パラメータ、最終 SQLRouteResult sqlRouteResult ){
// ビルドSQLRewriteContext
SQLRewriteContext sqlRewriteContext = 新しい SQLRewriteContext(のmetaData .getRelationMetas()、sqlRouteResult .getSqlStatementContext()、SQL 、パラメータ)。
// ビルドShardingSQLRewriteContextDecorator にSQLRewriteContextが飾ら
新しい ShardingSQLRewriteContextDecorator(shardingRule 、sqlRouteResult ).decorate(sqlRewriteContext )。
// データの脱感作列かどうかを判断します
ブール isQueryWithCipherColumn = shardingProperties <ブール>のgetValue(ShardingPropertiesConstant。。QUERY_WITH_CIPHER_COLUMN);
// ビルドEncryptSQLRewriteContextDecorator にSQLRewriteContextが飾ら
新しい EncryptSQLRewriteContextDecorator(shardingRule .getEncryptRule()、isQueryWithCipherColumn ).decorate(sqlRewriteContext )。
// 生成SQLTokensを
sqlRewriteContext .generateSQLTokens()。
コレクション<RouteUnit> 結果= 新しい LinkedHashSetの<>();
用(RoutingUnit それぞれ:sqlRouteResult {.getRoutingResult()getRoutingUnits())。
// ビルドShardingSQLRewriteEngine
ShardingSQLRewriteEngine sqlRewriteEngine = 新しい ShardingSQLRewriteEngine(shardingRule 、sqlRouteResult .getShardingConditions()、それぞれ)。
// 書き換えを実行します
SQLRewriteResult sqlRewriteResult = sqlRewriteEngine .rewrite(sqlRewriteContext )。
// 書き換え結果を保存します
結果(.add 新しい RouteUnit(各.getDataSourceName()、新しい SQLUnit(sqlRewriteResult .getSql()、sqlRewriteResult .getParameters())))。
}
戻り値の 結果;
}
このコードは、多くのコンテンツではありませんが、彼らは、SQLの完全な説明は、プロセス全体を書き換える達成んが。コアクラスがたくさん含まれ、我々はいくつかの記事の価値はその詳細な拡大を過ごしました。以下のように以前、我々は、関連するコアクラスの全体構成を与えます:
チャートについては、我々はSQLRewriteContextはそれに依存する非常に重要なクラス、SQLRewriteEngine、SQLRewriteContextDecoratorコア・インタフェースで、書き換えエンジンを発見しました。話す命名から、SQLRewriteContextは、コンテキストオブジェクトである、それは考えられる確かにSQLのデータ書き換えに関連する多くの情報を保持し、次のように、その変数で見てみましょうが定義されています。
民間 最終 RelationMetasのrelationMetas 。
民間 最終 SQLStatementContext sqlStatementContext 。
民間 最終文字列のSQL 。
民間 最終リストの<Object> パラメータ。
民間 最終リスト<SQLToken> sqlTokens = 新しい LinkedListは<>();
民間 最終 ParameterBuilder parameterBuilder 。
@Getter (ACCESSLEVEL。NONE)
民間 最終 SQLTokenGenerators sqlTokenGenerators = 新しい SQLTokenGenerators();
ここでは、我々はすでにまた、SQLStatementContextを導入された新しいSQLTokenとSQLTokenGeneratorsを見て参照してください。コンテンツの進化と、これらのオブジェクトは1つずつ紹介しています。ここでは、最初の明確なSQLRewriteContextは、この情報が異なるアプリケーションシナリオに応じて変化するSQLリライトのための情報を保存しますが、ビルドプロセス。
見てみましょうSQLTokenが、ここで重要なの書き換えエンジン内のオブジェクトの高いオブジェクト、SQLRewriteEngineはSQLToken実装SQLリライトを基づいています。次のようにSQLTokenが定義されて:
@RequiredArgsConstructor
@Getter
パブリック 抽象 クラス SQLTokenの実装匹敵<SQLToken> {
民間 最終 int型の 開始インデックス。
@オーバーライド
公共 最終 INTのcompareTo(最終 SQLToken sqlToken ){
リターン のstartIndex - sqlToken .getStartIndex();
}
}
SQLTokenは実際に見ることが抽象クラスであり、ShardingSphereに、階層構造を以下に示すSQLTokenサブクラスが多数存在します。
(パッケージ名を暗号化も含まれている)これらのSQLTokenのほとんどは、SQLで(パッケージ名は書き換えが含まれている)に関連書き換え、書き換えに基づいて、後にも脱感作機能に関連するデータに対処したいのいくつかは、データ提供脱感作はShardingSphereです私たちは、後でトピックを導入している、非常に便利な機能です。同時に、いくつかは、ここで他の人がシャーディング・コア・リライトプロジェクトを設置している間、shardingsphere書き換え-Engineプロジェクトを設置SQLToken、この点も注意が必要です。
共通シーンのSQL書き換え組み合わせは、いくつかのSQLToken上図の意味は、私たちは文字通りの意味から理解することができます。
例如,对于INSERT语句而言,如果使用数据库自增主键,是无需写入主键字段的。但数据库的自增主键是无法满足分布式场景下的主键唯一性,因此ShardingSphere提供了分布式自增主键的生成策略,并且可以通过补列,让使用方无需改动现有代码,即可将分布式自增主键透明的替换数据库现有的自增主键。关于ShardingSphere中的分布式主键的介绍可以回顾《ShardingSphere源码解析之分布式主键》中的内容。举例说明,假设表t_order的主键是order_id,原始的SQL为:
INSERT INTO t_order (`field1`, `field2`) VALUES (10, 1);
可以看到,上述SQL中并未包含自增主键,需要数据库自行填充。ShardingSphere配置自增主键后,SQL将改写为:
INSERT INTO t_order (`field1`, `field2`, order_id) VALUES (10, 1, xxxxx);
改写后的SQL将在INSERT语句的最后部分增加主键列名称以及自动生成的自增主键值。上述SQL中的xxxxx表示自动生成的自增主键值。
从命名上看,GeneratedKeyInsertColumnToken对应上述的自动主键填充的场景,这实际上属于常见的一种SQL改写策略,即补列。GeneratedKeyInsertColumnToken的实现如下所示:
public final class GeneratedKeyInsertColumnToken extends SQLToken implements Attachable {
private final String column;
public GeneratedKeyInsertColumnToken(final int startIndex, final String column) {
super(startIndex);
this.column = column;
}
@Override
public String toString() {
return String.format(", %s", column);
}
}
可以看到这里多了一个column变量用于指定主键的所在列。我们再来跟踪GeneratedKeyInsertColumnToken的构造函数调用情况,发现在GeneratedKeyInsertColumnTokenGenerator中创建了GeneratedKeyInsertColumnToken。顾名思义,GeneratedKeyInsertColumnTokenGenerator是一种TokenGenerator,专门负责生成具体的Token。TokenGenerator接口定义如下:
public interface SQLTokenGenerator {
//判断是否要生成SQLToken
boolean isGenerateSQLToken(SQLStatementContext sqlStatementContext);
}
该接口还有两个子接口,分别是负责生成单个SQLToken的OptionalSQLTokenGenerator和负责生成批量SQLToken的CollectionSQLTokenGenerator,如下所示:
public interface OptionalSQLTokenGenerator extends SQLTokenGenerator {
//生成单个SQLToken
SQLToken generateSQLToken(SQLStatementContext sqlStatementContext);
}
public interface CollectionSQLTokenGenerator extends SQLTokenGenerator {
//生成批量SQLToken
Collection<? extends SQLToken> generateSQLTokens(SQLStatementContext sqlStatementContext);
}
在ShardingSphere,和SQLToken一样,TokenGenerator的类层结构也比较复杂,类层结构如下所示:
对于GeneratedKeyInsertColumnTokenGenerator而言,它还有一个抽象的基类,即上图中的BaseGeneratedKeyTokenGenerator,其实现如下所示:
public abstract class BaseGeneratedKeyTokenGenerator implements OptionalSQLTokenGenerator, SQLRouteResultAware {
private SQLRouteResult sqlRouteResult;
@Override
public final boolean isGenerateSQLToken(final SQLStatementContext sqlStatementContext) {
return sqlStatementContext instanceof InsertSQLStatementContext && sqlRouteResult.getGeneratedKey().isPresent()
&& sqlRouteResult.getGeneratedKey().get().isGenerated() && isGenerateSQLToken((InsertStatement) sqlStatementContext.getSqlStatement());
}
protected abstract boolean isGenerateSQLToken(InsertStatement insertStatement);
@Override
public final SQLToken generateSQLToken(final SQLStatementContext sqlStatementContext) {
Preconditions.checkState(sqlRouteResult.getGeneratedKey().isPresent());
return generateSQLToken(sqlStatementContext, sqlRouteResult.getGeneratedKey().get());
}
protected abstract SQLToken generateSQLToken(SQLStatementContext sqlStatementContext, GeneratedKey generatedKey);
}
这个抽象类留下了两个模板方法isGenerateSQLToken和generateSQLToken交由子类进行实现,在GeneratedKeyInsertColumnTokenGenerator中,这两个方法的实现如下所示:
public final class GeneratedKeyInsertColumnTokenGenerator extends BaseGeneratedKeyTokenGenerator {
@Override
protected boolean isGenerateSQLToken(final InsertStatement insertStatement) {
Optional<InsertColumnsSegment> sqlSegment = insertStatement.findSQLSegment(InsertColumnsSegment.class);
return sqlSegment.isPresent() && !sqlSegment.get().getColumns().isEmpty();
}
@Override
protected GeneratedKeyInsertColumnToken generateSQLToken(final SQLStatementContext sqlStatementContext, final GeneratedKey generatedKey) {
Optional<InsertColumnsSegment> sqlSegment = sqlStatementContext.getSqlStatement().findSQLSegment(InsertColumnsSegment.class);
Preconditions.checkState(sqlSegment.isPresent());
return new GeneratedKeyInsertColumnToken(sqlSegment.get().getStopIndex(), generatedKey.getColumnName());
}
}
我们看到在上述generateSQLToken方法中,通过利用在SQL解析引擎中获取的InsertColumnsSegment以及从用于生成分布式主键的GeneratedKey中获取对应的主键列,我们就可以构建一个GeneratedKeyInsertColumnToken。
关于SQLToken的基本概念以及它的其中一个实现类GeneratedKeyInsertColumnToken就介绍到这里,关于其他SQLToken实现类我们不会全部展开,部分会在后面的内容中进行穿插介绍。
作为总结,从今天起,我们正式进入到ShardingSphere中关于改写引擎部分的讲解,本文先从整体结构上给出了改写引擎部分的核心类,然后重点分析了改写引擎的上下文对象SQLRewriteContext中与SQLToken相关的内容。
更多内容可以关注我的公众号:程序员向架构师转型。