spring中@Transactional注解和事務的實戰理解附代碼

文章目錄

  • 前言
  • 一、事務是什么?
  • 二、事務的特性
    • 2.1隔離性
    • 2.2事務的隔離級別
  • 三、@Transactional注解
      • @Transactional注解簡介
      • 基本用法
      • 常用屬性配置
      • 事務傳播行為
      • 事務隔離級別
      • 異常處理與回滾
      • 性能優化建議
  • 四、 事務不生效的可能原因
      • 方法訪問權限非public
      • 自調用問題
      • 異常被捕獲未拋出
      • 數據庫引擎不支持事務
      • 未啟用事務管理
      • 特殊場景:final/static方法
  • 五、分布式事務考慮
  • 總結


前言

在開發過程中,遇到多個數據庫操作的時候往往只知道加上@transactional注解(spring框架)但是沒有系統學習數據庫的事務知識以及各種用法,本文會舉例介紹數據庫中事務的概念以及用法


一、事務是什么?

在實際的項目開發過程中會涉及很多不可分割的數據庫操作,如轉賬業務的進賬和出帳,要么全部執行成功,要么全部失敗回滾。這樣一些的對數據庫操作的集合就叫事務。現在假設數據庫中有一個表accounts

balanceuser_id
10001
1002

在數據庫中執行一段事務的sql如下:

-- 顯式事務示例
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2;
COMMIT; -- 或 ROLLBACK 回滾

很多人會覺得這里的COMMIT或者ROLLBACK之前,數據庫操作只是在內存中,并沒有更改表中的數據,需要注意的是,這里的COMMIT或者ROLLBACK 之前,數據已經是持久化了,也就是寫入了磁盤(數據庫表中)。

二、事務的特性

根據對事務的理解,可以歸納出事務應該具有的特性
原子性(Atomicity):事務被視為不可分割的最小單元,要么全部提交成功,要么全部失敗回滾。
一致性(Consistency):事務執行前后,數據庫從一個一致狀態轉變為另一個一致狀態。
隔離性(Isolation):多個事務并發執行時,一個事務的操作不應影響其他事務。
持久性(Durability):事務提交后,其對數據的修改是永久性的。

2.1隔離性

在上述特性中比較難理解的應該是隔離性,可以想象這樣一個場景:
因為網絡延遲或者條件異常,事務A未提交還沒來得及回滾

-- 事務A
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2;

事務B開始查詢事務,查詢的結果就是balance = balance - 100

-- 事務B
BEGIN TRANSACTION;
select balance WHERE user_id = 1 from accounts;
commit;

事務A回滾,此刻a,b兩個事務間就發生了干擾,a的事務的提交影響了b事務對數據庫的正常讀取。

-- 事務A
ROLLBACK ;--回滾

所以隔離性就是指的的是a事務對與b事務的互不影響的程度。

2.2事務的隔離級別

事務的隔離級別也可以叫做事務的互不影響程度的級別。
讀未提交(Read Uncommitted):影響程度最高,即使事務沒有commit,另外的事務也可以讀到,可能導致臟讀。

-- ----------------------
-- 窗口1(事務A)
-- ----------------------
-- 設置隔離級別為讀未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;-- 修改A賬戶余額(未提交)
UPDATE account SET balance = 800 WHERE name = 'A賬戶';
SELECT * FROM account;  -- 查看修改后結果:A賬戶800-- ----------------------
-- 窗口2(事務B)
-- ----------------------
-- 設置隔離級別為讀未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;-- 查詢A賬戶余額(讀到未提交的數據)
SELECT * FROM account;  -- 結果:A賬戶800(臟讀)-- 窗口1(事務A)回滾
ROLLBACK;-- 窗口2再次查詢
SELECT * FROM account;  -- 結果:A賬戶恢復1000(臟讀數據消失)

讀已提交(Read Committed):只能讀取已提交的數據,避免臟讀但可能出現不可重復讀(一個事務內的多次查詢結果不一致)。即b開啟事務
進行第一次查詢,此時a事務開啟事務,a更改完數據后,事務提交,b開啟第二次查詢,查詢結果不一致。

-- ----------------------
-- 窗口1(事務A)
-- ----------------------
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION; --設置讀已提交的隔離級別-- 修改A賬戶余額并提交
UPDATE account SET balance = 800 WHERE name = 'A賬戶';
COMMIT;  -- 提交事務-- ----------------------
-- 窗口2(事務B)
-- ----------------------
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;-- 第一次查詢(事務A未提交時)
SELECT * FROM account;  -- 結果:A賬戶1000-- 此時事務A已提交...-- 第二次查詢(事務A已提交)
SELECT * FROM account;  -- 結果:A賬戶800(不可重復讀)ROLLBACK;

可重復讀(Repeatable Read):確保同一事務多次讀取結果一致(相當于事務開始時候進行了一次數據庫快照,事務內的查詢的是查詢開啟事務時刻的數據庫快照的數據),避免不可重復讀但可能出現幻讀(同一事務內多次查詢返回不同行數)。

-- ----------------------
-- 窗口1(事務A)
-- ----------------------
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;-- 修改A賬戶余額并提交
UPDATE account SET balance = 800 WHERE name = 'A賬戶';
COMMIT;-- ----------------------
-- 窗口2(事務B)
-- ----------------------
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;-- 第一次查詢
SELECT * FROM account;  -- 結果:A賬戶1000-- 此時事務A已提交...-- 第二次查詢(結果與第一次一致)
SELECT * FROM account;  -- 結果:A賬戶1000(可重復讀)-- 提交事務B后,才能看到A的修改
COMMIT;
SELECT * FROM account;  -- 結果:A賬戶800

這里需要強調下為什么會出現幻讀同一事務內多次查詢返回不同行數)。
雖然不可重復讀讀的是快照讀,但是使用update/insert/delete等時,是會先當前讀,也就是說的是已提交的數據,此刻的快照讀會更新為當前使用update/insert/delete等時數據庫的快照,所以再之后的普通讀select中的快照和最開始的快照的版本是不一樣的了,讀的數據也就不一樣了。幻讀例子如下:

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;-- 插入新記錄
INSERT INTO account(name, balance) VALUES ('C賬戶', 500);
COMMIT;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;-- 第一次查詢(返回2條記錄)
SELECT COUNT(*) FROM account;  -- 此時事務A插入新記錄并提交...-- 第二次查詢(仍返回2條記錄)
SELECT COUNT(*) FROM account;  -- 執行更新操作后再次查詢(出現幻讀)
UPDATE account SET balance = balance+100 WHERE name = 'A賬戶';
SELECT COUNT(*) FROM account;  -- 返回3條記錄
COMMIT;

串行化(Serializable):最高隔離級別,完全禁止并發問題,所有事務完全同步性能最低(按照一定的順序執行)。事務A插入數據,事務B在查詢時被阻塞,避免幻讀。

-- 事務A
-- ----------------------
-- 窗口1(事務A)
-- ----------------------
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;-- 查詢當前數據
SELECT * FROM account;  -- 結果:A賬戶1000,B賬戶1000-- ----------------------
-- 窗口2(事務B)
-- ----------------------
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;-- 插入新數據(會被事務A阻塞,直到A提交或回滾)
INSERT INTO account (name, balance) VALUES ('C賬戶', 1000);-- 窗口1提交事務
COMMIT;-- 窗口2的插入操作繼續執行(此時事務A已提交)
-- 提交后,窗口1再次查詢會看到新數據(幻讀解決)

三、@Transactional注解

@Transactional注解簡介

@Transactional是Spring框架提供的聲明式事務管理注解,用于簡化數據庫事務操作。通過在方法或類上添加該注解,可以自動管理事務的開啟、提交、回滾等操作,無需手動編寫事務代碼。

基本用法

在Spring Boot項目中,需要在啟動類或配置類上添加@EnableTransactionManagement以啟用事務管理功能。隨后可以在服務層方法上使用@Transactional注解:

@Service
public class UserService {@Autowiredprivate UserRepository userRepository;@Transactionalpublic void createUser(User user) {userRepository.save(user);}
}

常用屬性配置

@Transactional提供多個屬性用于定制事務行為:

#使用注解時候默認配置如下
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout = -1,readOnly = false,rollbackFor = {RuntimeException.class, Error.class}
)

自定義注解配置

@Transactional(propagation = Propagation.REQUIRED, --事務傳播行為isolation = Isolation.DEFAULT, --隔離級別,數據庫默認不可重讀timeout = 30, --定義超時時間,超過自動給回滾readOnly = false, --不只讀,可修改rollbackFor = {SQLException.class}, --遇到異常捕獲后必須回滾noRollbackFor = {NullPointerException.class} --遇到NullPointerException異常時不回滾事務
)
public void updateUser(User user) {// 業務邏輯
}

事務傳播行為

Spring定義了7種事務傳播行為,常用選項包括:

  • REQUIRED:默認值,當前有事務則加入,沒有則新建
  • REQUIRES_NEW:總是新建事務,暫停當前事務
  • NESTED:在當前事務中嵌套子事務
  • SUPPORTS:有事務則加入,沒有則以非事務方式執行
  • NOT_SUPPORTED:以非事務方式執行,暫停當前事務
  • MANDATORY:必須在事務中調用,否則拋出異常
  • NEVER:不能在事務中調用,否則拋出異常

事務隔離級別

隔離級別控制事務間的可見性:

  • DEFAULT:使用數據庫默認級別
  • READ_UNCOMMITTED:讀未提交
  • READ_COMMITTED:讀已提交
  • REPEATABLE_READ:可重復讀
  • SERIALIZABLE:串行化

異常處理與回滾

默認只在運行時異常和Error時回滾,可通過以下屬性調整:

  • rollbackFor:指定觸發回滾的異常類型
  • noRollbackFor:指定不觸發回滾的異常類型
  • rollbackForClassName:通過類名指定回滾異常
  • noRollbackForClassName:通過類名指定不回滾異常

性能優化建議

對于只讀操作,建議明確設置readOnly=true

@Transactional(readOnly = true)
public User getUser(Long id) {return userRepository.findById(id);
}

避免在事務方法中進行遠程調用或耗時操作,防止事務長時間占用連接。

四、 事務不生效的可能原因

在很多面試題會問事務不生效的原因, @Transactional 是基于Spring AOP實現的事務切面,所以失效本質上可以歸因于AOP代理未生效或切面邏輯被阻斷。Spring通過 @EnableTransactionManagement 開啟事務支持,底層使用 TransactionInterceptor 攔截目標方法,生成代理對象(JDK動態代理或CGLIB代理)。只有通過代理對象調用方法時,事務切面才會生效;若直接調用目標對象(如 this.方法() ),會繞過代理,導致事務失效。

方法訪問權限非public

Spring事務代理要求目標方法必須是public,若定義為private/protected/default,代理無法攔截方法調用。

@Transactional  // 失效
private void saveData() {// 操作數據庫
}

自調用問題

同類中非事務方法調用事務方法,因直接調用this而非代理對象,導致攔截失效。

public class OrderService {public void createOrder() {this.saveOrder();  // 自調用,事務失效}@Transactionalpublic void saveOrder() {// 保存訂單}
}

異常被捕獲未拋出

spring默認僅對RuntimeException和Error回滾,若捕獲異常且未重新拋出,事務管理器無法感知異常。示例:

@Transactional
public void update() {try {jdbcTemplate.update("...");} catch (DataAccessException e) {// 捕獲后未拋出,事務不會回滾}
}

通過@Transactional注解指定需回滾的異常:

@Transactional(rollbackFor = IOException.class)

將受檢異常轉換為事務管理器可識別的類型:

catch (IOException e) {throw new RuntimeException("Wrapped exception", e);
}

數據庫引擎不支持事務

如MySQL的MyISAM引擎不支持事務,需改用InnoDB。配置示例:

CREATE TABLE test (id INT PRIMARY KEY
) ENGINE=InnoDB;  // 必須為InnoDB

未啟用事務管理

Spring Boot需配置@EnableTransactionManagement(默認自動開啟),傳統項目遺漏此注解會導致事務失效。示例缺失:

@SpringBootApplication
// 若手動配置需添加@EnableTransactionManagement
public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}
}

特殊場景:final/static方法

代理無法增強final/static方法,導致事務失效。示例:

@Transactional
public final void process() {  // final方法失效// 業務邏輯
}

五、分布式事務考慮

對于跨服務的事務操作,@Transactional只能管理本地事務。分布式場景可考慮:

  1. Seata框架
  2. 消息隊列最終一致性
  3. TCC模式
  4. SAGA模式

總結

在開發過程中遇到過在同一條件下查詢出不同的數據結果,最后發現是對事務的理解使用欠缺導致。這個問題在開發過程中通過還挺常見的,不僅僅是在面對事務的時候加個@Transactional注解就完事了。
本文梳理了數據庫事務的基本概念和隔離級別,同時對spring中的事務框架做了簡單的介紹,覺得有用請點下贊吧。

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

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

相關文章

替代進口SCA7606【智芯微】國產高精度電流傳感器 工業新能源電網專用

SCA7606(智芯微)產品解析與推廣文案一、產品概述SCA7606 是 智芯微電子(ZXMICRO) 推出的一款 高精度數字隔離式電流傳感器芯片,采用 霍爾效應數字輸出 技術,專為 工業控制、新能源、智能電網 等領域的電流檢…

Java 與 Vue 全棧開發:“一課一得“ 學習筆記系統實戰

一、項目背景與核心價值 "一課一得" 是一個面向學習者的筆記管理平臺,旨在幫助用戶系統化記錄、整理和回顧學習內容。項目采用前后端分離架構:前端基于 Vue.js 構建交互式界面,后端使用 Java Spring Boot 實現業務邏輯&#xff0c…

百度文心大模型 4.5 開源深度測評:技術架構、部署實戰與生態協同全解析

聲明:本文只做實際測評,并非廣告 1.前言 2025 年 6 月 30 日,百度做出一項重大舉措,將文心大模型 4.5 系列正式開源,并選擇國內領先的開源平臺 GitCode 作為首發平臺。該模型也是百度在2025年3月16日發布的自研的新一…

力扣_鏈表_python版本

一、206. 反轉鏈表代碼: class Solution:def reverseList(self, head):dummy ListNode()cur headwhile cur:last cur.nextcur.next dummy.nextdummy.next curcur lastreturn dummy.next二、92. 反轉鏈表 IIclass Solution:def reverseBetween(self, head: Opt…

[netty5: WebSocketProtocolHandler]-源碼分析

在閱讀這篇文章前,推薦先閱讀:[netty5: MessageToMessageCodec & MessageToMessageEncoder & MessageToMessageDecoder]-源碼分析 WebSocketProtocolHandler WebSocketProtocolHandler 是 WebSocket 處理的基礎抽象類,負責管理 Web…

[2025CVPR]一種新穎的視覺與記憶雙適配器(Visual and Memory Dual Adapter, VMDA)

引言 多模態目標跟蹤(Multi-modal Object Tracking)旨在通過結合RGB模態與其他輔助模態(如熱紅外、深度、事件數據)來增強可見光傳感器的感知能力,尤其在復雜場景下顯著提升跟蹤魯棒性。然而,現有方法在頻…

理想汽車6月交付36279輛 第二季度共交付111074輛

理想汽車-W(02015)發布公告,2025年6月,理想汽車交付新車36279輛,第二季度共交付111074輛。截至2025年6月30日,理想汽車歷史累計交付量為133.78萬輛。 在成立十周年之際,理想汽車已連續兩年成為人民幣20萬元以上中高端市…

MobileNets: 高效的卷積神經網絡用于移動視覺應用

摘要 我們提出了一類高效的模型,稱為MobileNets,專門用于移動和嵌入式視覺應用。MobileNets基于一種簡化的架構,利用深度可分離卷積構建輕量級的深度神經網絡。我們引入了兩個簡單的全局超參數,能夠有效地在延遲和準確性之間進行…

SDP服務發現協議:動態查詢設備能力的底層邏輯(面試深度解析)

SDP的底層邏輯揭示了物聯網設備交互的本質——先建立認知,再開展協作。 一、SDP 核心知識點高頻考點解析 1.1 SDP 的定位與作用 考點:SDP 在藍牙協議棧中的位置及核心功能 解析:SDP(Service Discovery Protocol,服務發現協議)位于藍牙協議棧的中間層,依賴 L2CAP 協議傳…

CppCon 2018 學習:GIT, CMAKE, CONAN

提到的: “THE MOST COMMON C TOOLSET” VERSION CONTROL SYSTEM BUILDING PACKAGE MANAGEMENT 這些是 C 項目開發中最核心的工具鏈組成部分。下面我將逐一解釋每部分的作用、常見工具,以及它們如何協同構建現代 C 項目。 1. VERSION CONTROL SYSTEM&am…

使用tensorflow的線性回歸的例子(五)

我們使用Iris數據,Sepal length為y值而Petal width為x值。import matplotlib.pyplot as pltimport numpy as npimport tensorflow as tffrom sklearn import datasetsfrom tensorflow.python.framework import opsops.reset_default_graph()# Load the data# iris.d…

虛幻基礎:動作——蒙太奇

能幫到你的話,就給個贊吧 😘 文章目錄 動作——蒙太奇如果動作被打斷,則后續的動畫通知不會執行 動作——蒙太奇 如果動作被打斷,則后續的動畫通知不會執行

[工具系列] 開源的 API 調試工具 Postwoman

介紹 隨著 Web 應用的復雜性增加,API 測試已成為開發中不可或缺的一部分,無論是前端還是后端開發,確保 API 正常運行至關重要。 Postman 長期以來是開發者進行 API 測試的首選工具,但是很多基本功能都需要登陸才能使用&#xff…

【力扣 簡單 C】746. 使用最小花費爬樓梯

目錄 題目 解法一 題目 解法一 int min(int a, int b) {return a < b ? a : b; }int minCostClimbingStairs(int* cost, int costSize) {const int n costSize; // 樓頂&#xff0c;第n階// 爬到第n階的最小花費 // 爬到第n-1階的最小花費從第n-1階爬上第n階的花費…

python+django開發帶auth接口

pythondjango開發帶auth接口 # coding utf-8 import base64 from django.contrib import auth as django_authfrom django.core.exceptions import ObjectDoesNotExist from django.http import JsonResponsefrom sign.models import Eventdef user_auth(request):"&quo…

RBAC權限模型如何讓API訪問控制既安全又靈活?

url: /posts/9f01e838545ae8d34016c759ef461423/ title: RBAC權限模型如何讓API訪問控制既安全又靈活? date: 2025-07-01T04:52:07+08:00 lastmod: 2025-07-01T04:52:07+08:00 author: cmdragon summary: RBAC權限模型通過用戶、角色和權限的關聯實現訪問控制,核心組件包括用…

安達發|告別低效排產:APS高級排程如何助力電池企業智造升級?

在全球能源轉型的背景下&#xff0c;動力電池、儲能電池等市場需求快速增長&#xff0c;電池制造企業面臨著訂單波動大、工藝復雜、交期嚴格等挑戰。傳統的手工排產或基于ERP的簡單計劃模式已難以滿足高效、精準的生產需求。APS高級排程通過智能算法優化生產計劃&#xff0c;實…

數據結構20250620_數據結構考試

試卷01 天津金海通軟件筆試題 選擇題(4*416) 對于雙向循環鏈表,在p指針所指的結點之后插入s指針所指結點的操作應為 p->nexts; s->prip; p->next->pris; s->nextp->nextp->nexts; p->next->pris; s->prip; s->nextp->nexts->pri …

4. 尋找正序數組的中位數

題目&#xff1a; 給定兩個大小分別為 m 和 n 的正序&#xff08;從小到大&#xff09;數組 nums1 和 nums2。請你找出并返回這兩個正序數組的 中位數 。 算法的時間復雜度應該為 O(log (mn)) 。 示例&#xff1a; 輸入&#xff1a;nums1 [1,3], nums2 [2] 輸出&#xff1a…

DeepSeek飛機大戰小游戲HTML5(附源碼)

用DeepSeek幫忙生成的飛機大戰小游戲網頁版&#xff0c;基于HTML5。 提示詞prompt 幫我做一個網頁版的飛機大戰游戲 html5的游戲功能說明 玩家控制&#xff1a; 使用鍵盤方向鍵或WASD移動飛機 空格鍵發射子彈 移動設備支持觸摸控制 游戲機制&#xff1a; 敵機會從屏幕頂部隨機位…