今天我们将继续围绕SQLRewriteContext展开讨论。在上一篇中,我们知道SQLRewriteContext是一个上下文对象,保存着与SQL改写相关的很多数据信息。同时,对于这些信息,其构建过程会根据不同的应用场景而有所不同。基于这种需求,ShardingSphere的改写引擎提供了SQLRewriteContextDecorator接口,定义如下:
public interface SQLRewriteContextDecorator {
void decorate(SQLRewriteContext sqlRewriteContext);
}
顾名思义,SQLRewriteContextDecorator是一种装饰器模式(Decorator Pattern)的具体应用,在ShardingSphere中只存在两种具体的SQLRewriteContextDecorator,其类层结构如下所示:
我们来看一下ShardingSQLRewriteContextDecorator的实现,如下所示:
public final class ShardingSQLRewriteContextDecorator implements SQLRewriteContextDecorator {
private final ShardingRule shardingRule;
private final SQLRouteResult sqlRouteResult;
@Override
public void decorate(final SQLRewriteContext sqlRewriteContext) {
//参数改写
for (ParameterRewriter each : new ShardingParameterRewriterBuilder(shardingRule, sqlRouteResult).getParameterRewriters(sqlRewriteContext.getRelationMetas())) {
if (!sqlRewriteContext.getParameters().isEmpty() && each.isNeedRewrite(sqlRewriteContext.getSqlStatementContext())) {
each.rewrite(sqlRewriteContext.getParameterBuilder(), sqlRewriteContext.getSqlStatementContext(), sqlRewriteContext.getParameters());
}
}
//添加SQLTokenGenerators
sqlRewriteContext.addSQLTokenGenerators(new ShardingTokenGenerateBuilder(shardingRule, sqlRouteResult).getSQLTokenGenerators());
}
}
这段代码不长,但内容却不少。今天我们先关注于第一部分内容,即参数改写。这里又引入了几个新类。首当其冲的是ParameterRewriter以及构建它的ParameterRewriterBuilder。我们先来看ParameterRewriter的定义,如下所示:
public interface ParameterRewriter {
//判断是否需要改写
boolean isNeedRewrite(SQLStatementContext sqlStatementContext);
//执行参数改写
void rewrite(ParameterBuilder parameterBuilder, SQLStatementContext sqlStatementContext, List<Object> parameters);
}
ParameterRewriter的类层结构如下所示,跟SQLRewriteContextDecorator一样,关于改写这块的内容实际上主要就是分成两大块,一块是用于分片,一块是用于数据脱敏:
这里以ShardingGeneratedKeyInsertValueParameterRewriter为例看一下ParameterRewriter的实现方式,它的isNeedRewrite方法如下所示:
@Override
public boolean isNeedRewrite(final SQLStatementContext sqlStatementContext) {
return sqlStatementContext instanceof InsertSQLStatementContext && sqlRouteResult.getGeneratedKey().isPresent() && sqlRouteResult.getGeneratedKey().get().isGenerated();
}
显然,我们应该在输入的SQL是一种InsertSQLStatement、并且在路由结果已经包含了GeneratedKey的情况下才执行这种改写。
在介绍ShardingGeneratedKeyInsertValueParameterRewriter的rewrite之前,我们先来一下ParameterBuilder的概念。ParameterBuilder是一种参数构建器,定义如下:
public interface ParameterBuilder {
List<Object> getParameters();
}
ParameterBuilder有两个实现类,分别是StandardParameterBuilder和GroupedParameterBuilder,类层结构如下所示
其中,GroupedParameterBuilder保存着StandardParameterBuilder的一个集合,只适用于InsertSQLStatement,这点在SQLRewriteContext的构造函数中可以得到体现:
parameterBuilder = sqlStatementContext instanceof InsertSQLStatementContext
? new GroupedParameterBuilder(((InsertSQLStatementContext) sqlStatementContext).getGroupedParameters()) : new StandardParameterBuilder(parameters);
了解了这层关系之后,我们再来看ShardingGeneratedKeyInsertValueParameterRewriter的rewrite方法,如下所示:
@Override
public void rewrite(final ParameterBuilder parameterBuilder, final SQLStatementContext sqlStatementContext, final List<Object> parameters) {
Preconditions.checkState(sqlRouteResult.getGeneratedKey().isPresent());
((GroupedParameterBuilder) parameterBuilder).setDerivedColumnName(sqlRouteResult.getGeneratedKey().get().getColumnName());
Iterator<Comparable<?>> generatedValues = sqlRouteResult.getGeneratedKey().get().getGeneratedValues().descendingIterator();
int count = 0;
int parametersCount = 0;
for (List<Object> each : ((InsertSQLStatementContext) sqlStatementContext).getGroupedParameters()) {
parametersCount += ((InsertSQLStatementContext) sqlStatementContext).getInsertValueContexts().get(count).getParametersCount();
Comparable<?> generatedValue = generatedValues.next();
if (!each.isEmpty()) {
((GroupedParameterBuilder) parameterBuilder).getParameterBuilders().get(count).addAddedParameters(parametersCount, Lists.<Object>newArrayList(generatedValue));
}
count++;
}
}
因为ShardingGeneratedKeyInsertValueParameterRewriter面向InsertSQLStatement,所以这里用到了GroupedParameterBuilder,并通过SQLRouteResult获取GeneratedKey。我们设置了GroupedParameterBuilder中的DerivedColumnName为GeneratedKey的主键Column,并通过一个循环添加了对应的Index和Parameter,也就是完成所需的补列操作。这部分的操作实际上可以与GeneratedKey的生成过程结合起来一起看以便加深理解,如下所示的createGeneratedKey方法也是通过一个循环对GeneratedKey进行赋值。
private static GeneratedKey createGeneratedKey(final ShardingRule shardingRule, final InsertStatement insertStatement, final String generateKeyColumnName) {
GeneratedKey result = new GeneratedKey(generateKeyColumnName, true);
for (int i = 0; i < insertStatement.getValueListCount(); i++) {
result.getGeneratedValues().add(shardingRule.generateKey(insertStatement.getTable().getTableName()));
}
return result;
}
了解了ParameterRewriter之后,我们再来看用于构建它的ParameterRewriterBuilder,如下所示:
public interface ParameterRewriterBuilder {
Collection<ParameterRewriter> getParameterRewriters(RelationMetas relationMetas);
}
ParameterRewriterBuilder同样有两个实现类,类层结构如下所示:
我们同样来看ShardingParameterRewriterBuilder类,它的getParameterRewriters方法如下所示:
@Override
public Collection<ParameterRewriter> getParameterRewriters(final RelationMetas relationMetas) {
Collection<ParameterRewriter> result = getParameterRewriters();
for (ParameterRewriter each : result) {
setUpParameterRewriters(each, relationMetas);
}
return result;
}
private static Collection<ParameterRewriter> getParameterRewriters() {
Collection<ParameterRewriter> result = new LinkedList<>();
result.add(new ShardingGeneratedKeyInsertValueParameterRewriter());
result.add(new ShardingPaginationParameterRewriter());
return result;
}
可以看到ShardingParameterRewriterBuilder内置了用于自动生成分布式主键的ShardingGeneratedKeyInsertValueParameterRewriter和用于进行分页修正(参考官网https://shardingsphere.apache.org/document/current/cn/features/sharding/principle/rewrite/中的描述)的ShardingPaginationParameterRewriter这两个ParameterRewriter,然后通过如下所示的setUpParameterRewriters方法对其进行设置:
private void setUpParameterRewriters(final ParameterRewriter parameterRewriter, final RelationMetas relationMetas) {
if (parameterRewriter instanceof RelationMetasAware) {
((RelationMetasAware) parameterRewriter).setRelationMetas(relationMetas);
}
if (parameterRewriter instanceof ShardingRuleAware) {
((ShardingRuleAware) parameterRewriter).setShardingRule(shardingRule);
}
if (parameterRewriter instanceof SQLRouteResultAware) {
((SQLRouteResultAware) parameterRewriter).setSqlRouteResult(sqlRouteResult);
}
}
这个方法比较有意思,我们看到了Aware系列的接口。Aware机制在Spring等框架中也得到了大量的应用,比如经典的ApplicationContextAware接口。在Spring中,当一个类实现了ApplicationContextAware之后,就可以方便获得当前的(指的是所运行的代码和已启动的Spring代码处于同一个Spring上下文)ApplicationContext,进而获取ApplicationContext中的所有Bean。ShardingSphere使用Aware的方式也是类似,通过Aware接口就可以传递ShardingRule和SQLRouteResult等对象。以SQLRouteResultAware为例,它的定义如下所示:
public interface SQLRouteResultAware {
void setSqlRouteResult(SQLRouteResult sqlRouteResult);
}
我们在前面的ShardingGeneratedKeyInsertValueParameterRewriter类定义中也发现了它实现了SQLRouteResultAware这一Aware接口,如下所示:
public final class ShardingGeneratedKeyInsertValueParameterRewriter implements ParameterRewriter, SQLRouteResultAware {
…
private SQLRouteResult sqlRouteResult;
}
因为ShardingSphere使用了lombok,所以这里没有显式提供setSqlRouteResult方法,而该方法的调用就在ShardingParameterRewriterBuilder的setUpParameterRewriters方法中进行,从而完成SQLRouteResult的注入。
这一块代码涉及到的类有点多,值得我们画一张类图来总结一下,如下所示:
前面的内容我们关注于ShardingSQLRewriteContextDecorator中使用ParameterRewriter进行参数改写的过程,这是ShardingSQLRewriteContextDecorator的decorate方法中的第一部分内容。接下来我们继续讲解该方法的第二部分内容,即为SQLRewriteContext添加SQLTokenGenerator,如下所示:
sqlRewriteContext.addSQLTokenGenerators(new ShardingTokenGenerateBuilder(shardingRule, sqlRouteResult).getSQLTokenGenerators());
这句代码关注于SQLTokenGenerator的创建,所以出现了一个ShardingTokenGenerateBuilder。关于SQLTokenGenerator,我们已经在《ShardingSphere源码解析之改写引擎(一)》中有过简单的讨论。SQLTokenGeneratorBuilder接口定义如下:
public interface SQLTokenGeneratorBuilder {
Collection<SQLTokenGenerator> getSQLTokenGenerators();
}
SQLTokenGeneratorBuilder的实现类有三个,除了可以想到的ShardingTokenGenerateBuilder和EncryptTokenGenerateBuilder之外,还多了一个DefaultTokenGeneratorBuilder(从命名上讲,这里的EncryptTokenGenerateBuilder和ShardingTokenGenerateBuilder应该分别是EncryptTokenGeneratorBuilder和ShardingTokenGeneratorBuilder更加合理一点),类层结构如下所示:
我们同样还是先关注于ShardingTokenGenerateBuilder,可以看到该SQLTokenGeneratorBuilder内置了很多TokenGenerator,包含我们在《ShardingSphere源码解析之改写引擎(一)》中提到过的GeneratedKeyInsertColumnTokenGenerator,如下所示:
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;
}
然后在ShardingTokenGenerateBuilder的getSQLTokenGenerators()方法中,和ShardingParameterRewriterBuilder一样,同样对这些SQLTokenGenerator都进行了Aware机制的处理,如下所示:
@Override
public Collection<SQLTokenGenerator> getSQLTokenGenerators() {
Collection<SQLTokenGenerator> result = buildSQLTokenGenerators();
for (SQLTokenGenerator each : result) {
if (each instanceof ShardingRuleAware) {
((ShardingRuleAware) each).setShardingRule(shardingRule);
}
if (each instanceof SQLRouteResultAware) {
((SQLRouteResultAware) each).setSqlRouteResult(sqlRouteResult);
}
}
return result;
}
至此,我们对ShardingSQLRewriteContextDecorator的讨论到此结束。从下一篇开始,我们将开始介绍SQLRewriteEngine。
更多内容可以关注我的公众号:程序员向架构师转型。