溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

sharding-jdbc中SQL改寫用法

發(fā)布時(shí)間:2021-06-22 15:15:04 來源:億速云 閱讀:767 作者:chen 欄目:大數(shù)據(jù)

這篇文章主要介紹“sharding-jdbc中SQL改寫用法”,在日常操作中,相信很多人在sharding-jdbc中SQL改寫用法問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”sharding-jdbc中SQL改寫用法”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

本文主要以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 = ?一個(gè)簡單查詢語句,來分析ss大致如何來改寫sql的,不同類型sql改寫需自行查看對應(yīng)的sql token生成器

  • 比如分頁查看OffsetTokenGenerator

1.BaseShardingEngine#shard執(zhí)行改寫,主要查看rewriteAndConvert方法

@RequiredArgsConstructor
public abstract class BaseShardingEngine {
    //分庫分表規(guī)則
    private final ShardingRule shardingRule;
    //分片參數(shù)
    private final ShardingProperties shardingProperties;
    //分片元數(shù)據(jù)
    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是綁定表關(guān)系,那么這里路由單元集合只有一個(gè)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();
            //添加邏輯表:真實(shí)表 t_order:t_order_0
            result.put(logicTableName, each.getActualTableName());
            //根據(jù)綁定表添加剩余的解析表
            //比如t_order、t_order_item是綁定表,解析表為t_order、t_order_item,則添加t_order_item:t_order_item_0
            result.putAll(getLogicAndActualTablesFromBindingTable(routingUnit.getMasterSlaveLogicDataSourceName(), each, parsedTableNames));
        }
        //返回邏輯表對應(yīng)的真實(shí)表
        return result;
    }
    
    private Map<String, String> getLogicAndActualTablesFromBindingTable(final String dataSourceName, final TableUnit tableUnit, final Collection<String> parsedTableNames) {
        Map<String, String> result = new LinkedHashMap<>();
        //根據(jù)邏輯表獲取對應(yīng)的綁定表 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)) {
                //添加解析表對應(yīng)的真實(shí)表
                result.put(tableName, bindingTableRule.getBindingActualTable(dataSourceName, tableName, tableUnit.getActualTableName()));
            }
        }
        return result;
    }
}

2.改寫SQL,SQLRewriteEngine#generateSQL

public final class SQLRewriteEngine {
    //規(guī)則
    private final BaseRule baseRule;
    //優(yōu)化后的Statement
    private final OptimizedStatement optimizedStatement;
    //token
    private final List<SQLToken> sqlTokens;
    //sql構(gòu)建者
    private final SQLBuilder sqlBuilder;
    //參數(shù)構(gòu)建者
    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());
        //占位符參數(shù)值
        parameterBuilder = createParameterBuilder(parameters, sqlRouteResult);
        //創(chuàng)建sql token,主要通過token來生成真實(shí)sql
        sqlTokens = createSQLTokens(isSingleRoute);
        //sql構(gòu)建者
        sqlBuilder = new SQLBuilder(optimizedStatement.getSQLStatement().getLogicSQL(), sqlTokens);
    }
    
    ... ...
    
    private List<SQLToken> createSQLTokens(final boolean isSingleRoute) {
        List<SQLToken> result = new LinkedList<>();
        //改寫SQL核心,主要根據(jù)解析后的segment生成相應(yīng)類型的token,如TableTokenGenerator->TableToken
        //基礎(chǔ)token生成引擎
        result.addAll(new BaseTokenGenerateEngine().generateSQLTokens(optimizedStatement, parameterBuilder, baseRule, isSingleRoute));
        //分庫分表規(guī)則
        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));
        }
        //排序,這里主要根據(jù)解析后的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單元,主要根據(jù)token index、邏輯表對應(yīng)的真實(shí)表來生成sql
        return new SQLUnit(sqlBuilder.toSQL(routingUnit, logicAndActualTables), parameterBuilder.getParameters(routingUnit));
    }
}

3.構(gòu)建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截取到第一個(gè)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())); //截取結(jié)果為select * from
        //遍歷token
        for (SQLToken each : sqlTokens) {
            //以改寫表為例
            //此處為根據(jù)邏輯表改寫為真實(shí)表
            result.append(getSQLTokenLiterals(each, routingUnit, logicAndActualTables)); //結(jié)果為t_order_0
            //此處則是處理別名
            result.append(getConjunctionLiterals(each));//結(jié)果為 o,
        }
        return result.toString();
    }
    
    private String getSQLTokenLiterals(final SQLToken sqlToken, final RoutingUnit routingUnit, final Map<String, String> logicAndActualTables) {
        //判斷token是否可變(Alterable),調(diào)用對應(yīng)token的toString方法
        //如是Alterable,返回邏輯表對應(yīng)的真實(shí)表,即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)

        //找到當(dāng)前sqlToken的index
        //第一次遍歷currentSQLTokenIndex為0
        int currentSQLTokenIndex = sqlTokens.indexOf(sqlToken);
        //計(jì)算需要截取的結(jié)束位置
        //第一次遍歷stopIndex為27
        int stopIndex = sqlTokens.size() - 1 == currentSQLTokenIndex ? logicSQL.length() : sqlTokens.get(currentSQLTokenIndex + 1).getStartIndex();
        //計(jì)算需要截取的起始位置
        //判斷當(dāng)前sqlToken的起始位置是否大于邏輯sql長度,如果起始位置大于邏輯sql的長度時(shí),則為邏輯sql長度,否則獲取當(dāng)前sqlToken的起始位置
        //第一次遍歷 startIndex:23 stopIndex:27,截取結(jié)果為 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();
    }
}

到此,關(guān)于“sharding-jdbc中SQL改寫用法”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI