springboot dynamic多數(shù)據(jù)源demo以及常見切換、事務(wù)的問題
一:引入依賴
<dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.1</version> </dependency>
二:配置多數(shù)據(jù)源
yaml配置
通過yaml配置主數(shù)據(jù)源,這里就只配置了一個主數(shù)據(jù)源,后續(xù)通過代碼來自由的切換數(shù)據(jù)源。
spring: datasource: dynamic: hikari: connection-timeout: 5000 idle-timeout: 30000 # 經(jīng)過idle-timeout時間如果連接還處于空閑狀態(tài), 該連接會被回收 min-idle: 5 # 池中維護(hù)的最小空閑連接數(shù), 默認(rèn)為 10 個 max-pool-size: 16 # 池中最大連接數(shù), 包括閑置和使用中的連接, 默認(rèn)為 10 個 max-lifetime: 60000 # 如果一個連接超過了時長,且沒有被使用, 連接會被回收 is-auto-commit: true primary: master #設(shè)置默認(rèn)的數(shù)據(jù)源或者數(shù)據(jù)源組,默認(rèn)值即為master strict: true #嚴(yán)格匹配數(shù)據(jù)源,默認(rèn)false. true未匹配到指定數(shù)據(jù)源時拋異常,false使用默認(rèn)數(shù)據(jù)源 datasource: master: # 數(shù)據(jù)源名稱 url: username: password: driver-class-name: com.mysql.cj.jdbc.Driver # 如下,如果你是確定的幾個數(shù)據(jù)源,可以直接都在yaml配置寫死即可 # slave_1: # url: # username: # password: # driver-class-name: com.mysql.cj.jdbc.Driver 其中數(shù)據(jù)庫連接池,所有的數(shù)據(jù)庫統(tǒng)一配置,也可以單獨(dú)配置,例如: datasource: master: # 數(shù)據(jù)源名稱 url: username: password: driver-class-name: com.mysql.cj.jdbc.Driver hikari: connection-timeout: 5000 idle-timeout: 30000 # 經(jīng)過idle-timeout時間如果連接還處于空閑狀態(tài), 該連接會被回收 min-idle: 5 # 池中維護(hù)的最小空閑連接數(shù), 默認(rèn)為 10 個 max-pool-size: 16 # 池中最大連接數(shù), 包括閑置和使用中的連接, 默認(rèn)為 10 個 max-lifetime: 60000 # 如果一個連接超過了時長,且沒有被使用, 連接會被回收 is-auto-commit: true
三:切換數(shù)據(jù)源DS注解
DS放在哪里合適?
首先開發(fā)者要了解的基礎(chǔ)知識是,DS注解是基于AOP的原理實(shí)現(xiàn)的,aop的常見失效場景應(yīng)清楚。
比如內(nèi)部調(diào)用失效,shiro代理失效。
具體見切換數(shù)據(jù)源:
- 1.通常建議DS放在serviceImpl的方法上,如事務(wù)注解一樣。(常用方式二:訪問第三方庫api,單獨(dú)抽取接口,并作數(shù)據(jù)處理)
- 2.注解在Controller的方法上或類上:并不是不可以,并不建議的原因主要是controller主要作用是參數(shù)的檢驗(yàn)等一些基礎(chǔ)邏輯的處理,這部分操作常常并不涉及數(shù)據(jù)庫
- 3.注解在service的實(shí)現(xiàn)類的方法或類上:這是建議的方式,service主要是對業(yè)務(wù)的處理, 在復(fù)雜的場景涉及連續(xù)切換不同的數(shù)據(jù)庫。 如果你的方法有通用性,其他service也會調(diào)用你的方法。 這樣別人就不用重復(fù)處理切換數(shù)據(jù)源
- 4.注解在mapper上。(常用方式一):通常如果你某個Mapper對應(yīng)的表只在確定的一個庫,也是可以的。 但是建議只注解在Mapper的類上。
- 5.其他使用方式:繼承抽象類上的DS
- 6.繼承接口上的DS
示例:
@Service @DS("common") public class BookService extends ServiceImpl<BookMapper, Book> { @Resource private BookMapper bookMapper; @Transactional(propagation = Propagation.REQUIRES_NEW) public void save(ReqDto reqDto) { bookMapper.save(reqDto); } }
等。
四:切換數(shù)據(jù)源以及事務(wù)相關(guān)問題
1.使用動態(tài)數(shù)據(jù)源(@DS)時
@Transactional使用不當(dāng)會照成@DS失效。
- 1.實(shí)現(xiàn)層上面加@Transactional,數(shù)據(jù)源沒有切換
- 2.開啟事務(wù)的同時,會從數(shù)據(jù)庫連接池獲取數(shù)據(jù)庫連接;
- 3.如果內(nèi)層的service使用@DS切換數(shù)據(jù)源,只是又做了一層攔截,但是并沒有改變整個事務(wù)的連接;
- 4.在這個事務(wù)內(nèi)的所有數(shù)據(jù)庫操作,都是在事務(wù)連接建立之后,所以會產(chǎn)生數(shù)據(jù)源沒有切換的問題;
- 5.為了使@DS起作用,必須替換數(shù)據(jù)庫連接,也就是改變事務(wù)的傳播機(jī)智,產(chǎn)生新的事務(wù),獲取新的數(shù)據(jù)庫連接
解決方法:
- 去除MasterService.upload上面的@Transactional,數(shù)據(jù)源切換正常,雖然可以解決,但是事務(wù)無效。
- BookService的save上面加@Transactional(propagation =Propagation.REQUIRES_NEW),數(shù)據(jù)源切換,且事務(wù)有效。
- 完美解決。它會重新創(chuàng)建新事務(wù),獲取新的數(shù)據(jù)庫連接,從而得到@DS的數(shù)據(jù)源
2.@Transaction開啟了事務(wù)
為什么多數(shù)據(jù)源事務(wù)不生效?
@Transaction開啟了事務(wù),為什么多數(shù)據(jù)源事務(wù)不生效? 簡單來說:嵌套數(shù)據(jù)源的service中,如果操作了多個數(shù)據(jù)源,不能在最外層加上@Transaction開啟事務(wù),否則切換數(shù)據(jù)源不生效,因?yàn)檫@屬于分布式事務(wù)了,需要用seata方案解決,如果是單個數(shù)據(jù)源(不需要切換數(shù)據(jù)源)可以用@Transaction開啟事務(wù),保證每個數(shù)據(jù)源自己的完整性
加事務(wù)不生效的原因:dynamic-datasource切換數(shù)據(jù)源的原理就是實(shí)現(xiàn)了DataSource接口,實(shí)現(xiàn)了getConnection方法,只要在service中開啟事務(wù),service中對其他數(shù)據(jù)源操作只會使用開啟事務(wù)的數(shù)據(jù)源,因?yàn)殚_啟事務(wù)數(shù)據(jù)源會被緩存下來,可以在DataSourceTransactionManager的doBegin方法中看見那個txObject,如果在一個事務(wù)內(nèi),就會復(fù)用Connection,所以切換不了數(shù)據(jù)源
解決方法:本地事務(wù)
通過本地事務(wù)實(shí)現(xiàn)很簡單,就是循環(huán)提交,發(fā)生錯誤,循環(huán)回滾。 我們默認(rèn)的前提是數(shù)據(jù)庫本身不會異常,比如宕機(jī)。如數(shù)據(jù)在回滾的過程突然宕機(jī),本地事務(wù)就會有問題。如果你需要完整分布式方案請使用seata方案。
使用方法
在最外層的方法添加 @DSTransactional,底下調(diào)用的各個類就正常切換數(shù)據(jù)源即可。
簡單舉例如下:
@DeleteMapping //只要@DSTransactional注解下任一環(huán)節(jié)發(fā)生異常,則全局多數(shù)據(jù)源事務(wù)回滾。 @DSTransactional() @ApiOperation("刪除數(shù)據(jù)源") public String remove(String poolName) { DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource; ds.removeDataSource(poolName); return "刪除成功"; } 但一定要注意Spring事務(wù)@Transational和本地事務(wù)@DSTransactional,不能混用
3.其余問題了解
一:涉及需要切換數(shù)據(jù)源時
1.不能使用事務(wù),否則數(shù)據(jù)源不會切換,使用的還是是第一次加載的數(shù)據(jù)源 。
刪除 操作多數(shù)據(jù)源的方法或者類、接口 上的 注解 @Transactional() 即可。
2.第一次加載的數(shù)據(jù)源之后,第二次(第三次...)操作其它數(shù)據(jù)源,如果數(shù)據(jù)源不存在,使用的還是第一次加載的數(shù)據(jù)源
3.數(shù)據(jù)源名稱最好不要包含下滑線,下滑線的數(shù)據(jù)源切換不了
二:其他
1.接口中A、B兩個方法,A無@Transactional標(biāo)簽,B有,上層通過A間接調(diào)用B,此時事務(wù)不生效。
2.接口中異常(運(yùn)行時異常)被捕獲而沒有被拋出。默認(rèn)配置下,spring 只有在拋出的異常為運(yùn)行時 unchecked 異常時才回滾該事務(wù),也就是拋出的異常為RuntimeException 的子類(Errors也會導(dǎo)致事務(wù)回滾),而拋出 checked 異常則不會導(dǎo)致事務(wù)回滾 。可通過 @Transactional rollbackFor進(jìn)行配置。
3.多線程下事務(wù)管理因?yàn)榫€程不屬于 spring 托管,故線程不能夠默認(rèn)使用 spring 的事務(wù),也不能獲取spring 注入的 bean 。在被 spring 聲明式事務(wù)管理的方法內(nèi)開啟多線程,多線程內(nèi)的方法不被事務(wù)控制。一個使用了@Transactional 的方法,如果方法內(nèi)包含多線程的使用,方法內(nèi)部出現(xiàn)異常,不會回滾線程中調(diào)用方法的事務(wù)。
引用:
總結(jié)
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
- SpringBoot2整合JTA組件實(shí)現(xiàn)多數(shù)據(jù)源事務(wù)管理
- springboot-jta-atomikos多數(shù)據(jù)源事務(wù)管理實(shí)現(xiàn)
- SpringBoot2使用JTA組件實(shí)現(xiàn)基于JdbcTemplate多數(shù)據(jù)源事務(wù)管理(親測好用)
- java?SpringBoot?分布式事務(wù)的解決方案(JTA+Atomic+多數(shù)據(jù)源)
- SpringBoot整合Mybatis實(shí)現(xiàn)多數(shù)據(jù)源配置與跨數(shù)據(jù)源事務(wù)實(shí)例
- SpringBoot使用JTA實(shí)現(xiàn)對多數(shù)據(jù)源的事務(wù)管理
- SpringBoot 多數(shù)據(jù)源及事務(wù)解決方案小結(jié)
相關(guān)文章
MybatisPlus實(shí)現(xiàn)分頁效果并解決錯誤問題:cant?found?IPage?for?args
這篇文章主要介紹了MybatisPlus實(shí)現(xiàn)分頁效果并解決錯誤:cant?found?IPage?for?args,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-02-02Intellij IDEA下Spring Boot熱切換配置
這篇文章主要介紹了Intellij IDEA下Spring Boot熱切換配置,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08Springboot實(shí)現(xiàn)根據(jù)條件切換注入不同實(shí)現(xiàn)類的示例代碼
這篇文章主要介紹了Springboot實(shí)現(xiàn)根據(jù)條件切換注入不同實(shí)現(xiàn)類的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08Java實(shí)現(xiàn)學(xué)生成績管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)學(xué)生成績管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-04-04