上一篇文章咱們分析了sharding-jdbc 的路由(路由),今天咱們分析下sql改寫。sql
閒聊:翻了Sharding-Sphere 的文檔,也對SQL【解析、路由、改寫、執行、歸併】引擎作了介紹,你們有興趣能夠看看。編程
咱們繼續如下面的SQL爲例:bash
SELECT o.order_id FROM order o WHERE o.order_id = 4app
public SQLRouteResult route(final String logicSQL, final List<Object> parameters, final SQLStatement sqlStatement) {
final Context context = MetricsContext.start("Route SQL");
SQLRouteResult result = new SQLRouteResult(sqlStatement);
if (sqlStatement instanceof InsertStatement && null != ((InsertStatement) sqlStatement).getGeneratedKey()) {
processGeneratedKey(parameters, (InsertStatement) sqlStatement, result);
}
//路由
RoutingResult routingResult = route(parameters, sqlStatement);
//重寫
SQLRewriteEngine rewriteEngine = new SQLRewriteEngine(shardingRule, logicSQL, sqlStatement);
//判斷是不是單表路由
boolean isSingleRouting = routingResult.isSingleRouting();
//處理limit
if (null != sqlStatement.getLimit()) {
sqlStatement.getLimit().processParameters(parameters, !isSingleRouting);
}
//構建 SQLBuilder
SQLBuilder sqlBuilder = rewriteEngine.rewrite(!isSingleRouting);
if (routingResult instanceof CartesianRoutingResult) {
for (CartesianDataSource cartesianDataSource : ((CartesianRoutingResult) routingResult).getRoutingDataSources()) {
for (CartesianTableReference cartesianTableReference : cartesianDataSource.getRoutingTableReferences()) {
result.getExecutionUnits().add(new SQLExecutionUnit(cartesianDataSource.getDataSource(), rewriteEngine.generateSQL(cartesianTableReference, sqlBuilder)));
}
}
} else {
//建立sql最小執行單元
for (TableUnit each : routingResult.getTableUnits().getTableUnits()) {
result.getExecutionUnits().add(new SQLExecutionUnit(each.getDataSourceName(), rewriteEngine.generateSQL(each, sqlBuilder)));
}
}
MetricsContext.stop(context);
logSQLRouteResult(result, parameters);
return result;
}
複製代碼
public SQLRewriteEngine(final ShardingRule shardingRule, final String originalSQL, final SQLStatement sqlStatement) {
this.shardingRule = shardingRule;
this.originalSQL = originalSQL;
sqlTokens.addAll(sqlStatement.getSqlTokens());
tableNames = sqlStatement.getTables().getTableNames();
limit = sqlStatement.getLimit();
}
複製代碼
處理邏輯:把SQL分爲2部分:須要替換的(例如:表名) 和不須要替換的;須要替換的表名替換成真實的表名(利用解析好的TableToken),分別add 到SQLBuilder的segments集合中。這樣生成真實SQL直接foreach segments就能夠了post
/**
* SQL改寫.
*
* @param isRewriteLimit 是否重寫Limit
* @return SQL構建器
*/
public SQLBuilder rewrite(final boolean isRewriteLimit) {
SQLBuilder result = new SQLBuilder();
//若是沒有sqlTokens,說明沒有須要替換的標記
if (sqlTokens.isEmpty()) {
//直接add
result.appendLiterals(originalSQL);
return result;
}
int count = 0;
sortByBeginPosition();
//遍歷SQL標記
for (SQLToken each : sqlTokens) {
if (0 == count) {
//標記的位置是須要替換的開始index,這裏直接add不須要替換的部分
result.appendLiterals(originalSQL.substring(0, each.getBeginPosition()));
}
//須要替換table
if (each instanceof TableToken) {
appendTableToken(result, (TableToken) each, count, sqlTokens);
} else if (each instanceof ItemsToken) {
appendItemsToken(result, (ItemsToken) each, count, sqlTokens);
} else if (each instanceof RowCountLimitToken) {
appendLimitRowCount(result, (RowCountLimitToken) each, count, sqlTokens, isRewriteLimit);
} else if (each instanceof OffsetLimitToken) {
appendLimitOffsetToken(result, (OffsetLimitToken) each, count, sqlTokens, isRewriteLimit);
}
count++;
}
return result;
}
//appendTableToken:
private void appendTableToken(final SQLBuilder sqlBuilder, final TableToken tableToken, final int count, final List<SQLToken> sqlTokens) {
//獲取真實tableName
String tableName = tableNames.contains(tableToken.getTableName()) ? tableToken.getTableName() : tableToken.getOriginalLiterals();
// addTableToken
sqlBuilder.appendTable(tableName);
int beginPosition = tableToken.getBeginPosition() + tableToken.getOriginalLiterals().length();
int endPosition = sqlTokens.size() - 1 == count ? originalSQL.length() : sqlTokens.get(count + 1).getBeginPosition();
//截取2個token之間,不須要替換的部分
sqlBuilder.appendLiterals(originalSQL.substring(beginPosition, endPosition));
}
複製代碼
for (TableUnit each : routingResult.getTableUnits().getTableUnits()) {
result.getExecutionUnits().add(new SQLExecutionUnit(each.getDataSourceName(), rewriteEngine.generateSQL(each, sqlBuilder)));
}
複製代碼
/**
* 生成SQL語句.
*
* @param tableUnit 路由表單元
* @param sqlBuilder SQL構建器
* @return SQL語句
*/
public String generateSQL(final TableUnit tableUnit, final SQLBuilder sqlBuilder) {
return sqlBuilder.toSQL(getTableTokens(tableUnit));
}
//將邏輯表和真實表以map的形式關聯
private Map<String, String> getTableTokens(final TableUnit tableUnit) {
Map<String, String> tableTokens = new HashMap<>();
tableTokens.put(tableUnit.getLogicTableName(), tableUnit.getActualTableName());
Optional<BindingTableRule> bindingTableRule = shardingRule.findBindingTableRule(tableUnit.getLogicTableName());
if (bindingTableRule.isPresent()) {
tableTokens.putAll(getBindingTableTokens(tableUnit, bindingTableRule.get()));
}
return tableTokens;
}
/**
* 生成SQL語句.
* foreach 每一個segments,生成真正可執行的SQL
* @param tableTokens 佔位符集合
* @return SQL語句
*/
public String toSQL(final Map<String, String> tableTokens) {
StringBuilder result = new StringBuilder();
for (Object each : segments) {
if (each instanceof TableToken && tableTokens.containsKey(((TableToken) each).tableName)) {
result.append(tableTokens.get(((TableToken) each).tableName));
} else {
result.append(each);
}
}
return result.toString();
}
複製代碼
小尾巴走一波,歡迎關注個人公衆號,不按期分享編程、投資、生活方面的感悟:)ui