ShardingSphere的SQL重写机制竟然用到了这种设计模式

本篇文章源码基于4.0.1版本

我们之前在讲ShardingSphere的路由流程的时候,提到所有的路由引擎都是BaseShardingEngine抽象类的实现类,分片流程在它的shard()方法中定义

BaseShardingEngine的shard()方法:

public SQLRouteResult shard(final String sql, final List<Object> parameters) {
    List<Object> clonedParameters = cloneParameters(parameters);
    SQLRouteResult result = executeRoute(sql, clonedParameters);
    result.getRouteUnits().addAll(HintManager.isDatabaseShardingOnly() ? convert(sql, clonedParameters, result) : rewriteAndConvert(sql, clonedParameters, result));
    boolean showSQL = shardingProperties.getValue(ShardingPropertiesConstant.SQL_SHOW);
    if (showSQL) {
        boolean showSimple = shardingProperties.getValue(ShardingPropertiesConstant.SQL_SIMPLE);
        SQLLogger.logSQL(sql, showSimple, result.getSqlStatementContext(), result.getRouteUnits());
    }
    return result;
}

在分片的过程中,先对参数进行拷贝准备参数,然后执行路由,接下来就是SQL的转换和重写,今天就分析一下它的重写机制

ShardingSphere的重写机制

ShardingSphere的重写机制对应的方法是BaseShardingEngine的rewriteAndConvert()方法:

private Collection<RouteUnit> rewriteAndConvert(final String sql, final List<Object> parameters, final SQLRouteResult sqlRouteResult) {
    SQLRewriteContext sqlRewriteContext = new SQLRewriteContext(metaData.getRelationMetas(), sqlRouteResult.getSqlStatementContext(), sql, parameters);
    new ShardingSQLRewriteContextDecorator(shardingRule, sqlRouteResult).decorate(sqlRewriteContext);
    boolean isQueryWithCipherColumn = shardingProperties.<Boolean>getValue(ShardingPropertiesConstant.QUERY_WITH_CIPHER_COLUMN);
    new EncryptSQLRewriteContextDecorator(shardingRule.getEncryptRule(), isQueryWithCipherColumn).decorate(sqlRewriteContext);
    sqlRewriteContext.generateSQLTokens();
    Collection<RouteUnit> result = new LinkedHashSet<>();
    for (RoutingUnit each : sqlRouteResult.getRoutingResult().getRoutingUnits()) {
        ShardingSQLRewriteEngine sqlRewriteEngine = new ShardingSQLRewriteEngine(shardingRule, sqlRouteResult.getShardingConditions(), each);
        SQLRewriteResult sqlRewriteResult = sqlRewriteEngine.rewrite(sqlRewriteContext);
        result.add(new RouteUnit(each.getDataSourceName(), new SQLUnit(sqlRewriteResult.getSql(), sqlRewriteResult.getParameters())));
    }
    return result;
}
  1. 构建SQL重写上下文对象
  2. 构建用于分片的 SQL 重写上下文装饰器对象,对SQL重写上下文对象进行装饰
  3. 判断加密数据时,是否使用加密列查询。
  4. 构建用于加密的 SQL 重写上下文装饰器对象,对SQL重写上下文对象进行装饰
  5. 通过SQL重写上下文生成SQL令牌
  6. 遍历路由结果的路由单元,创建用于分片的 SQL 重写引擎ShardingSQLRewriteEngine对象,调用rewrite()方法进行重写,然后保存重写的结果并返回,结果包含目标数据库,目标SQL和相关参数。

构建SQL重写上下文对象

构建SQL重写上下文对象的构造方法中添加了移除令牌的SQL 令牌生成器,用来生成移除SQL的SQL令牌

用于分片的 SQL 重写上下文

这里用到了装饰者模式,它的decorate()方法中第一步会判断是否需要参数重写,如果需要就使用GroupedParameterBuilder 补充列名,设置参数值,像插入SQL的主键的自动生成就是通过这个来添加主键列和主键值,主键值通过上一步的路由调用相关的主键生成策略生成后封装到了SQLRouteResult对象中,第二步会添加令牌生成器,ShardingTokenGenerateBuilder的buildSQLTokenGenerators()方法中构建了很多SQL令牌生成器包括生成的键插入列令牌生成器GeneratedKeyInsertColumnTokenGenerator和表令牌生成器TableTokenGenerator

判断是否需要加密列查询

这一块的判断主要是下一步的用于加密的 SQL 重写上下文装饰器作为参数使用

用于加密的 SQL 重写上下文

这里也是装饰者模式的体现,不再细说了

通过SQL重写上下文生成SQL令牌

生成SQL令牌的过程会由具体的令牌生成器类生成

重写SQL

ShardingSQLRewriteEngine的rewrite()方法中调用ShardingSQLBuilder生成SQL,这一块的逻辑在它的父类AbstractSQLBuilder中定义,是模板方法模式的体现,子类需要完成的是getSQLTokenText()方法,也就是获取Sql令牌的文本内容,细说这里,判断SQL令牌是RoutingUnitAware路由单元感知的实例还是LogicAndActualTablesAware逻辑表与实际表感知实例,RoutingUnitAware是实现类是ShardingInsertValuesToken,LogicAndActualTablesAware的实现类有TableToken和IndexToken,TableToken要做的工作就是根据逻辑名获取真实的表名,转化为小写,构建令牌,逻辑表名和真实表名的对应关系BindingTableRule提供了getBindingActualTable()的方法,IndexToken的逻辑实现很简答,这里就不多说了

总结

这篇文章我们以路由流程为入口,详细分析了执行完路由之后的重写SQL的环节,大致流程是创建SQL重写下文对象,然后利用装饰器模式构建用于分片的SQL重写上下文装饰器和用于加密的SQL重写上下文装饰器装饰SQL重写上下文,然后遍历路由结果的路由单元,生成包含目标数据库,目标SQL和相关参数的结果。

❤️ 感谢大家

如果你觉得这篇内容对你挺有有帮助的话:

  1. 欢迎关注我❤️,点赞👍🏻,评论🤤,转发🙏
  2. 有不当之处欢迎批评指正。