多數(shù)據(jù)源下@transactional事務(wù)踩坑實戰(zhàn)指南
多數(shù)據(jù)源使用DynamicDatasource,再用@Transactiona注解,造成數(shù)據(jù)源會切換的BUG:
- 方法上寫了
@DS("slave") - 執(zhí)行 SQL 時卻仍然走了
master
情景浮現(xiàn):
@DS("master")
public class DB1Service{
public list<?> queryDB1()
}
@DS("slaver")
public class DB2Service{
public list<?> queryDB2()
}
public class Main{
@Transactional(rollbackFor = Exception.class)
public void sync(){
// 預(yù)期會走slaver庫,?? 但實際還是會查詢 master DB
var list2 = DB2Service.queryDB2()
// .....
var list1 = DB1Service.queryDB1()
}
}
1 為什么?
先說結(jié)論 :
在同一個本地事務(wù)里,第一次拿到的連接會綁定到當前線程。后續(xù)再切@DS,通常只會改路由標識,不會替換已經(jīng)綁定的連接。
調(diào)用 sync() 時,大致發(fā)生下面幾步:
- 進入
@Transactional代理,事務(wù)開始 - 事務(wù)管理器通過
DataSourceUtils.getConnection(...)拿連接 - 連接綁定到
TransactionSynchronizationManager(ThreadLocal) - 執(zhí)行業(yè)務(wù)代碼,進入
db2Service.queryDB2() @DS("slave")把當前線程的數(shù)據(jù)源 key 改成slave- 但取連接時發(fā)現(xiàn)線程里已經(jīng)有事務(wù)連接,于是直接復(fù)用
- SQL 繼續(xù)走原連接(常見是
master)
這里最重要的一點: @DS 決定的是 “下一次新建連接時選哪個數(shù)據(jù)源”,不是“把當前事務(wù)里的連接熱切換”。
如果連接已經(jīng)在事務(wù)里綁定好了,它不會換連接。
2 擴展:Dynamic-Datasource實現(xiàn)多數(shù)據(jù)源原理
啟動階段裝配
• DynamicDataSourceAutoConfiguration 注冊 DynamicRoutingDataSource 作為主 DataSource。
• 把你配置的 spring.datasource.dynamic.datasource.* 加載成數(shù)據(jù)源池,維護在內(nèi)部 dataSourceMap/groupDataSources 里。AOP 攔截 @DS
• DynamicDataSourceAopConfiguration 裝配 DynamicDataSourceAnnotationAdvisor + Interceptor。
• 方法進入時,DynamicDataSourceAnnotationInterceptor 解析 @DS 值(DataSourceClassResolver,支持方法/類、接口、繼承等)。
• 解析出的 key(如 master/doris)被 push 到
DynamicDataSourceContextHolder(ThreadLocal,棧結(jié)構(gòu),支持嵌套調(diào)用)。
• 方法退出后 poll,恢復(fù)上一個數(shù)據(jù)源上下文。真正路由發(fā)生在“取連接” • JdbcTemplate/MyBatis 最終都會走到當前主數(shù)據(jù)源取連接。
• 主數(shù)據(jù)源是 DynamicRoutingDataSource,它在 determineDataSource() 里 peek 當前線程 key。
• 然后 getDataSource(key):
• key 為空 -> 用 primary(默認 master)
• key 是具體庫名 -> 直連該庫
• key 是組名 -> 按策略(默認負載均衡)選組內(nèi)某個庫
• 未命中時:strict=true 拋異常,strict=false 回退 primary
總結(jié):
• @DS 只是改“路由上下文”
• DynamicRoutingDataSource 在取連接時按上下文選庫
• 不是直接“切換已拿到的連接”
到此這篇關(guān)于多數(shù)據(jù)源下@transactiona事務(wù)踩坑實戰(zhàn)指南的文章就介紹到這了,更多相關(guān)多數(shù)據(jù)源下@transactiona事務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot配置文件屬性變量引用方式${}和@@用法及區(qū)別說明
這篇文章主要介紹了springboot配置文件屬性變量引用方式${}和@@用法及區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
IDEA基于支付寶小程序搭建springboot項目的詳細步驟
這篇文章主要介紹了IDEA基于支付寶小程序搭建springboot項目的詳細步驟,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-04-04
Java使用BigDecimal確保數(shù)值計算精度的最佳實踐指南
這篇文章主要為大家詳細介紹了Java使用BigDecimal確保數(shù)值計算精度的相關(guān)知識,BigDecimal一般適用于金融計算、高精度運算等對數(shù)值準確性要求高的場景,下面小編就和大家詳細介紹一下吧2026-01-01
mybatis如何設(shè)置useGeneratedKeys=true
這篇文章主要介紹了mybatis如何設(shè)置useGeneratedKeys=true,具有很好的參考價值,希望對大家有所幫助。2022-01-01
SpringBoot 集成 MyBatis/MyBatis-Plus 超詳細教程
本文詳細介紹了SpringBoot集成MyBatis和MyBatis-Plus的完整流程,包括數(shù)據(jù)源配置、Mapper掃描、XML映射等,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2026-06-06
關(guān)于@RequestParam和@RequestBody的用法及說明
本文主要介紹了Spring框架中參數(shù)綁定相關(guān)的三個注解:@RequestParam、@ModelAttribute和@RequestBody的使用場景和區(qū)別,通過比較它們的使用場景和注意事項,幫助讀者快速理解并正確使用這些注解,避免在項目開發(fā)中出現(xiàn)常見問題2026-03-03

