sharding-jdbc 執(zhí)行流程源碼分析-初始化

和其他應(yīng)對(duì)高并發(fā),數(shù)據(jù)量大的方案比若皱,分庫(kù)分表通常是最實(shí)用,最樸素的一個(gè)方案尘颓,既簡(jiǎn)單又有效走触。但是分庫(kù)分表后怎么可以對(duì)業(yè)務(wù)層代碼影響降到最小,是程序員們需要解決的問(wèn)題疤苹,下邊看一下一個(gè)開(kāi)源組件 sharding-jdbc互广,應(yīng)用程序只需引入jar然后通過(guò)編寫(xiě)分片策略方法、和相關(guān)的配置卧土,即可實(shí)現(xiàn)分庫(kù)分表惫皱。
本篇文章主要解釋以下內(nèi)容,基于 4.0.1 版本

研究源碼前尤莺,首先思考幾個(gè)問(wèn)題

1旅敷、sharding-jdbc 為什么可以和 Mybatis\ibaits\hibernate\jpa\spring data template 結(jié)合使用,換句話說(shuō):為什么不依賴orm
2颤霎、內(nèi)部運(yùn)行流程分別是怎么實(shí)現(xiàn)的
(1)初始化過(guò)程
(2)sql 解析
(3)sql 提取
(4)sql 路由
(5)sql 替換
(6)sql 執(zhí)行

1媳谁、sharding-jdbc 為什么不依賴orm

sharding-jdbc 的執(zhí)行入口是 ShardingDataSource 此類實(shí)現(xiàn)了 javax.sql.DataSource 涂滴,javax 的 x 是extension 的意思,也就是擴(kuò)展包,為了使 java 基礎(chǔ)包更加通用撼玄,在上邊加了一層擴(kuò)展唆缴。javax.sql.DataSource 接口只有一個(gè)方法 getConnection , 幾乎所有的 orm 框架都是用 DataSource 接口獲取數(shù)據(jù)庫(kù)連接的搁料。 所以才使得 sharding-jdbc 不依賴具體的 orm 層框架成為可能。

其中 ShardingDataSource 構(gòu)造函數(shù)就是进苍,初始化的入口加缘。
其中 getConnection 方法,是執(zhí)行階段的入口觉啊。

public class ShardingDataSource extends AbstractDataSourceAdapter {
    private final ShardingRuntimeContext runtimeContext;
    //初始化階段
    public ShardingDataSource(final Map<String, DataSource> dataSourceMap, final ShardingRule shardingRule, final Properties props) throws SQLException {
        //匹配數(shù)據(jù)庫(kù)類型
        super(dataSourceMap);
        checkDataSourceType(dataSourceMap);
        runtimeContext = new ShardingRuntimeContext(dataSourceMap, shardingRule, props, getDatabaseType());
    }
    private void checkDataSourceType(final Map<String, DataSource> dataSourceMap) {
        for (DataSource each : dataSourceMap.values()) {
            Preconditions.checkArgument(!(each instanceof MasterSlaveDataSource), "Initialized data sources can not be master-slave data sources.");
        }
    }
    //執(zhí)行階段
    @Override
    public final ShardingConnection getConnection() {
        return new ShardingConnection(getDataSourceMap(), runtimeContext, TransactionTypeHolder.get());
    }
}

2拣宏、內(nèi)部運(yùn)行流程分別是怎么實(shí)現(xiàn)的

(1)初始化階段

涉及的相關(guān)類 uml 圖


image.png

初始化分為3個(gè)步驟:

  • 根據(jù)配置信息 shardingRuleConfiguration 創(chuàng)建分片規(guī)則 ShardingRule
  • 匹配數(shù)據(jù)庫(kù)類型 databaseType
  • 創(chuàng)建 sql 解析引擎 SQLParseEngine 、sql 執(zhí)行引擎 ShardingExecuteEngine
  • 保存數(shù)據(jù)庫(kù)杠人、數(shù)據(jù)表信息勋乾。

下邊我們一個(gè)一個(gè)分析,以 spring namespace 方法創(chuàng)建為例嗡善。

1辑莫、根據(jù)配置信息 shardingRuleConfiguration 創(chuàng)建分片規(guī)則 ShardingRule
public class SpringShardingDataSource extends ShardingDataSource {
    public SpringShardingDataSource(final Map<String, DataSource> dataSourceMap, final ShardingRuleConfiguration shardingRuleConfiguration, final Properties props) throws SQLException {
        //new ShardingRule 創(chuàng)建分片規(guī)則
        super(dataSourceMap, new ShardingRule(shardingRuleConfiguration, dataSourceMap.keySet()), props);
    }
}

spring namespace 會(huì)根據(jù) xml 的配置創(chuàng)建好用戶配置信息類ShardingRuleConfiguration 包括:

  • TableRuleConfiguration:邏輯表名 + 可路由到的數(shù)據(jù)數(shù)據(jù)節(jié)點(diǎn)名(數(shù)據(jù)源名+表名)支持表達(dá)式
  • bindingTableGroups 綁定表組:例如order + order_item 表
  • broadcastTables 廣播表:例如配置信息需要每個(gè)數(shù)據(jù)庫(kù)都有一張這樣的表,需要修改的時(shí)候罩引,那么需要廣播對(duì)吧!
  • defaultDataSourceName 默認(rèn)數(shù)據(jù)源名稱
  • defaultDatabaseShardingStrategyConfig 默認(rèn)分庫(kù)策略
  • defaultTableShardingStrategyConfig 默認(rèn)分表策略
    其中分片策略配置包括:暗示策略Hint基于ThraedLocal
    表達(dá)式分片策略Inline基于groovy 的 Inline 表達(dá)式
    復(fù)合分片策略 Complex 基于多列 各吨、不分庫(kù)策略None,標(biāo)準(zhǔn)策略Standard基于單列袁铐。

然后根據(jù) shardingRuleConfiguration和數(shù)據(jù)源集合揭蜒,創(chuàng)建分片規(guī)則 ShardingRule

public ShardingRule(final ShardingRuleConfiguration shardingRuleConfig, final Collection<String> dataSourceNames) {
        Preconditions.checkArgument(null != shardingRuleConfig, "ShardingRuleConfig cannot be null.");
        Preconditions.checkArgument(null != dataSourceNames && !dataSourceNames.isEmpty(), "Data sources cannot be empty.");
        this.ruleConfiguration = shardingRuleConfig;
        //根據(jù)配置創(chuàng)建所有數(shù)據(jù)源集合,主從復(fù)制需要特殊處理(只是保留一個(gè)主從公共數(shù)據(jù)源)
        shardingDataSourceNames = new ShardingDataSourceNames(shardingRuleConfig, dataSourceNames);
        //根據(jù)分片配置剔桨,創(chuàng)建分片規(guī)則
        tableRules = createTableRules(shardingRuleConfig);
        broadcastTables = shardingRuleConfig.getBroadcastTables();
        //創(chuàng)建 表分片key與分片規(guī)則完全一致 的規(guī)則
        bindingTableRules = createBindingTableRules(shardingRuleConfig.getBindingTableGroups());
        //創(chuàng)建默認(rèn)的分庫(kù)策略
        defaultDatabaseShardingStrategy = createDefaultShardingStrategy(shardingRuleConfig.getDefaultDatabaseShardingStrategyConfig());
        defaultTableShardingStrategy = createDefaultShardingStrategy(shardingRuleConfig.getDefaultTableShardingStrategyConfig());
        defaultShardingKeyGenerator = createDefaultKeyGenerator(shardingRuleConfig.getDefaultKeyGeneratorConfig());
        //主從規(guī)則
        masterSlaveRules = createMasterSlaveRules(shardingRuleConfig.getMasterSlaveRuleConfigs());
        encryptRule = createEncryptRule(shardingRuleConfig.getEncryptRuleConfig());
    }

其中最重要的方法 createTableRules 根據(jù)分片配置以邏輯表為單位創(chuàng)建數(shù)據(jù)節(jié)點(diǎn)屉更,通常一個(gè)TableRule(一個(gè)邏輯表規(guī)則) 對(duì)應(yīng)多個(gè)分表,分庫(kù)洒缀。

   //以每個(gè)表為單位創(chuàng)建分片規(guī)則
    private Collection<TableRule> createTableRules(final ShardingRuleConfiguration shardingRuleConfig) {
        Collection<TableRuleConfiguration> tableRuleConfigurations = shardingRuleConfig.getTableRuleConfigs();
        Collection<TableRule> result = new ArrayList<>(tableRuleConfigurations.size());
        for (TableRuleConfiguration each : tableRuleConfigurations) {
            //創(chuàng)建分片規(guī)則
            result.add(new TableRule(each, shardingDataSourceNames, getDefaultGenerateKeyColumn(shardingRuleConfig)));
        }
        return result;
    }

然后創(chuàng)建初始化 TableRule

1瑰谜、將邏輯表名轉(zhuǎn)成小寫(xiě)
2、獲取用戶配置的真實(shí)表名树绩,因?yàn)榭梢耘渲帽磉_(dá)式所以這里需要計(jì)算一下萨脑。
3、將真實(shí) datasource + 表名 (逗號(hào)分隔) 封裝成 DataNode
如果用戶沒(méi)有配置真實(shí)表葱峡,那么默認(rèn)使用邏輯表名充當(dāng)真實(shí)表名
4砚哗、根據(jù)分庫(kù)策略配置項(xiàng),創(chuàng)建分庫(kù)策略砰奕。根據(jù)分表策略配置項(xiàng)蛛芥,創(chuàng)建分表策略
5提鸟、非默認(rèn)策略必須配置一個(gè)想要路由到的真實(shí)表

    //tableRuleConfig 為分片規(guī)則配置項(xiàng)
    public TableRule(final TableRuleConfiguration tableRuleConfig, final ShardingDataSourceNames shardingDataSourceNames, final String defaultGenerateKeyColumn) {
        //將邏輯表名轉(zhuǎn)成小寫(xiě)
        logicTable = tableRuleConfig.getLogicTable().toLowerCase();
        //將真實(shí)表名表達(dá)式,轉(zhuǎn)換為多個(gè) datasource +表名
        List<String> dataNodes = new InlineExpressionParser(tableRuleConfig.getActualDataNodes()).splitAndEvaluate();
        //按照順序存儲(chǔ) DataNode (datasource +表名)
        dataNodeIndexMap = new HashMap<>(dataNodes.size(), 1);
        //將真實(shí) datasource + 表名 封裝成 DataNode
        actualDataNodes = isEmptyDataNodes(dataNodes)
            ? generateDataNodes(tableRuleConfig.getLogicTable(), shardingDataSourceNames.getDataSourceNames()) : generateDataNodes(dataNodes, shardingDataSourceNames.getDataSourceNames());
        //單純存放真實(shí)表名仅淑,去重
        actualTables = getActualTables();
        //根據(jù)分庫(kù)策略配置項(xiàng)称勋,創(chuàng)建分庫(kù)策略
        databaseShardingStrategy = null == tableRuleConfig.getDatabaseShardingStrategyConfig() ? null : ShardingStrategyFactory.newInstance(tableRuleConfig.getDatabaseShardingStrategyConfig());
        //根據(jù)分表策略配置項(xiàng),創(chuàng)建分表策略
        tableShardingStrategy = null == tableRuleConfig.getTableShardingStrategyConfig() ? null : ShardingStrategyFactory.newInstance(tableRuleConfig.getTableShardingStrategyConfig());
        generateKeyColumn = getGenerateKeyColumn(tableRuleConfig.getKeyGeneratorConfig(), defaultGenerateKeyColumn);
        shardingKeyGenerator = containsKeyGeneratorConfiguration(tableRuleConfig)
                ? new ShardingKeyGeneratorServiceLoader().newService(tableRuleConfig.getKeyGeneratorConfig().getType(), tableRuleConfig.getKeyGeneratorConfig().getProperties()) : null;
        //非默認(rèn)策略必須配置一個(gè)想要路由到的真實(shí)表
        checkRule(dataNodes);
    }

將真實(shí) datasource + 表名 (逗號(hào)分隔) 封裝成 DataNode

    private List<DataNode> generateDataNodes(final String logicTable, final Collection<String> dataSourceNames) {
        List<DataNode> result = new LinkedList<>();
        int index = 0;
        //為每個(gè)數(shù)據(jù)源創(chuàng)建一套
        for (String each : dataSourceNames) {
            DataNode dataNode = new DataNode(each, logicTable);
            result.add(dataNode);
            //保存數(shù)據(jù)節(jié)點(diǎn)的順序
            dataNodeIndexMap.put(dataNode, index);
            actualDatasourceNames.add(each);
            //按照數(shù)據(jù)源維度涯竟,存放真實(shí)數(shù)據(jù)表
            addActualTable(dataNode.getDataSourceName(), dataNode.getTableName());
            index++;
        }
        return result;
    }
    public DataNode(final String dataNode) {
        if (!isValidDataNode(dataNode)) {
            throw new ShardingConfigurationException("Invalid format for actual data nodes: '%s'", dataNode);
        }
        //根據(jù).分隔 前半部分為數(shù)據(jù)源赡鲜,后邊為真實(shí)表
        List<String> segments = Splitter.on(DELIMITER).splitToList(dataNode);
        dataSourceName = segments.get(0);
        tableName = segments.get(1);
    }

根據(jù)分庫(kù)策略配置項(xiàng),創(chuàng)建分庫(kù)策略庐船。根據(jù)分表策略配置項(xiàng)银酬,創(chuàng)建分表策略,這里分表策略和分庫(kù)策略的創(chuàng)建邏輯都是一樣的筐钟。包括默認(rèn)的分庫(kù)分表策略揩瞪。

1、標(biāo)準(zhǔn)分片策略
2篓冲、Inline表達(dá)式分片策略
3李破、復(fù)合分片策略
4、threadLocal 分片策略

  public static ShardingStrategy newInstance(final ShardingStrategyConfiguration shardingStrategyConfig) {
        //標(biāo)準(zhǔn)分片策略
        if (shardingStrategyConfig instanceof StandardShardingStrategyConfiguration) {
            return new StandardShardingStrategy((StandardShardingStrategyConfiguration) shardingStrategyConfig);
        }
        //Inline表達(dá)式分片策略
        if (shardingStrategyConfig instanceof InlineShardingStrategyConfiguration) {
            return new InlineShardingStrategy((InlineShardingStrategyConfiguration) shardingStrategyConfig);
        }
        //復(fù)合分片策略
        if (shardingStrategyConfig instanceof ComplexShardingStrategyConfiguration) {
            return new ComplexShardingStrategy((ComplexShardingStrategyConfiguration) shardingStrategyConfig);
        }
        //threadLocal 分片策略
        if (shardingStrategyConfig instanceof HintShardingStrategyConfiguration) {
            return new HintShardingStrategy((HintShardingStrategyConfiguration) shardingStrategyConfig);
        }
        return new NoneShardingStrategy();
    }

  //標(biāo)準(zhǔn)分片策略
  public StandardShardingStrategy(final StandardShardingStrategyConfiguration standardShardingStrategyConfig) {
        Preconditions.checkNotNull(standardShardingStrategyConfig.getShardingColumn(), "Sharding column cannot be null.");
        Preconditions.checkNotNull(standardShardingStrategyConfig.getPreciseShardingAlgorithm(), "precise sharding algorithm cannot be null.");
        //用于分片的列
        shardingColumn = standardShardingStrategyConfig.getShardingColumn();
        //是必選的 用于處理=和IN的分片
        preciseShardingAlgorithm = standardShardingStrategyConfig.getPreciseShardingAlgorithm();
        //是可選的 用于處理BETWEEN AND分片壹将,如果不配置 RangeShardingAlgorithm嗤攻,SQL中的 BETWEEN AND 將按照全庫(kù)路由處理
        rangeShardingAlgorithm = standardShardingStrategyConfig.getRangeShardingAlgorithm();
    }
    /**
     * Inline表達(dá)式分片策略。使用Groovy的Inline表達(dá)式诽俯,提供對(duì)SQL語(yǔ)句中的=和IN的分片操作支持妇菱。
     * InlineShardingStrategy只支持單分片鍵,對(duì)于簡(jiǎn)單的分片算法暴区,可以通過(guò)簡(jiǎn)單的配置使用恶耽,從而避免繁瑣的Java代碼開(kāi)發(fā),
     * 如: tuser${user_id % 8} 表示t_user表按照user_id按8取模分成8個(gè)表颜启,表名稱為t_user_0 到 t_user_7。
     */
    public InlineShardingStrategy(final InlineShardingStrategyConfiguration inlineShardingStrategyConfig) {
        Preconditions.checkNotNull(inlineShardingStrategyConfig.getShardingColumn(), "Sharding column cannot be null.");
        Preconditions.checkNotNull(inlineShardingStrategyConfig.getAlgorithmExpression(), "Sharding algorithm expression cannot be null.");
        shardingColumn = inlineShardingStrategyConfig.getShardingColumn();
        String algorithmExpression = InlineExpressionParser.handlePlaceHolder(inlineShardingStrategyConfig.getAlgorithmExpression().trim());
        closure = new InlineExpressionParser(algorithmExpression).evaluateClosure();
    }
    //復(fù)合分片策略浪讳。提供對(duì)SQL語(yǔ)句中的 =, IN 和 BETWEEN AND 的分片操作支持缰盏。
    public ComplexShardingStrategy(final ComplexShardingStrategyConfiguration complexShardingStrategyConfig) {
        Preconditions.checkNotNull(complexShardingStrategyConfig.getShardingColumns(), "Sharding columns cannot be null.");
        Preconditions.checkNotNull(complexShardingStrategyConfig.getShardingAlgorithm(), "Sharding algorithm cannot be null.");
        shardingColumns = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        shardingColumns.addAll(Splitter.on(",").trimResults().splitToList(complexShardingStrategyConfig.getShardingColumns()));
        shardingAlgorithm = complexShardingStrategyConfig.getShardingAlgorithm();
    }
    //通過(guò)Hint而非SQL解析的方式分片的策略
    public HintShardingStrategy(final HintShardingStrategyConfiguration hintShardingStrategyConfig) {
        Preconditions.checkNotNull(hintShardingStrategyConfig.getShardingAlgorithm(), "Sharding algorithm cannot be null.");
        shardingColumns = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        shardingAlgorithm = hintShardingStrategyConfig.getShardingAlgorithm();
    }

這里可能有個(gè)誤區(qū),分片策略不是自定義的嗎淹遵?怎么會(huì)出現(xiàn)在源碼中口猜。sharding jdbc 分片策略不能自定義,分片算法是自定義的透揣,上邊代碼中济炎,getShardingAlgorithm() 就是在獲取用戶自定義的算法。

2辐真、匹配數(shù)據(jù)庫(kù)類型 databaseType

在初始化 ShardingDataSource 時(shí)候會(huì)調(diào)用父類AbstractDataSourceAdapter 構(gòu)造方法须尚,根據(jù)數(shù)據(jù)庫(kù)連接的url進(jìn)行匹配數(shù)據(jù)庫(kù)類型

   public AbstractDataSourceAdapter(final Map<String, DataSource> dataSourceMap) throws SQLException {
        this.dataSourceMap = dataSourceMap;
        databaseType = createDatabaseType();
    }
    //根據(jù)數(shù)據(jù)數(shù)據(jù)庫(kù)連接 url 匹配找到數(shù)據(jù)庫(kù)類型
    private DatabaseType createDatabaseType(final DataSource dataSource) throws SQLException {
        if (dataSource instanceof AbstractDataSourceAdapter) {
            return ((AbstractDataSourceAdapter) dataSource).databaseType;
        }
        try (Connection connection = dataSource.getConnection()) {
            return DatabaseTypes.getDatabaseTypeByURL(connection.getMetaData().getURL());
        }
    }
    private static boolean matchStandardURL(final String url, final DatabaseType databaseType) {
        return url.startsWith(String.format("jdbc:%s:", databaseType.getName().toLowerCase()));
    }
3崖堤、創(chuàng)建 sql 解析引擎 SQLParseEngine 、sql 執(zhí)行引擎 ShardingExecuteEngine

1耐床、創(chuàng)建 ShardingRuntimeContext 調(diào)用父類 AbstractRuntimeContext 的構(gòu)造方法
2密幔、創(chuàng)建運(yùn)行時(shí)候需要的一些配置信息,包括是否打印sql,單次執(zhí)行消耗的數(shù)據(jù)庫(kù)連接數(shù)撩轰、并行執(zhí)行線程池大小等等
3胯甩、創(chuàng)建 sql 執(zhí)行引擎
4、創(chuàng)建 sql 解析引擎

    protected AbstractRuntimeContext(final T rule, final Properties props, final DatabaseType databaseType) {
        this.rule = rule;
        //運(yùn)行時(shí)候的一些配置信息
        this.props = new ShardingProperties(null == props ? new Properties() : props);
        this.databaseType = databaseType;
        //創(chuàng)建 sql 執(zhí)行
        executeEngine = new ShardingExecuteEngine(this.props.<Integer>getValue(ShardingPropertiesConstant.EXECUTOR_SIZE));
        //根據(jù)數(shù)據(jù)庫(kù)類型初始化堪嫂,sql 解析引擎
        parseEngine = SQLParseEngineFactory.getSQLParseEngine(DatabaseTypes.getTrunkDatabaseTypeName(databaseType));
        ConfigurationLogger.log(rule.getRuleConfiguration());
        ConfigurationLogger.log(props);
    }
創(chuàng)建 sql 執(zhí)行引擎

sql 執(zhí)行引擎 ShardingExecuteEngine 復(fù)合了sql 執(zhí)行服務(wù)ShardingExecutorService偎箫,在創(chuàng)建ShardingExecuteEngine 時(shí)同時(shí)創(chuàng)建了 ShardingExecutorService 一對(duì)一的關(guān)系,這里涉及到了guava 的 MoreExecutors 對(duì)jdk Executor 的增強(qiáng)

    public ShardingExecutorService(final int executorSize, final String nameFormat) {
        executorService = MoreExecutors.listeningDecorator(getExecutorService(executorSize, nameFormat));
        //java 系統(tǒng)默認(rèn)創(chuàng)建的線程都是用戶線程,也就是說(shuō)如果 用戶線程不結(jié)束皆串,main 方法不會(huì)結(jié)束的淹办。
        //在線程 start 之前當(dāng)調(diào)用 setDaemon 時(shí),此線程就是守護(hù)線程了愚战,main 方法不會(huì)等待此線程運(yùn)行結(jié)束
        //非主動(dòng)結(jié)束進(jìn)程  或調(diào)用 System.exit 時(shí)無(wú)論什么線程都會(huì)結(jié)束
        //MoreExecutors 是 gava 對(duì) jdk 線程池的增強(qiáng)
        //加 60 秒的 jvm 關(guān)閉鉤子, 在 jvm 中已經(jīng)沒(méi)有用戶線程在運(yùn)行了,那么 等待 60 秒后關(guān)閉線程池
        MoreExecutors.addDelayedShutdownHook(executorService, 60, TimeUnit.SECONDS);
    }
    //創(chuàng)建線程池娇唯,如果配置參數(shù) executorSize 為0那么創(chuàng)建 CachedThreadPool 核心線程數(shù)量為0 最大線程數(shù)量為 Integer.MAX_VALUE
    private ExecutorService getExecutorService(final int executorSize, final String nameFormat) {
        //線程名字為 ShardingSphere 開(kāi)頭
        ThreadFactory shardingThreadFactory = ShardingThreadFactoryBuilder.build(nameFormat);
        return 0 == executorSize ? Executors.newCachedThreadPool(shardingThreadFactory) : Executors.newFixedThreadPool(executorSize, shardingThreadFactory);
    }
sql 解析引擎

1、根據(jù)數(shù)據(jù)庫(kù)名稱創(chuàng)建 SQLParseEngine
這里還有一個(gè)誤區(qū)寂玲,不是每個(gè)數(shù)據(jù)都應(yīng)該有對(duì)應(yīng)的 SQLParseEngine 嗎塔插?其實(shí)類都是同一個(gè) SQLParseEngine 只是其中 String databaseTypeName 不同而已,雖然調(diào)用了構(gòu)造函數(shù)但是此處的初始化還沒(méi)有完成拓哟,真正完成在第一次解析sql的時(shí)候想许。
2、第一次解析sql断序。SQLParseEngine.parse0
(1)是否用緩存中已解析好的結(jié)果
(2)創(chuàng)建 SQLParseKernel 并且調(diào)用了 ParseRuleRegistry.getInstance() 完整初始化過(guò)程流纹。

 public static SQLParseEngine getSQLParseEngine(final String databaseTypeName) {
        if (ENGINES.containsKey(databaseTypeName)) {
            return ENGINES.get(databaseTypeName);
        }
        //這里同步保證每個(gè)數(shù)據(jù)庫(kù)只有一個(gè) SQLParseEngine 實(shí)例
        synchronized (ENGINES) {
            if (ENGINES.containsKey(databaseTypeName)) {
                return ENGINES.get(databaseTypeName);
            }
            SQLParseEngine result = new SQLParseEngine(databaseTypeName);
            ENGINES.put(databaseTypeName, result);
            return result;
        }
    }
第一次解析sql。SQLParseEngine.parse0

因?yàn)榈谝淮螆?zhí)行解析 sql 所以虛擬機(jī)會(huì)加載 ParseRuleRegistry 到內(nèi)存违诗,并且調(diào)用 ParseRuleRegistry 中的靜態(tài)代碼塊漱凝。
1、利用 java spi 機(jī)制加載 SQLParserEntry 的實(shí)現(xiàn)類到內(nèi)存诸迟,SQLParserEntry 的實(shí)現(xiàn)類包括MySQLParserEntry,OracleParserEntry 茸炒,分別在不同的模塊中,所以可以按需引入阵苇。
2壁公、加載 sql 解析規(guī)范例如 sql-statement-rule-definition.xml 文件并且解析存放在Map<String, SQLStatementRuleDefinition> sqlStatementRuleDefinitions 中。

 private SQLStatement parse0(final String sql, final boolean useCache) {
        //是否用緩存中已解析好的結(jié)果
        if (useCache) {
            Optional<SQLStatement> cachedSQLStatement = cache.getSQLStatement(sql);
            if (cachedSQLStatement.isPresent()) {
                return cachedSQLStatement.get();
            }
        }
        // ParseRuleRegistry.getInstance() 獲取定的一些規(guī)范绅项,和配置信息
        SQLStatement result = new SQLParseKernel(ParseRuleRegistry.getInstance(), databaseTypeName, sql).parse();
        if (useCache) {
            cache.put(sql, result);
        }
        return result;
    }

ParseRuleRegistry.getInstance()

 static {
        //spi 機(jī)制加載注冊(cè)的 sql 語(yǔ)法解析類型紊册,例如 oracl mysql
        NewInstanceServiceLoader.register(SQLParserEntry.class);
        instance = new ParseRuleRegistry();
    }
//key 接口類型, value 實(shí)現(xiàn)類集合
private static final Map<Class, Collection<Class<?>>> SERVICE_MAP = new HashMap<>();
   /**
     * 根據(jù) ServiceLoader java util spi 機(jī)制獲取相應(yīng)的 service 目錄默認(rèn)在 META-INF/services/
     */
    public static <T> void register(final Class<T> service) {
        for (T each : ServiceLoader.load(service)) {
            registerServiceClass(service, each);
        }
    }

加載 sql 解析規(guī)范例如 sql-statement-rule-definition.xml 文件并且解析存放在Map<String, SQLStatementRuleDefinition> sqlStatementRuleDefinitions 中快耿,key 為數(shù)據(jù)庫(kù)名稱囊陡,value 為規(guī)則類芳绩。

    /**
     * load sql 解析規(guī)范 例如sql-statement-rule-definition.xml
     */
    private void initParseRuleDefinition() {
        ExtractorRuleDefinitionEntity generalExtractorRuleEntity = extractorRuleLoader.load(RuleDefinitionFileConstant.getExtractorRuleDefinitionFile());
        FillerRuleDefinitionEntity generalFillerRuleEntity = fillerRuleLoader.load(RuleDefinitionFileConstant.getFillerRuleDefinitionFile());
        for (SQLParserEntry each : NewInstanceServiceLoader.newServiceInstances(SQLParserEntry.class)) {
            String databaseTypeName = each.getDatabaseTypeName();
            fillerRuleDefinitions.put(databaseTypeName, createFillerRuleDefinition(generalFillerRuleEntity, databaseTypeName));
            sqlStatementRuleDefinitions.put(databaseTypeName, createSQLStatementRuleDefinition(generalExtractorRuleEntity, databaseTypeName));
        }
    }
那隨后的執(zhí)行階段就可以根據(jù)數(shù)據(jù)庫(kù)名取到對(duì)應(yīng)的解析方法了。
    public SQLAST parse() {
        //根據(jù)數(shù)據(jù)庫(kù)名獲取关斜,對(duì)應(yīng)的解析類
        SQLParser sqlParser = SQLParserFactory.newInstance(databaseTypeName, sql);
        ....略
        //根據(jù)數(shù)據(jù)庫(kù)名獲取示括,對(duì)應(yīng)的解析規(guī)則類
        SQLStatementRule rule = parseRuleRegistry.getSQLStatementRule(databaseTypeName, parseTree.getClass().getSimpleName());
        if (null == rule) {
            throw new SQLParsingException(String.format("Unsupported SQL of `%s`", sql));
        }
        return new SQLAST((ParserRuleContext) parseTree, getParameterMarkerIndexes((ParserRuleContext) parseTree), rule);
    }

至此 sharding jdbc 4.0.1 初始化階段已經(jīng)完成,關(guān)于
2痢畜、內(nèi)部運(yùn)行流程分別是怎么實(shí)現(xiàn)的
(1)初始化過(guò)程
(2)sql 解析
(3)sql 提取
(4)sql 路由
(5)sql 替換
(6)sql 執(zhí)行
中的 2垛膝、3、4丁稀、5吼拥、6 下次分析。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末线衫,一起剝皮案震驚了整個(gè)濱河市凿可,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌授账,老刑警劉巖枯跑,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異白热,居然都是意外死亡敛助,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)屋确,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)纳击,“玉大人,你說(shuō)我怎么就攤上這事攻臀』朗” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵刨啸,是天一觀的道長(zhǎng)堡赔。 經(jīng)常有香客問(wèn)我,道長(zhǎng)设联,這世上最難降的妖魔是什么加匈? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮仑荐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘纵东。我一直安慰自己粘招,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布偎球。 她就那樣靜靜地躺著洒扎,像睡著了一般辑甜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上袍冷,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天磷醋,我揣著相機(jī)與錄音,去河邊找鬼胡诗。 笑死邓线,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的煌恢。 我是一名探鬼主播骇陈,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼瑰抵!你這毒婦竟也來(lái)了你雌?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤二汛,失蹤者是張志新(化名)和其女友劉穎婿崭,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體肴颊,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氓栈,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了苫昌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颤绕。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖祟身,靈堂內(nèi)的尸體忽然破棺而出奥务,到底是詐尸還是另有隱情,我是刑警寧澤袜硫,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布氯葬,位于F島的核電站,受9級(jí)特大地震影響婉陷,放射性物質(zhì)發(fā)生泄漏帚称。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一秽澳、第九天 我趴在偏房一處隱蔽的房頂上張望闯睹。 院中可真熱鬧,春花似錦担神、人聲如沸楼吃。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)孩锡。三九已至酷宵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間躬窜,已是汗流浹背浇垦。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留荣挨,地道東北人男韧。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像垦沉,于是被迫代替她去往敵國(guó)和親煌抒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容