本文主要以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生成器
@RequiredArgsConstructorpublic 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
2.改写SQL,SQLRewriteEngine#generateSQL
public final class SQLRewriteEngine { //规则 private final BaseRule baseRule; //优化后的Statement private final OptimizedStatement optimizedStatement; //token private final List sqlTokens; //sql构建者 private final SQLBuilder sqlBuilder; //参数构建者 private final ParameterBuilder parameterBuilder; public SQLRewriteEngine(final ShardingRule shardingRule, final SQLRouteResult sqlRouteResult, final List parameters, final boolean isSingleRoute) { baseRule = shardingRule; this.optimizedStatement = getEncryptedOptimizedStatement(shardingRule.getEncryptRule().getEncryptorEngine(), sqlRouteResult.getOptimizedStatement()); //占位符参数值 parameterBuilder = createParameterBuilder(parameters, sqlRouteResult); //创建sql token,主要通过token来生成真实sql sqlTokens = createSQLTokens(isSingleRoute); //sql构建者 sqlBuilder = new SQLBuilder(optimizedStatement.getSQLStatement().getLogicSQL(), sqlTokens); } ... ... private List createSQLTokens(final boolean isSingleRoute) { List 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 logicAndActualTables) { //封装sql单元,主要根据token index、逻辑表对应的真实表来生成sql return new SQLUnit(sqlBuilder.toSQL(routingUnit, logicAndActualTables), parameterBuilder.getParameters(routingUnit)); }}
3.构建SQL,SQLBuilder#toSQL
@RequiredArgsConstructorpublic final class SQLBuilder { //逻辑sql private final String logicSQL; //sql token private final List sqlTokens; /** * Convert to SQL. * * @return SQL */ public String toSQL() { return toSQL(null, Collections.emptyMap()); } /** * Convert to SQL. * * @param routingUnit routing unit * @param logicAndActualTables logic and actual map * @return SQL */ public String toSQL(final RoutingUnit routingUnit, final Map logicAndActualTables) { if (sqlTokens.isEmpty()) { return logicSQL; } return createLogicSQL(routingUnit, logicAndActualTables); } private String createLogicSQL(final RoutingUnit routingUnit, final Map 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 //遍历token for (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 logicAndActualTables) { //判断token是否可变(Alterable),调用对应token的toString方法 //如是Alterable,返回逻辑表对应的真实表,即t_order:t_order_0,返回t_order_0 return 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为0 int currentSQLTokenIndex = sqlTokens.indexOf(sqlToken); //计算需要截取的结束位置 //第一次遍历stopIndex为27 int 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(); }}