Spring Boot:影響事務回滾的幾種情況

一、Controller 捕獲異常導致事務失效

需求

我們有一個用戶注冊服務,注冊時需要:

  1. 創建用戶賬戶
  2. 分配初始積分
  3. 發送注冊通知

這三個操作需要在同一個事務中執行,任何一步失敗都要回滾。

錯誤示例:Controller 捕獲異常導致事務失效

@RestController
@RequestMapping("/api/users")
public class UserController {@Autowiredprivate UserService userService;@PostMapping("/register")public ApiResponse registerUser(@RequestBody UserRegistrationRequest request) {try {// 調用服務層方法(帶有 @Transactional 注解)userService.registerUser(request);return ApiResponse.success();} catch (Exception e) {// 捕獲異常并返回自定義錯誤響應return ApiResponse.error("注冊失敗");}}
}@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserRepository userRepository;@Autowiredprivate PointRepository pointRepository;@Autowiredprivate NotificationService notificationService;@Override@Transactionalpublic void registerUser(UserRegistrationRequest request) {// 1. 創建用戶User user = new User();user.setUsername(request.getUsername());userRepository.save(user);// 2. 分配初始積分(模擬異常)if (request.getUsername().contains("test")) {throw new RuntimeException("測試異常");}Point point = new Point();point.setUserId(user.getId());point.setAmount(100);pointRepository.save(point);// 3. 發送注冊通知(實際項目中可能調用外部服務)notificationService.sendRegistrationNotification(user.getId());}
}

問題分析

  1. 事務注解registerUser 方法使用了 @Transactional,期望三個操作在同一事務中。
  2. 異常捕獲:Controller 捕獲了所有異常并返回自定義響應,導致事務管理器無法感知異常。
  3. 結果
    • request.getUsername() 包含 “test” 時,拋出異常。
    • Controller 捕獲異常并返回 ApiResponse.error(),但事務未回滾。
    • 數據庫結果:用戶記錄被創建,但積分未分配,導致數據不一致。

正確示例:讓異常自然拋出觸發回滾

@RestController
@RequestMapping("/api/users")
public class UserController {@Autowiredprivate UserService userService;@PostMapping("/register")public ApiResponse registerUser(@RequestBody UserRegistrationRequest request) {// 直接調用,不捕獲異常userService.registerUser(request);return ApiResponse.success();}
}@Service
public class UserServiceImpl implements UserService {@Override@Transactionalpublic void registerUser(UserRegistrationRequest request) {// 業務邏輯同上...// 任何異常都會導致事務回滾}
}// 全局異常處理器(統一處理異常)
@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(RuntimeException.class)@ResponseBodypublic ApiResponse handleRuntimeException(RuntimeException e) {return ApiResponse.error("系統錯誤:" + e.getMessage());}
}

關鍵區別

  1. 移除 try-catch:Controller 不再捕獲異常,讓異常自然傳播到事務管理器。
  2. 全局異常處理:通過 @ControllerAdvice 統一處理異常,返回自定義響應。
  3. 事務生效:當拋出異常時,事務管理器自動回滾所有操作。

另一種方案:手動管理事務

如果你堅持在 Controller 中處理異常,可以使用 TransactionTemplate

@Service
public class UserServiceImpl implements UserService {@Autowiredprivate TransactionTemplate transactionTemplate;@Overridepublic void registerUser(UserRegistrationRequest request) {transactionTemplate.execute(status -> {try {// 業務邏輯...return null;} catch (Exception e) {// 手動標記回滾status.setRollbackOnly();throw e;}});}
}

總結

  • 事務生效條件:異常必須傳播到代理方法外部。
  • Controller 捕獲異常:會導致事務管理器無法感知異常,從而不回滾。
  • 解決方案
    1. 讓異常自然拋出,通過全局異常處理器統一處理。
    2. 使用 TransactionTemplate 手動管理事務。

二、Feign 調用導致事務失效

Service 層使用了 Feign 調用,通常情況下是不能保證在同一個事務里的。

事務的基本原理和范圍

  • 本地事務機制:在傳統的單體應用中,像基于 Spring 的事務管理(使用 @Transactional 注解等方式),事務是依托于數據庫連接來實現的。例如,當一個方法被標記為 @Transactional 時,Spring 會在方法執行前開啟事務,獲取數據庫連接,在方法執行過程中如果出現異常就根據配置決定是否回滾事務,正常執行完則提交事務,整個過程都是圍繞著同一個數據庫連接進行操作,保證了一組數據庫操作的原子性等特性。

  • 事務傳播范圍:事務的范圍通常限定在一個本地的業務方法以及它所調用的其他同層級的本地方法內(前提是滿足事務傳播行為的相關規則),也就是在同一個應用的內部方法調用之間起作用。

Feign 調用的本質和特點

  • 遠程調用:Feign 是用于實現微服務之間的 HTTP 客戶端調用的工具,簡單來說,它是一種通過 HTTP 協議去調用其他微服務提供的接口的方式。例如,服務 A 通過 Feign 調用服務 B 的某個接口,本質上是向服務 B 發送了一個 HTTP 請求,這和在同一個應用內的方法調用有著本質區別。

  • 不同的運行環境和資源管理:被調用的服務(如服務 B)有自己獨立的運行環境、數據庫連接等資源管理機制。服務 A 所在的事務上下文沒辦法直接延伸到服務 B 那邊,因為它們是兩個獨立的微服務實例,各自管理著自己的事務。

示例說明無法保證同一事務

假設我們有兩個微服務,一個是 OrderService 微服務,另一個是 InventoryService 微服務。

  • OrderService 中的業務邏輯
@Service
@Transactional
public class OrderServiceImpl implements OrderService {@Autowiredprivate OrderRepository orderRepository;@Autowiredprivate FeignClient inventoryFeignClient;public void createOrder(Order order) {// 保存訂單到本地數據庫orderRepository.save(order);// 通過 Feign 調用 InventoryService 來扣減庫存inventoryFeignClient.reduceInventory(order.getProductId(), order.getQuantity());// 假設后續還有其他本地數據庫操作,比如記錄訂單日志等// orderLogRepository.save(...)}
}
  • InventoryService 中的業務邏輯(被調用方)
@Service
@Transactional
public class InventoryServiceImpl implements InventoryService {@Autowiredprivate InventoryRepository inventoryRepository;public void reduceInventory(Long productId, Integer quantity) {// 從本地數據庫扣減庫存Inventory inventory = inventoryRepository.findById(productId).orElseThrow(() -> new ResourceNotFoundException("庫存不存在"));inventory.setQuantity(inventory.getQuantity() - quantity);inventoryRepository.save(inventory);}
}

在上述例子中,OrderServiceImpl 中雖然整體方法標記了 @Transactional,但當它通過 Feign 調用 InventoryServiceImpl 中的 reduceInventory 方法時:

  • 即使 OrderServiceImpl 這邊在執行 orderRepository.save(order) 后出現異常,InventoryService 那邊已經接收到請求并執行了 inventoryRepository.save(inventory) 的話,是沒辦法自動回滾 InventoryService 里的操作的,因為這兩個服務的數據庫操作處于不同的事務環境中,各自管理自己的事務提交與回滾邏輯。

解決思路(實現分布式事務)

如果要在涉及 Feign 調用的多個微服務操作間保證事務的一致性,通常需要采用分布式事務的解決方案,常見的有以下幾種:

  • 基于消息隊列的最終一致性方案
    比如使用 RabbitMQ 或 Kafka 等消息隊列,在 OrderService 中保存訂單成功后,發送一個扣減庫存的消息到消息隊列,InventoryService 監聽這個消息并執行扣減庫存操作。兩邊通過消息的重試、補償等機制來保證最終數據的一致性,不過這種方式不是強事務一致性,而是最終一致性,即經過一段時間后,各個微服務的數據狀態會達到一致狀態。

  • 使用分布式事務框架
    像 Seata 這樣的分布式事務框架,它提供了多種分布式事務模式,例如 AT 模式(自動補償模式)、TCC 模式(補償事務模式)等。以 AT 模式為例,框架會在各個微服務的數據庫操作前后進行數據的快照、記錄相關的回滾日志等,當出現異常時,根據這些信息自動協調各個微服務回滾操作,從而保證多個微服務間事務的一致性。

所以,單純的 Feign 調用本身不能保證在同一個事務里,需要借助分布式事務相關的技術手段來實現跨微服務的事務一致性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/88095.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/88095.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/88095.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何避免分布式爬蟲被目標網站封禁?

在分布式爬蟲的大規模數據采集場景中,避免被目標網站封禁的核心邏輯是:通過技術手段模擬真實用戶行為,降低爬蟲行為的可識別性,同時建立動態適配機制應對網站反爬策略的升級。以下從請求偽裝、行為控制、資源管理、反爬對抗四個維…

Maven 打包排除特定依賴的完整指南(詳細方法 + 示例)

前言 在使用 Maven 構建 Java 項目時,我們常常需要對項目的打包過程進行精細化控制,尤其是希望排除某些特定的依賴庫。這可能是為了減小最終構建產物的體積、避免版本沖突,或者僅僅是為了滿足不同環境下的部署需求。 本文將詳細介紹如何在 Ma…

Terraform `for_each` 精講:優雅地自動化多域名證書驗證

大家好,在云原生和自動化運維的世界里,Terraform無疑是基礎設施即代碼(IaC)領域的王者。它強大的聲明式語法讓我們能夠輕松地描述和管理云資源。然而,即使是經驗豐富的工程師,在面對某些動態場景時也可能會…

C++標準庫中各種互斥鎖的用法 mutex

示例 僅供參考學習 #include <mutex> #include <shared_mutex> #include <thread> #include <chrono> #include <iostream> #include <vector>// // 1. std::mutex - 基本互斥鎖 // void basic_mutex_example() {std::mutex mtx;int cou…

Android Handler機制與底層原理詳解

Android 的 Handler 機制是跨線程通信和異步消息處理的核心框架&#xff0c;它構成了 Android 應用響應性和事件驅動模型的基礎&#xff08;如 UI 更新、后臺任務協調&#xff09;。其核心思想是 “消息隊列 循環處理”。 核心組件及其關系Handler (處理器): 角色: 消息的發送…

jQuery JSONP:實現跨域數據交互的利器

jQuery JSONP&#xff1a;實現跨域數據交互的利器 引言 隨著互聯網的發展&#xff0c;跨域數據交互的需求日益增加。在Web開發中&#xff0c;由于同源策略的限制&#xff0c;直接通過XMLHttpRequest請求跨域數據會遇到諸多問題。而JSONP&#xff08;JSON with Padding&#xff…

Redis集群和 zookeeper 實現分布式鎖的優勢和劣勢

在分布式系統中&#xff0c;實現分布式鎖是確保多個節點間互斥訪問共享資源的一種常見需求。Redis 集群 和 zookeeper 都可以用來實現這一功能&#xff0c;但它們有著各自不同的優勢和劣勢。 CAP 理論&#xff1a; 在設計一個分布式系統時&#xff0c;一致性&#xff08;Consis…

如何備份vivo手機中的聯系人?

隨著vivo移動設備在全球設立7個研發中心&#xff0c;vivo正在進入更多的國家。如今&#xff0c;越來越多的人開始使用vivo手機。以vivo X100為例&#xff0c;它配備了主攝像頭和多個輔助攝像頭&#xff0c;提供多樣化的拍攝選項&#xff0c;并搭載了最新的FunTouch OS&#xff…

python腳本編程:使用BeautifulSoup爬蟲庫獲取熱門單機游戲排行榜

BeautifulSoup是一個便捷的解析html頁面元素的python庫&#xff0c;此處用來寫一個簡單的爬蟲批量抓取國內游戲資訊網站的近期熱門單機游戲排行榜。 網頁來源如下所示代碼 from bs4 import BeautifulSoup import requests# get web page web_url "https://www.3dmgame.co…

C#配置全面詳解:從傳統方式到現代配置系統

C#配置全面詳解&#xff1a;從傳統方式到現代配置系統 在軟件開發中&#xff0c;配置是指應用程序運行時可調整的參數集合&#xff0c;如數據庫連接字符串、API 地址、日志級別等。將這些參數從代碼中分離出來&#xff0c;便于在不修改代碼的情況下調整應用行為。C# 提供了多種…

數據中臺架構解析:湖倉一體的實戰設計

目錄 一、數據中臺與湖倉一體架構是什么 1. 數據中臺 2. 湖倉一體架構 3. 湖倉一體在數據中臺里的價值 二、湖倉一體架構的核心部件 1. 數據湖 2. 數據倉庫 3. 數據集成工具 4. 數據分析與處理引擎 三、湖倉一體架構實戰設計 1. 需求分析與規劃 2. 數據湖建設 3. …

SQL Server表分區技術詳解

表分區概述 表分區是將大型數據庫表物理分割為多個較小單元的技術,邏輯上仍表現為單一實體。該技術通過水平分割數據顯著提升查詢性能,尤其針對TB級數據表可降低90%的響應時間。典型應用場景包含訂單歷史表、日志記錄表等具有明顯時間特征的業務數據,以及需要定期歸檔的審計…

WHIP(WebRTC HTTP Ingestion Protocol)詳解

WHIP&#xff08;WebRTC HTTP Ingestion Protocol&#xff09;詳解 WHIP&#xff08;WebRTC HTTP Ingestion Protocol&#xff09;是一種基于 HTTP 的協議&#xff0c;用于將 WebRTC 媒體流推送到媒體服務器&#xff08;如 SRS、Janus、LiveKit&#xff09;。它是為簡化 WebRT…

圖像噪點消除:用 OpenCV 實現多種濾波方法

在圖像處理中&#xff0c;噪點是一個常見的問題。它可能是由于圖像采集設備的缺陷、傳輸過程中的干擾&#xff0c;或者是光照條件不佳引起的。噪點會影響圖像的質量和后續處理的效果&#xff0c;因此消除噪點是圖像預處理的重要步驟之一。本文將介紹如何使用 OpenCV 實現幾種常…

AI的Prompt提示詞:英文寫好還是中文好?

在與AI人大模型交互時,Prompt(提示詞)的質量直接決定了輸出的精準度和有效性。一個常見的問題是:究竟是用英文寫Prompt好,還是用中文寫更好?這并非一個簡單的二元選擇,而是涉及到語言模型的底層邏輯、表達的精確性以及個人使用習慣的綜合考量。 英文Prompt的優勢 模型訓…

react的條件渲染【簡約風5min】

const flag1true; console.log(flag1&&hello); console.log(flag1||hello); const flag20; console.log(flag2&&hello); console.log(flag2||hello); // &&運算符&#xff0c;如果第一個條件為假&#xff0c;則返回第一個條件&#xff0c;否則返回第二…

【RK3568+PG2L50H開發板實驗例程】FPGA部分 | 紫光同創 IP core 的使用及添加

本原創文章由深圳市小眼睛科技有限公司創作&#xff0c;版權歸本公司所有&#xff0c;如需轉載&#xff0c;需授權并注明出處&#xff08;www.meyesemi.com)1.實驗簡介實驗目的&#xff1a;了解 PDS 軟件如何安裝 IP、使用 IP 以及查看 IP 手冊實驗環境&#xff1a;Window11 PD…

thinkphp微信小程序一鍵獲取手機號登陸(解密數據)

微信小程序獲取手機號登錄的步驟相對較為簡單,主要分為幾個部分: 1.用戶授權獲取手機號: 微信小程序通過調用 wx.getPhoneNumber API 獲取用戶授權后,獲取手機號。 2.前端獲取用戶的手機號: 用戶在小程序中點擊獲取手機號時,系統會彈出授權框,用戶同意后,你可以通過 …

數據庫設計精要:完整性和范式理論

文章目錄數據的完整性實體的完整性主鍵域完整性參照完整性外鍵多表設計/多表理論一對一和一對多多對多數據庫的設計范式第一范式&#xff1a;原子性第二范式&#xff1a;唯一性第三范式&#xff1a;不冗余性數據的完整性 實體的完整性 加主鍵&#xff0c;保證一個表中每一條數…

智能推薦社交分享小程序(websocket即時通訊、協同過濾算法、時間衰減因子模型、熱度得分算法)

&#x1f388;系統亮點&#xff1a;websocket即時通訊、協同過濾算法、時間衰減因子模型、熱度得分算法&#xff1b;一.系統開發工具與環境搭建1.系統設計開發工具后端使用Java編程語言的Spring boot框架項目架構&#xff1a;B/S架構運行環境&#xff1a;win10/win11、jdk17小程…