Spring Boot2.x 多數(shù)據(jù)源配置

1 為什么需要多數(shù)據(jù)源

在比較大型的項(xiàng)目中赫模,數(shù)據(jù)庫(kù)可能會(huì)分布在多臺(tái)服務(wù)器上,例如有若干個(gè)數(shù)據(jù)庫(kù)服務(wù)是專門(mén)存放日志數(shù)據(jù)的蒸矛,又有若干個(gè)數(shù)據(jù)庫(kù)服務(wù)是專門(mén)存放業(yè)務(wù)數(shù)據(jù)的等等....這時(shí)候應(yīng)用程序如果需要對(duì)這兩種類型的數(shù)據(jù)進(jìn)行處理的話瀑罗,就需要配置多數(shù)據(jù)源了。

本文將使用的Spring Boot的版本是2.1.1.RELEASE雏掠,其他的版本配置可能會(huì)略有不同斩祭,具體的建議參考Spring Boot官方文檔,文檔中有多數(shù)據(jù)源配置的相關(guān)介紹乡话。并且本文僅針對(duì)JPA的多數(shù)據(jù)源配置摧玫,其他的ORM例如MyBaties不在本文的討論范圍。

2 項(xiàng)目結(jié)構(gòu)

├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── top
│   │   │       └── yeonon
│   │   │           └── multidatasource
│   │   │               ├── controller
│   │   │               │   └── HelloController.java
│   │   │               ├── dbconfig
│   │   │               │   ├── DataSourceConfig.java
│   │   │               │   ├── DockerDatasourceConfig.java
│   │   │               │   └── LocalDataSourceConfig.java
│   │   │               ├── entity
│   │   │               │   ├── docker
│   │   │               │   │   └── User.java
│   │   │               │   └── local
│   │   │               │       └── Course.java
│   │   │               ├── MultiDatasourceApplication.java
│   │   │               └── repository
│   │   │                   ├── docker
│   │   │                   │   └── UserRepository.java
│   │   │                   └── local
│   │   │                       └── CourseRepository.java
│   │   └── resources
│   │       ├── application.properties
│   │       ├── static
│   │       └── templates

上面是項(xiàng)目的包結(jié)構(gòu)蚊伞,和單一數(shù)據(jù)源的最大區(qū)別就是在entity包和repository包中細(xì)分了不同的數(shù)據(jù)源席赂,例如我這里的是local以及docker,這樣區(qū)分的好處后面會(huì)看到时迫。其他的就沒(méi)什么了颅停,真有需要注意的地方,會(huì)穿插的介紹掠拳。

3 數(shù)據(jù)源的配置

首先是DataSourceConfig類癞揉,該類是一個(gè)配置類(有@Configuration注解的類),我們?cè)谶@里配置數(shù)據(jù)源DataSource:

@Configuration
public class DataSourceConfig {

    @Bean(name = "localDatasource")
    @Qualifier(value = "localDatasource")
    @ConfigurationProperties(prefix = "spring.datasource.local")
    @Primary
    public DataSource localDatasource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "dockerDatasource")
    @Qualifier(value = "dockerDatasource")
    @ConfigurationProperties(prefix = "spring.datasource.docker")
    public DataSource dockerDatasource() {
        return DataSourceBuilder.create().build();
    }
}
  1. @Bean注解就表示該方法是用來(lái)配置Bean的,name屬性指示了該Bean的名字喊熟。
  2. @Qualifier柏肪,因?yàn)槲覀冞@里為DataSource這個(gè)類型配置了兩個(gè)Bean,如果不做特殊處理芥牌,Spring在對(duì)Bean進(jìn)行自動(dòng)注入的時(shí)候烦味,將無(wú)法知道使用哪個(gè)Bean,好的情況是直接報(bào)錯(cuò)壁拉,令人郁悶的情況是Spring不保錯(cuò)谬俄,但是僅僅有一個(gè)Bean生效,即另一個(gè)被覆蓋了弃理,這種情況下的BUG排查是非常麻煩的溃论。
  3. @ConfigurationProperties,該注解是為該Bean配置一個(gè)與之對(duì)應(yīng)的屬性痘昌,prefix即前綴钥勋,所以在配置local數(shù)據(jù)源的時(shí)候,我們可以這樣配置:spring.datasource.local.dirver-class-name:XXX(其他的同理)辆苔。
  4. @Primary算灸,用來(lái)標(biāo)識(shí)主庫(kù),這個(gè)注解是必須的姑子,但是也只能有一個(gè)乎婿。各位可以自行嘗試一下不加或者多加會(huì)出現(xiàn)什么錯(cuò)誤,加深理解街佑。

重要的就是這幾個(gè)注解了谢翎,DataSource實(shí)例就直接用DataSourceBuilder來(lái)創(chuàng)建就行了,如果你有自己定制好的DataSource實(shí)例也可以不用DataSourceBuilder來(lái)創(chuàng)建沐旨。

光配置數(shù)據(jù)源的Bean還沒(méi)完森逮,在單數(shù)據(jù)源的情況下,Spring Boot會(huì)通過(guò)自動(dòng)配置將數(shù)據(jù)源DataSource(Spring Boot也會(huì)自動(dòng)的完成對(duì)數(shù)據(jù)源Bean的配置)磁携,然后將其注入到LocalContainerEntityManagerFactoryBean實(shí)體類工廠以及PlatformTransactionManager事務(wù)管理對(duì)象里褒侧,以此完成對(duì)JPA的自動(dòng)配置。但在多數(shù)據(jù)源的情況下谊迄,Spring Boot自動(dòng)配置還不能幫我們完成這樣的注入闷供,所以需要我們手動(dòng)配置LocalContainerEntityManagerFactoryBean以及PlatformTransactionManager。下面來(lái)看代碼:

@Configuration
//開(kāi)啟事務(wù)管理
@EnableTransactionManagement
//開(kāi)啟JpaRepositories
@EnableJpaRepositories(
        //在這個(gè)JpaRepositories中用到的事務(wù)管理器统诺,這里是Ref歪脏,即引用
        transactionManagerRef = "localTransactionManager",
        //實(shí)體類工廠
        entityManagerFactoryRef = "localEntityManagerFactory",
        //JpaRepositories所在的包名,這也是為什么我們要細(xì)分包名的原因
        basePackages = "top.yeonon.multidatasource.repository.local"
)
public class LocalDataSourceConfig {

    //這里必須要要加 @Qualifier(value = "localDatasource")注解
    //否則Spring將不會(huì)知道用哪個(gè)Bean
    @Autowired
    @Qualifier(value = "localDatasource")
    private DataSource localDatasource;

    //JpaProperties粮呢,Spring自動(dòng)配置會(huì)自動(dòng)加載該Bean
    @Autowired
    private JpaProperties jpaProperties;

    //配置實(shí)體類工廠
    @Bean(name = "localEntityManagerFactory")
    //在數(shù)據(jù)源的配置中婿失,我們將Local配置成了主庫(kù)钞艇,所以必須在這里有@Primary注解
    @Primary
    public LocalContainerEntityManagerFactoryBean localEntityManagerFactory(EntityManagerFactoryBuilder builder) {
        //創(chuàng)建一個(gè)工廠
        return builder
                .dataSource(localDatasource)        //數(shù)據(jù)源實(shí)例,在這里指的就是localDatasource這個(gè)實(shí)例
                .properties(jpaProperties.getProperties()) //加Jpa的屬性配置加入進(jìn)來(lái)
                .packages("top.yeonon.multidatasource.entity.local") //實(shí)體類包名
                .persistenceUnit("localPersistenceUnit") 
                .build();
    }

    @Bean(name = "localTransactionManager")
    //和上面一樣豪硅,必須要有該注解
    @Primary
    public PlatformTransactionManager localTransactionManager(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(localEntityManagerFactory(builder).getObject());
    }
}

這樣就完成了LocalDatasource的配置哩照,下面直接來(lái)看docker數(shù)據(jù)源的配置吧,和LocalDataSourceConfig非常類似懒浮,就不多說(shuō)了飘弧,關(guān)鍵是注意名稱:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        transactionManagerRef = "dockerTransactionManager",
        entityManagerFactoryRef = "dockerEntityManagerFactory",
        basePackages = {"top.yeonon.multidatasource.repository.docker"}
)
public class DockerDatasourceConfig {

    @Autowired
    @Qualifier(value = "dockerDatasource")
    private DataSource dockerDatasource;

    @Autowired
    private JpaProperties jpaProperties;

    @Bean(name = "dockerEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean dockerEntityManagerFactory(EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(dockerDatasource)
                .properties(jpaProperties.getProperties())
                .packages("top.yeonon.multidatasource.entity.docker")
                .persistenceUnit("dockerPersistenceUnit")
                .build();
    }

    @Bean
    public PlatformTransactionManager dockerTransactionManager(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(dockerEntityManagerFactory(builder).getObject());
    }
}

除了名稱不同之外,還有就是這里不需要也不應(yīng)該加入@Primary注解砚著。配置完了嗎眯牧?其實(shí)還沒(méi)有,別忘了在屬性配置文件中配置url赖草,username等...下面是一個(gè)示例:

spring.datasource.local.jdbc-url=jdbc:mysql://localhost:3306/test?useSSL=false
spring.datasource.local.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.local.username=root
spring.datasource.local.password=124563

spring.datasource.docker.jdbc-url=jdbc:mysql://XXX.XXX.XXX.XXX:4306/test?useSSL=false
spring.datasource.docker.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.docker.username=root
spring.datasource.docker.password=124563

spring.jpa.hibernate.ddl-auto=none

發(fā)現(xiàn)了嗎?這里url配置和以往不太一樣剪个,以前僅僅是url即可秧骑,現(xiàn)在需要使用的屬性名字是jdbc-url,這是新版的Spring Boot修改的扣囊,大家可以嘗試一下沿用原來(lái)的url乎折,看看錯(cuò)誤堆棧,加深理解侵歇。

至此骂澄,就算是完成配置了,下面是一個(gè)簡(jiǎn)單的測(cè)試:

@RestController
@RequestMapping("/hello")
public class HelloController {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private CourseRepository courseRepository;

    @GetMapping("user")
    public User user(Long id) {
        return userRepository.findById(id).orElse(null);
    }

    @GetMapping("course")
    public Course course(Long id) {
        return courseRepository.findById(id).orElse(null);
    }
}

運(yùn)行后惕虑,往數(shù)據(jù)庫(kù)里加點(diǎn)測(cè)試數(shù)據(jù)坟冲,訪問(wèn)這里配置的路徑,應(yīng)該就能得到期望的結(jié)果了溃蔫。

終于完成了健提,其實(shí)這個(gè)多數(shù)據(jù)源的配置說(shuō)難也不難,但也不簡(jiǎn)單伟叛,關(guān)鍵在于要“細(xì)心”K奖浴!统刮!實(shí)體類工廠的Bean以及事務(wù)管理器的Bean的名字不要寫(xiě)錯(cuò)紊遵,還有就是自動(dòng)注入以及配置DataSource時(shí)候一定要有@Qualifier注解!=拿伞暗膜!而且@Primary也一定要有,不要多配辉哥,也不可少配桦山。

4 小結(jié)

最后攒射,總結(jié)一下整個(gè)配置的流程:

  1. 先配置數(shù)據(jù)源DataSouce的Bean,因?yàn)橐獮镈ataSouce這個(gè)類型配置多個(gè)Bean恒水,所以要有@Qualifier注解來(lái)標(biāo)識(shí)定位具體是哪個(gè)Bean会放。還要有屬性配置@ConfigurationProperties,否則無(wú)法讀取屬性钉凌,就只能在代碼中配置url咧最,username等屬性了。
  2. 之后為多個(gè)不同的數(shù)據(jù)源配置transactionManager以及entityManagerFactory御雕,核心是將數(shù)據(jù)源注入到這兩個(gè)玩意兒內(nèi)部矢沿,然后生成與特定數(shù)據(jù)源配套的transactionManager和entityManagerFactory。也還要注意在主庫(kù)的配置中加上@Primary注解酸纲。
  3. 在屬性配置文件中進(jìn)行屬性配置即可捣鲸。

5 參考資料

Spring Boot多數(shù)據(jù)源配置與使用

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市闽坡,隨后出現(xiàn)的幾起案子栽惶,更是在濱河造成了極大的恐慌,老刑警劉巖疾嗅,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件外厂,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡代承,警方通過(guò)查閱死者的電腦和手機(jī)汁蝶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)论悴,“玉大人掖棉,你說(shuō)我怎么就攤上這事“蚬溃” “怎么了啊片?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)玖像。 經(jīng)常有香客問(wèn)我紫谷,道長(zhǎng),這世上最難降的妖魔是什么捐寥? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任笤昨,我火速辦了婚禮,結(jié)果婚禮上握恳,老公的妹妹穿的比我還像新娘瞒窒。我一直安慰自己,他們只是感情好乡洼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布崇裁。 她就那樣靜靜地躺著匕坯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拔稳。 梳的紋絲不亂的頭發(fā)上葛峻,一...
    開(kāi)封第一講書(shū)人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音巴比,去河邊找鬼术奖。 笑死,一個(gè)胖子當(dāng)著我的面吹牛轻绞,可吹牛的內(nèi)容都是我干的采记。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼政勃,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼唧龄!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起奸远,我...
    開(kāi)封第一講書(shū)人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤选侨,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后然走,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡戏挡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年芍瑞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褐墅。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拆檬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出妥凳,到底是詐尸還是另有隱情竟贯,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布逝钥,位于F島的核電站屑那,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏艘款。R本人自食惡果不足惜持际,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望哗咆。 院中可真熱鬧蜘欲,春花似錦、人聲如沸晌柬。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至澈歉,卻和暖如春展鸡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背闷祥。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工娱颊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人凯砍。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓箱硕,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親悟衩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子剧罩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

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

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,806評(píng)論 6 342
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)座泳,斷路器惠昔,智...
    卡卡羅2017閱讀 134,652評(píng)論 18 139
  • 1.1 spring IoC容器和beans的簡(jiǎn)介 Spring 框架的最核心基礎(chǔ)的功能是IoC(控制反轉(zhuǎn))容器,...
    simoscode閱讀 6,713評(píng)論 2 22
  • 1.1 Spring IoC容器和bean簡(jiǎn)介 本章介紹了Spring Framework實(shí)現(xiàn)的控制反轉(zhuǎn)(IoC)...
    起名真是難閱讀 2,580評(píng)論 0 8
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架挑势,建立于...
    Hsinwong閱讀 22,394評(píng)論 1 92