Spring Boot實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源

應(yīng)用場(chǎng)景

  • 當(dāng)應(yīng)用程序需要在不同的場(chǎng)景下訪問(wèn)不同的數(shù)據(jù)庫(kù)時(shí)嗡官,可以用動(dòng)態(tài)數(shù)據(jù)源來(lái)實(shí)現(xiàn)敞映。
  • 實(shí)現(xiàn)方式:自定義注解 + AOP

配置多個(gè)數(shù)據(jù)源信息

jdbc1.driverClassName=com.mysql.jdbc.Driver
jdbc1.username=root
jdbc1.password=123456
jdbc1.url=jdbc:mysql://localhost:3306/db_1
jdbc2.driverClassName=com.mysql.jdbc.Driver
jdbc2.username=root
jdbc2.password=654321
jdbc2.url=jdbc:mysql://localhost:3306/db_2

定義數(shù)據(jù)源名稱常量枚舉

public enum DataSourceEnum {
    MYSQL_DS, ORACLE_DS
}

定義動(dòng)態(tài)數(shù)據(jù)源注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DynamicDS {
    DataSourceEnum value() default DataSourceEnum.MYSQL_DS;
}

定義DataSourceContextHolder

public class DataSourceContextHolder {
    private static ThreadLocal<DataSourceEnum> context = new ThreadLocal<>();

    public static void setDataSourceName(DataSourceEnum dsName){
        context.set(dsName);
    }

    public static DataSourceEnum getDataSourceName(){
        return context.get();
    }

    public static void clearDS(){
        context.remove();
    }
}

pom加入AOP的依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

AOP實(shí)現(xiàn)

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
public class DynamicDataSourceAspect {
    @Before("@annotation(DynamicDS)")
    public void setDataSource(JoinPoint joinPoint)
            throws NoSuchMethodException {
        String methodName = joinPoint.getSignature().getName();
        Class<?> clazz = joinPoint.getTarget().getClass();
        Method method = clazz.getMethod(methodName,
            ((MethodSignature) joinPoint.getSignature()).getParameterTypes());
        if (method.isAnnotationPresent(DynamicDS.class)) {
            DataSourceContextHolder.setDataSourceName(
                    method.getAnnotation(DynamicDS.class).value());
        }
    }

    @After("@annotation(DynamicDS)")
    public void clearDataSource() {
        DataSourceContextHolder.clearDS();
    }
}

定義動(dòng)態(tài)數(shù)據(jù)源

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

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

Mybatis配置類

import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

@Configuration
@MapperScan(basePackages="com.**.dao")
@PropertySource("classpath:datasource.properties")
public class MybatisConfig implements EnvironmentAware {
    @Primary
    @Bean
    public DataSource mysqlDS() throws Exception {
        Properties prop = new Properties();
        prop.setProperty("username",this.env.getProperty("jdbc1.username"));
        prop.setProperty("password",this.env.getProperty("jdbc1.password"));
        prop.setProperty("url",this.env.getProperty("jdbc1.url"));
        prop.setProperty("driverClassName",this.env.getProperty("jdbc1.driverClassName"));
        return DruidDataSourceFactory.createDataSource(prop);
    }

    @Bean
    public DataSource oracleDS() throws Exception {
        Properties prop = new Properties();
        prop.setProperty("username",this.env.getProperty("jdbc2.username"));
        prop.setProperty("password",this.env.getProperty("jdbc2.password"));
        prop.setProperty("url",this.env.getProperty("jdbc2.url"));
        prop.setProperty("driverClassName",this.env.getProperty("jdbc2.driverClassName"));
        return DruidDataSourceFactory.createDataSource(prop);
    }

    @Bean
    public DataSource dynamicDS(@Qualifier("mysqlDS") DataSource mysqlDS,
                                @Qualifier("oracleDS") DataSource oracleDS) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setDefaultTargetDataSource(mysqlDS);
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceEnum.MYSQL_DS, mysqlDS);
        targetDataSources.put(DataSourceEnum.ORACLE_DS, oracleDS);
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.afterPropertiesSet();
        return dynamicDataSource;
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(@Qualifier("dynamicDS") DataSource dynamicDS) throws IOException {
        SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(dynamicDS);
        ResourcePatternResolver res = new PathMatchingResourcePatternResolver();
        sqlSessionFactory.setMapperLocations(
                res.getResources("classpath:com/**/dao/*.xml"));
        return sqlSessionFactory;
    }

    private Environment env;

    @Override
    public void setEnvironment(Environment environment) {
        this.env = environment;
    }
}

具體使用

@DynamicDS(DataSourceEnum.MYSQL_DS)
  • 將該注解貼于具體場(chǎng)景service實(shí)現(xiàn)類的方法上
  • 括號(hào)里的值即是需要使用到的具體數(shù)據(jù)庫(kù)的名稱(在枚舉中定義的常量)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市绕德,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖童芹,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異曹傀,居然都是意外死亡辐脖,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門皆愉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人艇抠,你說(shuō)我怎么就攤上這事幕庐。” “怎么了家淤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵异剥,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我絮重,道長(zhǎng)冤寿,這世上最難降的妖魔是什么歹苦? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮督怜,結(jié)果婚禮上殴瘦,老公的妹妹穿的比我還像新娘。我一直安慰自己号杠,他們只是感情好蚪腋,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著姨蟋,像睡著了一般屉凯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上眼溶,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天悠砚,我揣著相機(jī)與錄音,去河邊找鬼堂飞。 笑死哩簿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的酝静。 我是一名探鬼主播节榜,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼别智!你這毒婦竟也來(lái)了宗苍?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤薄榛,失蹤者是張志新(化名)和其女友劉穎讳窟,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體敞恋,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丽啡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了硬猫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片补箍。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖啸蜜,靈堂內(nèi)的尸體忽然破棺而出坑雅,到底是詐尸還是另有隱情,我是刑警寧澤衬横,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布裹粤,位于F島的核電站,受9級(jí)特大地震影響蜂林,放射性物質(zhì)發(fā)生泄漏遥诉。R本人自食惡果不足惜拇泣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望矮锈。 院中可真熱鬧霉翔,春花似錦、人聲如沸愕难。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)猫缭。三九已至葱弟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間猜丹,已是汗流浹背芝加。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留射窒,地道東北人藏杖。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像脉顿,于是被迫代替她去往敵國(guó)和親蝌麸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350