最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Mybatis通過攔截器實(shí)現(xiàn)單數(shù)據(jù)源內(nèi)多數(shù)據(jù)庫切換

 更新時(shí)間:2023年12月11日 10:40:12   作者:方圓想當(dāng)圖靈  
這篇文章主要為大家詳細(xì)介紹了Mybatis如何通過攔截器實(shí)現(xiàn)單數(shù)據(jù)源內(nèi)多數(shù)據(jù)庫切換,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

物流的分揀業(yè)務(wù)在某些分揀場地只有一個(gè)數(shù)據(jù)源,因?yàn)閿?shù)據(jù)量比較大,將所有數(shù)據(jù)存在一張表內(nèi)查詢速度慢,也為了做不同設(shè)備數(shù)據(jù)的分庫管理,便在這個(gè)數(shù)據(jù)源內(nèi)創(chuàng)建了多個(gè)不同庫名但表完全相同的數(shù)據(jù)庫,如下圖所示:

現(xiàn)在需要上線報(bào)表服務(wù)來查詢所有數(shù)據(jù)庫中的數(shù)據(jù)進(jìn)行統(tǒng)計(jì),那么現(xiàn)在的問題來了,該如何 滿足在配置一個(gè)數(shù)據(jù)源的情況下來查詢該數(shù)據(jù)源下不同數(shù)據(jù)庫的數(shù)據(jù) 呢,借助搜索引擎查到的分庫實(shí)現(xiàn)大多是借助 Sharding-JDBC 框架,配置多個(gè)數(shù)據(jù)源根據(jù)分庫算法實(shí)現(xiàn)數(shù)據(jù)源的切換,但是對于只有一個(gè)數(shù)據(jù)源的系統(tǒng)來說,我覺得引入框架再將單個(gè)數(shù)據(jù)源根據(jù)不同的庫名配置成多個(gè)不同的數(shù)據(jù)源來實(shí)現(xiàn)分庫查詢的邏輯我覺得并不好。

如果我們能在 SQL 執(zhí)行前將 SQL 中所有的表名前拼接上對應(yīng)的庫名的話,那么就能夠?qū)崿F(xiàn)數(shù)據(jù)源的切換了,下面我們講一下使用 JSqlParser 和 Mybatis攔截器 實(shí)現(xiàn)該邏輯,借助 JSqlParser 主要是為了解析SQL,找到其中所有的表名進(jìn)行拼接,如果大家有更好的實(shí)現(xiàn)方式,該組件并不是必須的。

實(shí)現(xiàn)邏輯

SqlSource 是讀取 XML 中 SQL 內(nèi)容并將其發(fā)送給數(shù)據(jù)庫執(zhí)行的對象,如果我們在執(zhí)行前能攔截到該對象,并將其中的 SQL 替換掉便達(dá)成了我們的目的。 SqlSource 有多種實(shí)現(xiàn),包括常見的DynamicSqlSource。其中包含著必要的執(zhí)行邏輯,我們需要做的工作便是在這些邏輯執(zhí)行完之后,對 SQL 進(jìn)行改造,所以這次實(shí)現(xiàn)我們使用了 裝飾器模式,在原來的 SqlSource 上套一層,執(zhí)行完 SqlSource 本身的方法之后對其進(jìn)行增強(qiáng),代碼如下:

public abstract class AbstractDBNameInterceptor {

    /**
     * SqlSource 的裝飾器,作用是增強(qiáng)了 getBoundSql 方法,在基礎(chǔ)上增加了動(dòng)態(tài)分庫的邏輯
     */
    static class SqlSourceDecorator implements SqlSource {

        /**
         * SQL 字段名稱
         */
        private static final String SQL_FIELD_NAME = "sql";

        /**
         * 原本的 sql source
         */
        private final SqlSource sqlSource;

        /**
         * 裝飾器進(jìn)行封裝
         */
        public SqlSourceDecorator(SqlSource sqlSource) {
            this.sqlSource = sqlSource;
        }

        @Override
        public BoundSql getBoundSql(Object parameterObject) {
            try {
                // 先生成出未修改前的 SQL
                BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
                // 獲取數(shù)據(jù)庫名
                String dbName = getSpecificDBName(parameterObject);
                // 有效才修改
                if (isValid(dbName)) {
                    // 生成需要修改完庫名的 SQL
                    String targetSQL = getRequiredSqlWithSpecificDBName(boundSql, dbName);
                    // 更新 SQL
                    updateSql(boundSql, targetSQL);
                }

                return boundSql;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        /**
         * 校驗(yàn)是否為有效庫名
         */
        private boolean isValid(String dbName) {
            return StringUtils.isNotEmpty(dbName) && !"null".equals(dbName);
        }

        /**
         * 獲取到我們想要的庫名的 SQL
         */
        private String getRequiredSqlWithSpecificDBName(BoundSql boundSql, String dbName) throws JSQLParserException {
            String originSql = boundSql.getSql();
            // 獲取所有的表名
            Set<String> tables = TablesNamesFinder.findTables(originSql);
            for (String table : tables) {
                originSql = originSql.replaceAll(table, dbName + "." + table);
            }
            return originSql;
        }

        /**
         * 修改 SQL
         */
        private void updateSql(BoundSql boundSql, String sql) throws NoSuchFieldException, IllegalAccessException {
            // 通過反射修改sql語句
            Field field = boundSql.getClass().getDeclaredField(SQL_FIELD_NAME);
            field.setAccessible(true);
            field.set(boundSql, sql);
        }
    }
    
    // ... 
}

定義了 AbstractDBNameInterceptor 抽象類是為了實(shí)現(xiàn)復(fù)用,并將 SqlSourceDecorator 裝飾器定義為靜態(tài)內(nèi)部類,這樣的話,將所有邏輯都封裝在抽象類內(nèi)部,之后這部分實(shí)現(xiàn)好后研發(fā)直接實(shí)現(xiàn)抽象類的通用方法即可,不必關(guān)注它的內(nèi)部實(shí)現(xiàn)。

結(jié)合注釋我們解釋一下 SqlSourceDecorator 的邏輯,其中用到了 Java 反射相關(guān)的操作。首先通過反射獲取到 SQL,getSpecificDBName 方法是需要自定義實(shí)現(xiàn)的,其中 parameterObject 對象是傳到 DAO 層執(zhí)行查詢時(shí)的參數(shù),在我們的業(yè)務(wù)中是能夠根據(jù)其中的設(shè)備相關(guān)參數(shù)拿到對應(yīng)的所在庫名的,而設(shè)備和具體庫名的映射關(guān)系需要提前初始化好。在獲取到具體的庫名后執(zhí)行 getRequiredSqlWithSpecificDBName 方法來將其拼接到表名前,在這里我們使用到了 JSqlParser 的工具類,解析出來所有的表名,執(zhí)行字符串的替換,最后一步同樣是使用反射操作將該參數(shù)值再寫回去,這樣便完成了指定庫名的任務(wù)。

接下來我們需要看下抽象攔截器中供攔截器復(fù)用的方法,如下:

public abstract class AbstractDBNameInterceptor {

    /**
     * SqlSource 字段名稱
     */
    private static final String SQL_SOURCE_FIELD_NAME = "sqlSource";

    /**
     * 執(zhí)行修改數(shù)據(jù)庫名的邏輯
     */
    protected Object updateDBName(Invocation invocation) throws Throwable {
        // 裝飾器裝飾 SqlSource
        decorateSqlSource((MappedStatement) invocation.getArgs()[0]);
        return invocation.proceed();
    }

    /**
     * 裝飾 SqlSource
     */
    private void decorateSqlSource(MappedStatement statement) throws NoSuchFieldException, IllegalAccessException {
        if (!(statement.getSqlSource() instanceof SqlSourceDecorator)) {
            Field sqlSource = statement.getClass().getDeclaredField(SQL_SOURCE_FIELD_NAME);
            sqlSource.setAccessible(true);
            sqlSource.set(statement, new SqlSourceDecorator(statement.getSqlSource()));
        }
    }
}

這個(gè)還是比較簡單的,只是借助反射機(jī)制做了一層“裝飾”,查詢攔截器實(shí)現(xiàn)如下:

@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
})
public class SelectDBNameInterceptor extends AbstractDBNameInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        return updateDBName(invocation);
    }
}

將其配置到 Mybatis 攔截器中,便能實(shí)現(xiàn)數(shù)據(jù)庫動(dòng)態(tài)切換了。

到此這篇關(guān)于Mybatis通過攔截器實(shí)現(xiàn)單數(shù)據(jù)源內(nèi)多數(shù)據(jù)庫切換的文章就介紹到這了,更多相關(guān)Mybatis單數(shù)據(jù)源內(nèi)多數(shù)據(jù)庫切換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring Cache使用RedisCache案例解析

    Spring Cache使用RedisCache案例解析

    這篇文章主要介紹了Spring Cache使用RedisCache案例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • java比較兩個(gè)list是否相同equals的代碼詳解

    java比較兩個(gè)list是否相同equals的代碼詳解

    在本篇文章里小編給大家分享的是關(guān)于java比較兩個(gè)list是否相同equals的代碼詳解,有需要的朋友們可以參考學(xué)習(xí)下。
    2020-02-02
  • Java真題實(shí)練掌握哈希表的使用

    Java真題實(shí)練掌握哈希表的使用

    哈希表是一種根據(jù)關(guān)鍵碼去尋找值的數(shù)據(jù)映射結(jié)構(gòu),該結(jié)構(gòu)通過把關(guān)鍵碼映射的位置去尋找存放值的地方,說起來可能感覺有點(diǎn)復(fù)雜,我想我舉個(gè)例子你就會明白了,最典型的的例子就是字典
    2022-07-07
  • java中ResultSet遍歷數(shù)據(jù)操作

    java中ResultSet遍歷數(shù)據(jù)操作

    這篇文章主要介紹了java中ResultSet遍歷數(shù)據(jù)操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • Spring Boot 實(shí)現(xiàn)程序的優(yōu)雅退出(詳細(xì)步驟)

    Spring Boot 實(shí)現(xiàn)程序的優(yōu)雅退出(詳細(xì)步驟)

    Spring Boot 為我們提供了優(yōu)雅退出的功能,使應(yīng)用程序能夠在關(guān)閉時(shí)正常處理完所有當(dāng)前請求,避免請求被中斷導(dǎo)致數(shù)據(jù)丟失或不一致等問題,本文將全面介紹如何在 Spring Boot 應(yīng)用程序中實(shí)現(xiàn)優(yōu)雅退出,感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • SSH框架網(wǎng)上商城項(xiàng)目第4戰(zhàn)之EasyUI菜單的實(shí)現(xiàn)

    SSH框架網(wǎng)上商城項(xiàng)目第4戰(zhàn)之EasyUI菜單的實(shí)現(xiàn)

    SSH框架網(wǎng)上商城項(xiàng)目第4戰(zhàn)之EasyUI菜單的實(shí)現(xiàn),本文主要使用EasyUI技術(shù)簡單實(shí)現(xiàn)后臺菜單,感興趣的小伙伴們可以參考一下
    2016-05-05
  • MyBatis的mapper.xml文件中入?yún)⒑头祷刂档膶?shí)現(xiàn)

    MyBatis的mapper.xml文件中入?yún)⒑头祷刂档膶?shí)現(xiàn)

    這篇文章主要介紹了MyBatis的mapper.xml文件中入?yún)⒑头祷刂档膶?shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • myBatis使用@GeneratedValue(generator?=?“...“,?strategy?=?...)注解

    myBatis使用@GeneratedValue(generator?=?“...“,?strategy?=?

    這篇文章主要介紹了myBatis使用@GeneratedValue(generator?=?“...“,?strategy?=?...)注解問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Spring Boot 注解方式自定義Endpoint詳解

    Spring Boot 注解方式自定義Endpoint詳解

    這篇文章主要介紹了Spring Boot注解方式自定義Endpoint詳解,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 如何解決Mybatis-plus中@TableLogic注解失效問題

    如何解決Mybatis-plus中@TableLogic注解失效問題

    這篇文章主要介紹了如何解決Mybatis-plus中@TableLogic注解失效問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05

最新評論

会东县| 太仓市| 怀集县| 平原县| 闻喜县| 浦江县| 丹江口市| 霍州市| 广平县| 澜沧| 溆浦县| 达日县| 灵台县| 永善县| 洛宁县| 肇源县| 阿拉善盟| 礼泉县| 社旗县| 金华市| 徐水县| 清苑县| 静宁县| 新田县| 马尔康县| 富民县| 宁陕县| 新竹县| 鸡东县| 白银市| 陕西省| 西华县| 敦煌市| 临城县| 弋阳县| 北流市| 手游| 敦化市| 万载县| 五大连池市| 承德市|