【引用官网】在包含分表的场景中,需要将分表配置中的逻辑表名称改写为路由之后所获取的真实表名称。仅分库则不需要表名称的改写。除此之外,还包括补列和分页信息修正等内容,如图:

本文主要以SELECT i.* FROM t_order_1 o, t_order_item_1 i WHERE o.order_id = i.order_id and o.order_id = ? and o.user_id = ?一个简单查询语句,来分析ss大致如何来改写sql的,不同类型sql改写需自行查看对应的sql token生成器

  • 比如分页查看OffsetTokenGenerator

1.BaseShardingEngine#shard执行改写,主要查看rewriteAndConvert方法

@RequiredArgsConstructor
public abstract class BaseShardingEngine {//分库分表规则private final ShardingRule shardingRule;//分片参数private final ShardingProperties shardingProperties;//分片元数据private final ShardingMetaData metaData;//路由钩子private final SPIRoutingHook routingHook = new SPIRoutingHook();/*** Shard.** @param sql SQL* @param parameters parameters of SQL* @return SQL route result*/public SQLRouteResult shard(final String sql, final List<Object> parameters) {List<Object> clonedParameters = cloneParameters(parameters);SQLRouteResult result = executeRoute(sql, clonedParameters);//sql改写,如何是Hint则不需要改写sqlresult.getRouteUnits().addAll(HintManager.isDatabaseShardingOnly() ? convert(sql, clonedParameters, result) : rewriteAndConvert(clonedParameters, result));if (shardingProperties.getValue(ShardingPropertiesConstant.SQL_SHOW)) {boolean showSimple = shardingProperties.getValue(ShardingPropertiesConstant.SQL_SIMPLE);SQLLogger.logSQL(sql, showSimple, result.getOptimizedStatement().getSQLStatement(), result.getRouteUnits());}return result;}... ...private Collection<RouteUnit> convert(final String sql, final List<Object> parameters, final SQLRouteResult sqlRouteResult) {Collection<RouteUnit> result = new LinkedHashSet<>();for (RoutingUnit each : sqlRouteResult.getRoutingResult().getRoutingUnits()) {result.add(new RouteUnit(each.getDataSourceName(), new SQLUnit(sql, parameters)));}return result;}private Collection<RouteUnit> rewriteAndConvert(final List<Object> parameters, final SQLRouteResult sqlRouteResult) {//改写引擎SQLRewriteEngine rewriteEngine = new SQLRewriteEngine(shardingRule, sqlRouteResult, parameters, sqlRouteResult.getRoutingResult().isSingleRouting());Collection<RouteUnit> result = new LinkedHashSet<>();//遍历路由单元,//如t_order、t_order_item是绑定表关系,那么这里路由单元集合只有一个t_orderfor (RoutingUnit each : sqlRouteResult.getRoutingResult().getRoutingUnits()) {//添加sql改写后的路由单元result.add(new RouteUnit(each.getDataSourceName(),//封装改写sql单元rewriteEngine.generateSQL(each, getLogicAndActualTables(each, sqlRouteResult.getOptimizedStatement().getSQLStatement().getTables().getTableNames()))));}return result;}private Map<String, String> getLogicAndActualTables(final RoutingUnit routingUnit, final Collection<String> parsedTableNames) {Map<String, String> result = new HashMap<>();//遍历表单元for (TableUnit each : routingUnit.getTableUnits()) {String logicTableName = each.getLogicTableName().toLowerCase();//添加逻辑表:真实表 t_order:t_order_0result.put(logicTableName, each.getActualTableName());//根据绑定表添加剩余的解析表//比如t_order、t_order_item是绑定表,解析表为t_order、t_order_item,则添加t_order_item:t_order_item_0result.putAll(getLogicAndActualTablesFromBindingTable(routingUnit.getMasterSlaveLogicDataSourceName(), each, parsedTableNames));}//返回逻辑表对应的真实表return result;}private Map<String, String> getLogicAndActualTablesFromBindingTable(final String dataSourceName, final TableUnit tableUnit, final Collection<String> parsedTableNames) {Map<String, String> result = new LinkedHashMap<>();//根据逻辑表获取对应的绑定表 t_order、t_order_itemOptional<BindingTableRule> bindingTableRule = shardingRule.findBindingTableRule(tableUnit.getLogicTableName());if (bindingTableRule.isPresent()) {result.putAll(getLogicAndActualTablesFromBindingTable(dataSourceName, tableUnit, parsedTableNames, bindingTableRule.get()));}return result;}private Map<String, String> getLogicAndActualTablesFromBindingTable(final String dataSourceName, final TableUnit tableUnit, final Collection<String> parsedTableNames, final BindingTableRule bindingTableRule) {Map<String, String> result = new LinkedHashMap<>();//遍历解析后的表 t_order、t_order_itemfor (String each : parsedTableNames) {String tableName = each.toLowerCase();//解析表和逻辑表不想等,且解析表是绑定表if (!tableName.equals(tableUnit.getLogicTableName().toLowerCase()) && bindingTableRule.hasLogicTable(tableName)) {//添加解析表对应的真实表result.put(tableName, bindingTableRule.getBindingActualTable(dataSourceName, tableName, tableUnit.getActualTableName()));}}return result;}
}    

2.改写SQL,SQLRewriteEngine#generateSQL

public final class SQLRewriteEngine {//规则private final BaseRule baseRule;//优化后的Statementprivate final OptimizedStatement optimizedStatement;//tokenprivate final List<SQLToken> sqlTokens;//sql构建者private final SQLBuilder sqlBuilder;//参数构建者private final ParameterBuilder parameterBuilder;public SQLRewriteEngine(final ShardingRule shardingRule, final SQLRouteResult sqlRouteResult, final List<Object> parameters, final boolean isSingleRoute) {baseRule = shardingRule;this.optimizedStatement = getEncryptedOptimizedStatement(shardingRule.getEncryptRule().getEncryptorEngine(), sqlRouteResult.getOptimizedStatement());//占位符参数值parameterBuilder = createParameterBuilder(parameters, sqlRouteResult);//创建sql token,主要通过token来生成真实sqlsqlTokens = createSQLTokens(isSingleRoute);//sql构建者sqlBuilder = new SQLBuilder(optimizedStatement.getSQLStatement().getLogicSQL(), sqlTokens);}... ...private List<SQLToken> createSQLTokens(final boolean isSingleRoute) {List<SQLToken> result = new LinkedList<>();//改写SQL核心,主要根据解析后的segment生成相应类型的token,如TableTokenGenerator->TableToken//基础token生成引擎result.addAll(new BaseTokenGenerateEngine().generateSQLTokens(optimizedStatement, parameterBuilder, baseRule, isSingleRoute));//分库分表规则if (baseRule instanceof ShardingRule) {ShardingRule shardingRule = (ShardingRule) baseRule;result.addAll(new ShardingTokenGenerateEngine().generateSQLTokens(optimizedStatement, parameterBuilder, shardingRule, isSingleRoute));result.addAll(new EncryptTokenGenerateEngine().generateSQLTokens(optimizedStatement, parameterBuilder, shardingRule.getEncryptRule(), isSingleRoute));} else if (baseRule instanceof EncryptRule) {result.addAll(new EncryptTokenGenerateEngine().generateSQLTokens(optimizedStatement, parameterBuilder, (EncryptRule) baseRule, isSingleRoute));}//排序,这里主要根据解析后的startIndex排序,用来保证sql token的正确性Collections.sort(result);return result;}/*** Generate SQL.* * @return sql unit*/public SQLUnit generateSQL() {return new SQLUnit(sqlBuilder.toSQL(), parameterBuilder.getParameters());}/*** Generate SQL.* * @param routingUnit routing unit* @param logicAndActualTables logic and actual tables* @return sql unit*/public SQLUnit generateSQL(final RoutingUnit routingUnit, final Map<String, String> logicAndActualTables) {//封装sql单元,主要根据token index、逻辑表对应的真实表来生成sqlreturn new SQLUnit(sqlBuilder.toSQL(routingUnit, logicAndActualTables), parameterBuilder.getParameters(routingUnit));}
}

3.构建SQL,SQLBuilder#toSQL

@RequiredArgsConstructor
public final class SQLBuilder {//逻辑sqlprivate final String logicSQL;//sql tokenprivate final List<SQLToken> sqlTokens;/*** Convert to SQL.** @return SQL*/public String toSQL() {return toSQL(null, Collections.<String, String>emptyMap());}/*** Convert to SQL.** @param routingUnit routing unit* @param logicAndActualTables logic and actual map* @return SQL*/public String toSQL(final RoutingUnit routingUnit, final Map<String, String> logicAndActualTables) {if (sqlTokens.isEmpty()) {return logicSQL;}return createLogicSQL(routingUnit, logicAndActualTables);}private String createLogicSQL(final RoutingUnit routingUnit, final Map<String, String> logicAndActualTables) {StringBuilder result = new StringBuilder();//截取逻辑sql,从0截取到第一个token start index//如:SELECT i.* FROM t_order_1 o, t_order_item_1 i WHERE o.order_id = i.order_id and o.order_id = ? and o.user_id = ?
​//以上面sql为例,sqlTokens为://[TableToken(startIndex=16,stopIndex=22,tableName=t_order), TableToken(startIndex=27,stopIndex=38,tableName=t_order_item)]
​result.append(logicSQL.substring(0, sqlTokens.get(0).getStartIndex())); //截取结果为select * from//遍历tokenfor (SQLToken each : sqlTokens) {//以改写表为例//此处为根据逻辑表改写为真实表result.append(getSQLTokenLiterals(each, routingUnit, logicAndActualTables)); //结果为t_order_0//此处则是处理别名result.append(getConjunctionLiterals(each));//结果为 o,}return result.toString();}private String getSQLTokenLiterals(final SQLToken sqlToken, final RoutingUnit routingUnit, final Map<String, String> logicAndActualTables) {//判断token是否可变(Alterable),调用对应token的toString方法//如是Alterable,返回逻辑表对应的真实表,即t_order:t_order_0,返回t_order_0return sqlToken instanceof Alterable ? ((Alterable) sqlToken).toString(routingUnit, logicAndActualTables) : sqlToken.toString();}private String getConjunctionLiterals(final SQLToken sqlToken) {//TableToken(startIndex=16,stopIndex=22,tableName=t_order)//TableToken(startIndex=27,stopIndex=38,tableName=t_order_item)
​//找到当前sqlToken的index//第一次遍历currentSQLTokenIndex为0int currentSQLTokenIndex = sqlTokens.indexOf(sqlToken);//计算需要截取的结束位置//第一次遍历stopIndex为27int stopIndex = sqlTokens.size() - 1 == currentSQLTokenIndex ? logicSQL.length() : sqlTokens.get(currentSQLTokenIndex + 1).getStartIndex();//计算需要截取的起始位置//判断当前sqlToken的起始位置是否大于逻辑sql长度,如果起始位置大于逻辑sql的长度时,则为逻辑sql长度,否则获取当前sqlToken的起始位置//第一次遍历 startIndex:23 stopIndex:27,截取结果为 o,return logicSQL.substring(getStartIndex(sqlToken) > logicSQL.length() ? logicSQL.length() : getStartIndex(sqlToken), stopIndex);}private int getStartIndex(final SQLToken sqlToken) {//判断token是否可替代,如别名return sqlToken instanceof Substitutable ? ((Substitutable) sqlToken).getStopIndex() + 1 : sqlToken.getStartIndex();}
}

转载于:https://my.oschina.net/u/3180962/blog/3102419

sharding-jdbc之SQL改写相关推荐

  1. SpringBoot + Sharding JDBC,一文搞定分库分表、读写分离

    程序员的成长之路 互联网/程序员/技术/资料共享 关注 阅读本文大概需要 30 分钟. 来自:blog.csdn.net/qq_40378034/article/details/115264837 S ...

  2. SpringBoot + Sharding JDBC 读写分离、分库分表

    Sharding-JDBC 最早是当当网内部使用的一款分库分表框架,到2017年的时候才开始对外开源,这几年在大量社区贡献者的不断迭代下,功能也逐渐完善,现已更名为 ShardingSphere,20 ...

  3. Sharding JDBC(四) 分片策略一:标准分片策略StandardShardingStrategy

    目录 一.标准分片策略StandardShardingStrategy 二.StandardShardingStrategy配置实现 分库分表最核心的两点SQL 路由  . SQL 改写 applic ...

  4. 理解Sharding jdbc原理,看这一篇就够了

    相比于Spring基于AbstractRoutingDataSource实现的分库分表功能,Sharding jdbc在单库单表扩展到多库多表时,兼容性方面表现的更好一点.例如,spring实现的分库 ...

  5. Sharding JDBC分片和读写分离详解

    目录 Sharding Sphere简介 开始使用Sharding JDBC 数据分片 简单示例 Spring Boot示例 代码分析 属性分析 绑定表和广播表 真实表 绑定表 广播表 Shardin ...

  6. Spring boot项目集成Sharding Jdbc

    环境 jdk:1.8 framework: spring boot, sharding jdbc database: MySQL 搭建步骤 在pom 中加入sharding 依赖 <depend ...

  7. Sharding-JDBC 源码之 SQL 改写

    Sharding-JDBC 系列 第一篇 Sharding-JDBC 源码之启动流程分析 第二篇 Sharding-JDBC 源码之 SQL 解析 第三篇 Sharding-JDBC 源码之 SQL ...

  8. Spring boot + Sharding JDBC 分库分表 及 分布式事务处理

    Sharding JDBC 基础概念 Apache ShardingSphere 是一套开源的分布式数据库解决方案组成的生态圈,它由 JDBC.Proxy 和 Sidecar(规划中)这 3 款既能够 ...

  9. 【MySQL 读写分离】Sharding JDBC + Spring boot 实现数据库读写分离的登录 Demo

    上篇文章我们搭建了 MySQL 数据库主从复制集群 MySQL 搭建主从复制集群~~~ 本篇文章我们利用搭建好的主从复制集群,使用 SpringBoot 结合 Sharding-JDBC 搭建一个小的 ...

  10. SSM项目引入sharding JDBC进行分表

    SSM项目引入sharding JDBC进行分表 注意点: 本次集成sharing-jdbc 4.1.1,由于各个版本差别比较大,配置方式差别也特别大,请根据官方文档进行配置! 官方配置路径:http ...

最新文章

  1. Numpy中的meshgrid()函数
  2. 网站架构探索(2)-CDN基本常识 王泽宾
  3. Pokémon Go数据收集是否带来隐私问题
  4. JSBinding+SharpKit / 菜单介绍
  5. exchange无法收发邮件_Python使用POP3和SMTP协议收发邮件!
  6. java线程数翻倍性能翻倍_术业专攻 | 如何让Java Web性能翻倍?
  7. 如何看懂ORACLE执行计划
  8. android自动触发返回,ionic4处理android返回按钮事件
  9. 马云无偿划转阿里股权?蚂蚁集团回应:假消息
  10. Spring常用注解用法总结
  11. KMP算法------字符串匹配问题
  12. 一文带你了解微信/支付宝支付的相关概念
  13. matlab用于系统框图建模的函数,matlab工具箱与s份imulink.ppt
  14. Github使用教程Git下载文件
  15. 用python写网络爬虫-英文翻译
  16. 用gin+xorm+docker编写Online Judge后端
  17. 2021年茶艺师(中级)考试总结及茶艺师(中级)证考试
  18. 新版二开cp盲盒小纸条月老小程序源码【源码好优多】
  19. 华为笔记本键盘说明图_笔记本电脑的的键盘都表示什么意思
  20. 清华大学宣布成立人工智能研究院 张钹担任院长

热门文章

  1. Spring MVC普通类或工具类中调用service报空空指针的解决办法(调用service报java.lang.NullPointerException)
  2. 不填写内容用哪个斜杠代替_手写斜杠日期怎么写
  3. mysql 系统错误 1058,mysql启动服务报1058错误的解决方法
  4. 期货的暴富逻辑是什么?
  5. 戴尔电脑怎么录屏?这6个方法教你轻松录屏
  6. python从事软件测试_软件测试自学到什么程度可以开始找工作
  7. JQuery 基础知识学习(详尽版)
  8. ssm+java+vue基于微信小程序的游泳馆管理系统#毕业设计
  9. 图解Word2vec
  10. 【C# 】泛型,减少代码的方式