大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

專注Java教育14年 全國咨詢/投訴熱線:400-8080-105
動力節點LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁 hot資訊 MyBatis讀寫分離詳解

MyBatis讀寫分離詳解

更新時間:2022-04-06 09:58:38 來源:動力節點 瀏覽2635次

MyBatis讀寫分離是什么?對于初學者來說可能還不是很了解,下面動力節點小編來告訴大家。

ShardingSphere

ShardingSphere由JDBC、Proxy和Sidecar組成(規劃中),可以獨立部署,支持混合部署。ShardingSphere Proxy 與 MyCat 定位相同,而 ShardingSphere JDBC 在 Java 的 JDBC 層提供了額外的服務。

SpringBoot 集成 shardingsphere JDBC 也非常方便。引入包和編寫配置文件后即可使用。但是事務中有個小問題,就是事務中的寫操作之后,后面的讀操作都是從主庫中讀取的;也就是說,在寫操作之前,事務中的讀仍然是從庫中讀取的,可能會造成臟寫。

使用 Mybatis 攔截器

大部分代碼層面的讀寫分離都是通過判斷sql的讀寫類型來攔截sql并重定向數據庫。Shardingsphere JDBC 也不例外。

Mybatis 允許我們自定義 Interceptor。我們需要實現Interceptor接口,在自定義的Interceptor類上添加@Intercepts注解。在@Intercepts注解中,我們可以指定攔截方式。

多個數據源

由于讀寫分離是在代碼層面進行的,所以必須有讀寫庫。這里使用了多數據源功能。不用mybatis/mybatis plus默認的多數據源生成方式,多數據源自己配置。其實也可以使用默認的生成方式。自己寫的目的是為了更好的理解里面的原理。【配置文件中配置的格式是根據mybatis配置的格式加上多個數據源來配置的】

代碼

多數據源配置

/**
 * 主數據庫
 */
@ConfigurationProperties("spring.datasource.dynamic.datasource.master")
公共數據源 masterDataSource(){
    log.info("加載主數據源主數據源。");
    返回 DruidDataSourceBuilder.create().build();
}
/**
 * 數據庫從庫
 */
@ConfigurationProperties("spring.datasource.dynamic.datasource.slave1")
公共數據源 slave1DataSource(){
    log.info("從數據源 slave1 DataSource 加載。");
    返回 DruidDataSourceBuilder.create().build();
}
/**
 * 動態數據源
 */
@豆角,扁豆
公共數據源 myRoutingDataSource(@Qualifier("masterDataSource") 數據源 masterDataSource,
                                      @Qualifier("slave1DataSource") 數據源 slave1DataSource) {
    log.info("load[masterDataSource-slave1DataSource]設置為動態數據源DynamicDataSource。");
    Map<Object, Object> targetDataSources = new HashMap<>(2);
    targetDataSources.put(DBTypeEnum.MASTER, masterDataSource);
    targetDataSources.put(DBTypeEnum.SLAVE1, slave1DataSource);
    動態數據源 dynamicDataSource = new DynamicDataSource();
    dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
    動態數據源.setTargetDataSources(targetDataSources);
    返回動態數據源;
}

DBTypeEnum

public enum DBTypeEnum {
    /**Main library*/
    MASTER,
    /**From library 1*/
    SLAVE1
}

動態數據源

此處指定數據源的鍵。在每條sql語句執行之前,都會執行determineCurrentLookupKey獲取數據源。DbContextHolder.get()是獲取當前線程中指定數據源的key,會在自定義攔截器中指定。

公共類 DynamicDataSource 擴展 AbstractRoutingDataSource { 
    @Nullable @Override
    受保護對象 determineCurrentLookupKey() { 
        return DbContextHolder.get(); 
    } 
}
公共類 DbContextHolder {
    私有靜態最終 ThreadLocal<DBTypeEnum> CONTEXT_HOLDER = new ThreadLocal<>(); 
    私有靜態最終 AtomicInteger COUNTER = new AtomicInteger(-1); 
    public static void set(DBTypeEnum dbType) { 
        log.debug("切換到{}", dbType.name()); 
        CONTEXT_HOLDER.set(dbType); 
    }
    公共靜態 DBTypeEnum get() { 
        return CONTEXT_HOLDER.get(); 
    }
    公共靜態 DBTypeEnum getMaster() { 
        return DBTypeEnum.MASTER; 
    } 
    public static DBTypeEnum getSlave() { 
        // 可以輪詢多個從庫
        int index = COUNTER.getAndIncrement() % 2;
        if (COUNTER.get() > 9999) { 
            COUNTER.set(-1); 
        }
        返回 DBTypeEnum.SLAVE1; 
    } 
}

攔截器

在上一步中,我們定義了多個數據源并設置了數據源選擇的基礎(DbContextHolder.get())。這一步就是按照一定的規則在攔截器中設置這個基礎。

代碼

攔截器

@Intercepts({ 
        @Signature(type = Executor.class, method = "update", args = { 
                MappedStatement.class, Object.class }), 
        @Signature(type = Executor.class, method = "query", args = { 
                MappedStatement.class, Object.class, RowBounds.class, 
                ResultHandler.class }), 
        @Signature(type = Executor.class, method = "close", args = {boolean.class}) 
}) 
public class DbSelectorInterceptor implements Interceptor { 
    private靜態最終字符串 REGEX = ".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*"; 
    private static final Map<String, DBTypeEnum> CACHE_MAP = new ConcurrentHashMap<>(); 
    @覆蓋
    公共對象攔截(調用調用)拋出 Throwable { 
        String methodName = invocation.getMethod().getName(); 
        字符串 closeMethodName = "關閉"; 
        布爾同步活動 = TransactionSynchronizationManager.isSynchronizationActive(); 
        DBTypeEnum 數據庫類型 = null; 
        if(!synchronizationActive && !closeMethodName.equals(methodName)) { 
            Object[] objects = invocation.getArgs(); 
            MappedStatement ms = (MappedStatement) 對象[0]; 
            if((databaseType = CACHE_MAP.get(ms.getId())) == null) { 
                //讀取方法
                if(ms.getSqlCommandType().equals(SqlCommandType.SELECT)) {
                    //! selectKey是自增ID查詢主鍵(SELECT LAST_INSERT_ID())方法,使用主庫
                    if(ms.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)) { 
                        databaseType = DbContextHolder.getMaster(); 
                    } else { 
                        BoundSql boundSql = ms.getSqlSource().getBoundSql(objects[1]); 
                        String sql = boundSql.getSql().toLowerCase(Locale.CHINA).replaceAll("[\\t\\n\\r]", ""); 
                        if(sql.matches(REGEX)) { 
                            databaseType = DbContextHolder.getMaster(); 
                        } 別的 {
                            數據庫類型 = DbContextHolder.getSlave(); 
                        } 
                    } 
                }else{
                    數據庫類型 = DbContextHolder.getMaster(); 
                } 
                log.debug("設置方法[{}]使用[{}]策略,SqlCommandType [{}]..", ms.getId(), databaseType.name(), ms.getSqlCommandType().name()) ; 
                CACHE_MAP.put(ms.getId(), databaseType); 
            } 
        } else { 
            if (synchronizationActive) { 
                log.debug("事務使用 [{}] 策略", DBTypeEnum.MASTER.name()); 
            } 別的 {
                log.debug("關閉方法重置為 [{}] 策略", DBTypeEnum.MASTER.name()); 
            }
            數據庫類型 = DbContextHolder.getMaster(); 
        } 
        DbContextHolder.set(databaseType); 
        返回調用.proceed(); 
    } 
    @Override 
    public Object plugin(Object target) { 
        if (target instanceof Executor) { 
            return Plugin.wrap(target, this); 
        } else {
            返回目標;
        } 
    } 
}

這段代碼比較長,但核心邏輯只有三個:

如果事務啟動,則使用主數據庫;

如果當前連接已關閉,則重置到主庫;【ps:忘記不加會怎樣】

其他情況根據sql語句中的關鍵字select、update、delete判斷;

配置攔截器

這里,攔截器是基于mybatis plus配置的。

    @Bean 
    public MybatisSqlSessionFactoryBean sqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource, 
                                               @Qualifier("slave1DataSource") DataSource slave1DataSource) throws Exception { 
        log.info("自定義配置mybatis-plus of SqlSessionFactory."); 
        MybatisSqlSessionFactoryBean mybatisPlus = new MybatisSqlSessionFactoryBean(); 
        mybatisPlus.setDataSource(myRoutingDataSource(masterDataSource, slave1DataSource)); 
        MybatisConfiguration 配置 = new MybatisConfiguration(); 
        configuration.setJdbcTypeForNull(JdbcType.NULL); 
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setCacheEnabled(false); 
        ///自定義配置
        mybatisPlus.setConfiguration(configuration); 
         設置 mapper.xml 文件路徑
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); 
        org.springframework.core.io.Resource[] resource = resolver.getResources("classpath:mapper/webservice/*.xml"); 
        mybatisPlus.setMapperLocations(resource); 
        //給SqlSessionFactory添加一個插件生效
        mybatisPlus.setPlugins(paginationInterceptor(), new DbSelectorInterceptor()); 
        globalConfig.setMetaObjectHandler(this); 
        mybatisPlus.setGlobalConfig(globalConfig); 
        返回 mybatisPlus;
    }

實際上,它指的是com baomidou。mybatisplus。自動配置。mybatisplusautoconfiguration #sqlsessionfactory,將DbSelectorInterceptor織入。如果大家想了解更多相關知識,可以關注一下動力節點的Mybatis實戰教程,里面的課程內容細致全面,通俗易懂,適合小白學習,希望對大家能夠有所幫助哦。

提交申請后,顧問老師會電話與您溝通安排學習

免費課程推薦 >>
技術文檔推薦 >>
主站蜘蛛池模板: 欧美一级片在线免费观看 | 亚洲欧美天堂网 | 成年女人毛片免费观看中文w | 欧美视频性| 99精品国产兔费观看66 | 抱着cao才爽免费观看 | 天天操天天干天天舔 | 一级国产视频 | 欧美性猛交aa一级 | 精品久久久久久久久久 | 久久久久久不卡 | 男人女人真曰批的视频动态 | 99精品福利 | 久久精品欧美日韩精品 | 亚洲综合在线观看视频 | 精品免费国产一区二区女 | 亚洲综合极品香蕉久久网 | 久久精品国产2020观看福利色 | 国产欧美日韩网站 | 香蕉a视频 | 国产伦精品一区二区三区免费迷 | 韩国欧美一级毛片免费 | 欧美久久久久久久久 | 一本一道| 久久国产香蕉视频 | 青青青爽视频在线观看入口 | 欧美成人区 | 青青青国产免费手机视频在线观看 | 欧美成人黄色网 | 免费一级毛片在级播放 | 四虎在线影院 | 中文字幕在线看 | 中文字幕在线综合 | 国精品一区二区三区 | 香蕉视频一区二区 | 日本一级特黄aa毛片免费观看 | 色爱综合网欧美 | 成人a视频片在线观看免费 成人a视频在线观看 | 久久这里只有精品视频99 | 国产亚洲精品美女久久久 | 国产精品福利在线 |