亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

SpringBoot基于AbstractRoutingDataSource實(shí)現(xiàn)多數(shù)據(jù)源動(dòng)態(tài)切換

 更新時(shí)間:2022年05月26日 10:38:26   作者:未來設(shè)計(jì)師  
本文主要介紹了SpringBoot基于AbstractRoutingDataSource實(shí)現(xiàn)多數(shù)據(jù)源動(dòng)態(tài)切換,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

一、場(chǎng)景

在生產(chǎn)業(yè)務(wù)中,有一些任務(wù)執(zhí)行了耗時(shí)較長(zhǎng)的查詢操作,在實(shí)時(shí)性要求不高的時(shí)候,我們希望將這些查詢sql分離出來,去從庫(kù)查詢,以減少應(yīng)用對(duì)主數(shù)據(jù)庫(kù)的壓力。

一種方案是在配置文件中配置多個(gè)數(shù)據(jù)源,然后通過配置類來獲取數(shù)據(jù)源以及mapper相關(guān)的掃描配置,不同的數(shù)據(jù)源配置不佟的mapper掃描位置,然后需要哪一個(gè)數(shù)據(jù)源就注入哪一個(gè)mapper接口即可,這種方法比較簡(jiǎn)單。特征是通過mapper掃描位置區(qū)分?jǐn)?shù)據(jù)源。

第二種方案是配置一個(gè)默認(rèn)使用的數(shù)據(jù)源,然后定義多個(gè)其他的數(shù)據(jù)源,使用aop形成注解式選擇數(shù)據(jù)源。此種方案實(shí)現(xiàn)的核心是對(duì)AbstractRoutingDataSource 類的繼承。這是本文的重點(diǎn)。

二、原理

AbstractRoutingDataSource的多數(shù)據(jù)源動(dòng)態(tài)切換的核心邏輯是:在程序運(yùn)行時(shí),把數(shù)據(jù)源數(shù)據(jù)源通過 AbstractRoutingDataSource 動(dòng)態(tài)織入到程序中,靈活的進(jìn)行數(shù)據(jù)源切換。
基于AbstractRoutingDataSource的多數(shù)據(jù)源動(dòng)態(tài)切換,可以實(shí)現(xiàn)讀寫分離。邏輯如下:

/**
     * Retrieve the current target DataSource. Determines the
     * {@link #determineCurrentLookupKey() current lookup key}, performs
     * a lookup in the {@link #setTargetDataSources targetDataSources} map,
     * falls back to the specified
     * {@link #setDefaultTargetDataSource default target DataSource} if necessary.
     * @see #determineCurrentLookupKey()
     */
    protected DataSource determineTargetDataSource() {
        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
        Object lookupKey = determineCurrentLookupKey();
        DataSource dataSource = this.resolvedDataSources.get(lookupKey);
        if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
            dataSource = this.resolvedDefaultDataSource;
        }
        if (dataSource == null) {
            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
        }
        return dataSource;
    }
/**
     * Determine the current lookup key. This will typically be
     * implemented to check a thread-bound transaction context.
     * <p>Allows for arbitrary keys. The returned key needs
     * to match the stored lookup key type, as resolved by the
     * {@link #resolveSpecifiedLookupKey} method.
     */
    @Nullable
    protected abstract Object determineCurrentLookupKey();

通過實(shí)現(xiàn)抽象方法determineCurrentLookupKey指定需要切換的數(shù)據(jù)源

三、代碼示例

示例中主要依賴 

com.alibaba.druid;tk.mybatis

定義一個(gè)類用于關(guān)聯(lián)數(shù)據(jù)源。通過 TheadLocal 來保存每個(gè)線程選擇哪個(gè)數(shù)據(jù)源的標(biāo)志(key)

@Slf4j
public class DynamicDataSourceContextHolder {
 
 
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    public static List<String> dataSourceIds = new ArrayList<String>();
 
    public static void setDataSourceType(String dataSourceType) {
        log.info("設(shè)置當(dāng)前數(shù)據(jù)源為{}",dataSourceType);
        contextHolder.set(dataSourceType);
    }
 
    public static String getDataSourceType() {
        return contextHolder.get() ;
    }
 
    public static void clearDataSourceType() {
        contextHolder.remove();
    }
 
    public static boolean containsDataSource(String dataSourceId){
        log.info("list = {},dataId={}", JSON.toJSON(dataSourceIds),dataSourceId);
        return dataSourceIds.contains(dataSourceId);
    }
}

繼承

AbstractRoutingDataSource

public class DynamicDataSource  extends AbstractRoutingDataSource {
 
    @Override
    protected Object determineCurrentLookupKey() {
 
        return  DynamicDataSourceContextHolder.getDataSourceType();
    }
}

配置主數(shù)據(jù)庫(kù)master 與從數(shù)據(jù)庫(kù)slave(略)。數(shù)據(jù)源配置可以從簡(jiǎn)

@Configuration
@tk.mybatis.spring.annotation.MapperScan(value = {"com.server.dal.dao"})
@ConditionalOnProperty(name = "java.druid.datasource.master.url")
public class JavaDruidDataSourceConfiguration {
 
    private static final Logger logger = LoggerFactory.getLogger(JavaDruidDataSourceConfiguration.class);
 
    @Resource
    private JavaDruidDataSourceProperties druidDataSourceProperties;
    @Primary
    @Bean(name = "masterDataSource", initMethod = "init", destroyMethod = "close")
    @ConditionalOnMissingBean(name = "masterDataSource")
    public DruidDataSource javaReadDruidDataSource() {
 
        DruidDataSource result = new DruidDataSource();
 
        try {
//            result.setName(druidDataSourceProperties.getName());
            result.setUrl(druidDataSourceProperties.getUrl());
            result.setUsername(druidDataSourceProperties.getUsername());
            result.setPassword(druidDataSourceProperties.getPassword());
            result.setConnectionProperties(
                    "config.decrypt=false;config.decrypt.key=" + druidDataSourceProperties.getPwdPublicKey());
            result.setFilters("config");
            result.setMaxActive(druidDataSourceProperties.getMaxActive());
            result.setInitialSize(druidDataSourceProperties.getInitialSize());
            result.setMaxWait(druidDataSourceProperties.getMaxWait());
            result.setMinIdle(druidDataSourceProperties.getMinIdle());
            result.setTimeBetweenEvictionRunsMillis(druidDataSourceProperties.getTimeBetweenEvictionRunsMillis());
            result.setMinEvictableIdleTimeMillis(druidDataSourceProperties.getMinEvictableIdleTimeMillis());
            result.setValidationQuery(druidDataSourceProperties.getValidationQuery());
            result.setTestWhileIdle(druidDataSourceProperties.isTestWhileIdle());
            result.setTestOnBorrow(druidDataSourceProperties.isTestOnBorrow());
            result.setTestOnReturn(druidDataSourceProperties.isTestOnReturn());
            result.setPoolPreparedStatements(druidDataSourceProperties.isPoolPreparedStatements());
            result.setMaxOpenPreparedStatements(druidDataSourceProperties.getMaxOpenPreparedStatements());
 
            if (druidDataSourceProperties.isEnableMonitor()) {
                StatFilter filter = new StatFilter();
                filter.setLogSlowSql(druidDataSourceProperties.isLogSlowSql());
                filter.setMergeSql(druidDataSourceProperties.isMergeSql());
                filter.setSlowSqlMillis(druidDataSourceProperties.getSlowSqlMillis());
                List<Filter> list = new ArrayList<>();
                list.add(filter);
                result.setProxyFilters(list);
            }
 
        } catch (Exception e) {
 
            logger.error("數(shù)據(jù)源加載失敗:", e);
 
        } finally {
            result.close();
        }
 
 
        return result;
    }
}

注意主從數(shù)據(jù)庫(kù)的bean name

配置DynamicDataSource 

  • targetDataSources 存放數(shù)據(jù)源的k-v對(duì)
  • defaultTargetDataSource 存放默認(rèn)數(shù)據(jù)源

配置事務(wù)管理器和SqlSessionFactoryBean

@Configuration
public class DynamicDataSourceConfig {
? ? private static final String MAPPER_LOCATION = "classpath*:sqlmap/dao/*Mapper.xml";
?
? ? @Bean(name = "dynamicDataSource")
? ? public DynamicDataSource dynamicDataSource(@Qualifier("masterDataSource") DruidDataSource masterDataSource,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?@Qualifier("slaveDataSource") DruidDataSource slaveDataSource) {
? ? ? ? Map<Object, Object> targetDataSource = new HashMap<>();
? ? ? ? DynamicDataSourceContextHolder.dataSourceIds.add("masterDataSource");
? ? ? ? targetDataSource.put("masterDataSource", masterDataSource);
? ? ? ? DynamicDataSourceContextHolder.dataSourceIds.add("slaveDataSource");
? ? ? ? targetDataSource.put("slaveDataSource", slaveDataSource);
? ? ? ? DynamicDataSource dataSource = new DynamicDataSource();
? ? ? ? dataSource.setTargetDataSources(targetDataSource);
? ? ? ? dataSource.setDefaultTargetDataSource(masterDataSource);
? ? ? ? return dataSource;
? ? }
?
? ? @Primary
? ? @Bean(name = "javaTransactionManager")
? ? @ConditionalOnMissingBean(name = "javaTransactionManager")
? ? public DataSourceTransactionManager transactionManager(@Qualifier("dynamicDataSource") DynamicDataSource druidDataSource) {
? ? ? ? return new DataSourceTransactionManager(druidDataSource);
? ? }
?
? ? @Bean(name = "sqlSessionFactoryBean")
? ? public SqlSessionFactoryBean myGetSqlSessionFactory(@Qualifier("dynamicDataSource") DynamicDataSource ?dataSource) {
? ? ? ? SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
? ? ? ? ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
? ? ? ? try {
? ? ? ? ? ? sqlSessionFactoryBean.setMapperLocations(resolver.getResources(MAPPER_LOCATION));
? ? ? ? } catch (IOException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? sqlSessionFactoryBean.setDataSource(dataSource);
? ? ? ? return sqlSessionFactoryBean;
? ? }
}

定義一個(gè)注解用于指定數(shù)據(jù)源

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
    String value();
}

切面的業(yè)務(wù)邏輯。注意指定order,以確保在開啟事務(wù)之前執(zhí)行 。

@Aspect
@Slf4j
@Order(-1)
@Component
public class DataSourceAop {
 
    @Before("@annotation(targetDataSource)")
    public void changeDataSource(JoinPoint point, TargetDataSource targetDataSource) {
        String dsId = targetDataSource.value();
        if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
            log.error("數(shù)據(jù)源[{}]不存在,使用默認(rèn)數(shù)據(jù)源 > {}" + targetDataSource.value() + point.getSignature());
        } else {
            log.info("UseDataSource : {} > {}" + targetDataSource.value() + point.getSignature());
            DynamicDataSourceContextHolder.setDataSourceType(targetDataSource.value());
 
        }
    }
 
    @After("@annotation(targetDataSource)")
    public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource) {
        log.info("RevertDataSource : {} > {}"+targetDataSource.value()+point.getSignature());
        DynamicDataSourceContextHolder.clearDataSourceType();
    }
}

以上略去了pom.xml和application.yml

使用示例

    @Resource
    private ShopBillDOMapper shopBillDOMapper;
 
//使用默認(rèn)數(shù)據(jù)源
    public ShopBillBO queryTestData(Integer id){
 
        return shopBillDOMapper.getByShopBillId(id);
    }
 
//切換到指定的數(shù)據(jù)源
    @TargetDataSource("slaveDataSource")
    public ShopBill queryTestData2(Integer id){
        return shopBillDOMapper.getByShopBillId(id);
    }

如果返回不同的結(jié)果就成功了!

到此這篇關(guān)于SpringBoot基于AbstractRoutingDataSource實(shí)現(xiàn)多數(shù)據(jù)源動(dòng)態(tài)切換的文章就介紹到這了,更多相關(guān)SpringBoot 多數(shù)據(jù)源動(dòng)態(tài)切換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • spring@value注入配置文件值失敗的原因分析

    spring@value注入配置文件值失敗的原因分析

    這篇文章主要介紹了spring@value注入配置文件值失敗的原因分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java I/O 操作及優(yōu)化詳細(xì)介紹

    Java I/O 操作及優(yōu)化詳細(xì)介紹

    這篇文章主要介紹了Java I/O 操作及優(yōu)化詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • File.createTempFile創(chuàng)建臨時(shí)文件的示例詳解

    File.createTempFile創(chuàng)建臨時(shí)文件的示例詳解

    這篇文章主要介紹了File.createTempFile創(chuàng)建臨時(shí)文件的示例詳解,在默認(rèn)臨時(shí)文件目錄中創(chuàng)建一個(gè)空文件,使用給定前綴和后綴生成其名稱。 如果感興趣來了解一下
    2020-07-07
  • 一篇文章帶你了解mybatis的動(dòng)態(tài)SQL

    一篇文章帶你了解mybatis的動(dòng)態(tài)SQL

    這篇文章主要為大家介紹了mybatis的動(dòng)態(tài)SQL?,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • 一起學(xué)JAVA基礎(chǔ)之運(yùn)算符

    一起學(xué)JAVA基礎(chǔ)之運(yùn)算符

    計(jì)算機(jī)的最基本用途之一就是執(zhí)行數(shù)學(xué)運(yùn)算,作為一門計(jì)算機(jī)語(yǔ)言,Java也提供了一套豐富的運(yùn)算符來操縱變量,下面這篇文章主要給大家介紹了關(guān)于JAVA基礎(chǔ)之運(yùn)算符的相關(guān)資料,需要的朋友可以參考下
    2022-01-01
  • SpringBoot使用freemarker導(dǎo)出word文件方法詳解

    SpringBoot使用freemarker導(dǎo)出word文件方法詳解

    這篇文章主要介紹了SpringBoot使用freemarker導(dǎo)出word文件方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-11-11
  • 詳解Java使用Jsch與sftp服務(wù)器實(shí)現(xiàn)ssh免密登錄

    詳解Java使用Jsch與sftp服務(wù)器實(shí)現(xiàn)ssh免密登錄

    這篇文章主要介紹了詳解Java使用Jsch與sftp服務(wù)器實(shí)現(xiàn)ssh免密登錄,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • Spring框架JavaMailSender發(fā)送郵件工具類詳解

    Spring框架JavaMailSender發(fā)送郵件工具類詳解

    這篇文章主要為大家詳細(xì)介紹了Spring框架JavaMailSender發(fā)送郵件工具類,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • 基于mybatis batch實(shí)現(xiàn)批量提交大量數(shù)據(jù)

    基于mybatis batch實(shí)現(xiàn)批量提交大量數(shù)據(jù)

    這篇文章主要介紹了基于mybatis batch實(shí)現(xiàn)批量提交大量數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • SpringMVC ModelAndView的用法使用詳解

    SpringMVC ModelAndView的用法使用詳解

    這篇文章主要介紹了SpringMVC ModelAndView的用法使用詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12

最新評(píng)論