ShardingSphere源码解析之改写引擎(二)

今天我们将继续围绕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。

更多内容可以关注我的公众号:程序员向架构师转型。

发布了112 篇原创文章 · 获赞 12 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/lantian08251/article/details/104727482