您好,登錄后才能下訂單哦!
這篇文章主要介紹“sharding-jdbc中SQL改寫用法”,在日常操作中,相信很多人在sharding-jdbc中SQL改寫用法問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”sharding-jdbc中SQL改寫用法”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
本文主要以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則不需要改寫sql result.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_order for (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_0 result.put(logicTableName, each.getActualTableName()); //根據綁定表添加剩余的解析表 //比如t_order、t_order_item是綁定表,解析表為t_order、t_order_item,則添加t_order_item:t_order_item_0 result.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_item Optional<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_item for (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; //優化后的Statement private final OptimizedStatement optimizedStatement; //token private 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來生成真實sql sqlTokens = 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、邏輯表對應的真實表來生成sql return new SQLUnit(sqlBuilder.toSQL(routingUnit, logicAndActualTables), parameterBuilder.getParameters(routingUnit)); } }
3.構建SQL,SQLBuilder#toSQL
@RequiredArgsConstructor public final class SQLBuilder { //邏輯sql private final String logicSQL; //sql token private 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 //遍歷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<String, String> 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(); } }
到此,關于“sharding-jdbc中SQL改寫用法”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。