Spring/SpringBoot中的聲明式事務和編程式事務源碼、區別、優缺點、適用場景、實戰

来源:https://www.cnblogs.com/wang1221/archive/2023/11/03/17806836.html
-Advertisement-
Play Games

一、前言 在現代軟體開發中,事務處理是必不可少的一部分。當多個操作需要作為一個整體來執行時,事務可以確保數據的完整性和一致性,並避免出現異常和錯誤情況。在SpringBoot框架中,我們可以使用聲明式事務和編程式事務來管理事務處理。其中事務的坑也是不少,比較常見的就是事務失效,大家可以看看!後面小編 ...


一、前言

在現代軟體開發中,事務處理是必不可少的一部分。當多個操作需要作為一個整體來執行時,事務可以確保數據的完整性和一致性,並避免出現異常和錯誤情況。在SpringBoot框架中,我們可以使用聲明式事務和編程式事務來管理事務處理。其中事務的坑也是不少,比較常見的就是事務失效,大家可以看看!後面小編在出一篇事務失效場景哈,喜歡的可以關註,等待更新哈!

這篇博客將重點探討這兩種事務處理方式的源碼實現、區別、優缺點、適用場景以及實戰。
我們來接著說事務,裡面還涉及到三個知識點,大家可以自行百度好好瞭解!

  • 事務的特性
  • 事務的傳播行為
  • 隔離級別

本篇文章主要講的就是實現事務的兩種方式的分析!

讓我們開始探索聲明式事務和編程式事務吧!

文章很長,耐心看完希望對你有幫助!

本文源碼是使用:springboot2.7.1

二、開啟使用和大致源碼實現

1. 開啟使用

我們在啟動類上添加註解:@EnableTransactionManagement

後續使用就可以添加註解@Transactional(rollbackFor = Exception.class)使用,或者是使用編程式事務使用了 !
後面我們在詳細演示怎麼使用哈!

2. 聲明式事務源碼

public class TransactionInterceptor extends TransactionAspectSupport 
	implements MethodInterceptor, Serializable{}

TransactionInterceptor UML圖:
在這裡插入圖片描述

聲明式事務主要是通過AOP實現,主要包括以下幾個節點:

  1. 啟動時掃描@Transactional註解:在啟動時,Spring Boot會掃描所有使用了@Transactional註解的方法,並將其封裝成TransactionAnnotationParser對象。

  2. AOP 來實現事務管理的核心類依然是 TransactionInterceptor。TransactionInterceptor 是一個攔截器,用於攔截使用了 @Transactional 註解的方法

  3. 將TransactionInterceptor織入到目標方法中:在AOP編程中,使用AspectJ編寫切麵類,通過@Around註解將TransactionInterceptor織入到目標方法中

  4. 在目標方法執行前創建事務:在目標方法執行前,TransactionInterceptor會調用PlatformTransactionManager創建一個新的事務,並將其納入到當前線程的事務上下文中。

  5. 執行目標方法:在目標方法執行時,如果發生異常,則將事務狀態標記為ROLLBACK_ONLY;否則,將事務狀態標記為COMMIT

  6. 提交或回滾事務:在目標方法執行完成後,TransactionInterceptor會根據事務狀態(COMMIT或ROLLBACK_ONLY)來決定是否提交或回滾事務。

源碼:

@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
	// Work out the target class: may be {@code null}.
	// The TransactionAttributeSource should be passed the target class
	// as well as the method, which may be from an interface.
	Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

	// Adapt to TransactionAspectSupport's invokeWithinTransaction...
	return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
		@Override
		@Nullable
		public Object proceedWithInvocation() throws Throwable {
			return invocation.proceed();
		}
		@Override
		public Object getTarget() {
			return invocation.getThis();
		}
		@Override
		public Object[] getArguments() {
			return invocation.getArguments();
		}
	});
}

下麵是核心處理方法,把不太重要的代碼忽略了,留下每一步的節點。

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
	final InvocationCallback invocation) throws Throwable {
	// 獲取事務屬性
	final TransactionManager tm = determineTransactionManager(txAttr);
	// 準備事務
	TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
	// 執行目標方法
	Object retVal = invocation.proceedWithInvocation();
	 // 回滾事務
	completeTransactionAfterThrowing(txInfo, ex);
	// 提交事務
	commitTransactionAfterReturning(txInfo);
}		

3. 編程式事務源碼

編程式事務主要下麵的代碼:

public class TransactionTemplate extends DefaultTransactionDefinition
	implements TransactionOperations, InitializingBean{}

TransactionTemplate UML圖:

在這裡插入圖片描述

TransactionTemplate類的execute()方法封裝了事務的具體實現,通過調用TransactionCallback對象的doInTransaction()方法來執行業務邏輯並管理事務。在具體實現中,TransactionTemplate類會自動控制事務的提交和回滾,並將異常拋出給上層調用者進行處理。

@Override
@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
	Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");

	if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
		return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
	}
	else {
		TransactionStatus status = this.transactionManager.getTransaction(this);
		T result;
		try {
			result = action.doInTransaction(status);
		}
		catch (RuntimeException | Error ex) {
			// Transactional code threw application exception -> rollback
			rollbackOnException(status, ex);
			throw ex;
		}
		catch (Throwable ex) {
			// Transactional code threw unexpected exception -> rollback
			rollbackOnException(status, ex);
			throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
		}
		this.transactionManager.commit(status);
		return result;
	}
}

三、兩者區別

上面說了源碼里的大體實現,下麵我們來介紹一下兩者區別:

  1. 技術實現方式:聲明式事務是通過AOP技術來實現的,而編程式事務是通過編寫具體的代碼來實現的。
  2. 代碼耦合度:聲明式事務可以將事務處理邏輯從業務代碼中分離出來,從而降低代碼的耦合度。而編程式事務需要在業務代碼中顯式地調用事務管理代碼,因此會增加代碼的耦合度。
  3. 難易程度:聲明式事務相對來說比較容易上手,開發人員只需要學習註解或XML配置即可。而編程式事務需要開發人員理解事務管理的底層機制,並編寫具體的代碼。
  4. 性能影響:由於聲明式事務是由容器來處理的,所以在一些場景下可能會對性能產生影響,大事務會有很多問題(下麵在說一下大事務出現的問題)。而編程式事務由於直接調用事務管理API,相對來說會有更好的性能表現。

總體而言,聲明式事務和編程式事務都有各自的優缺點,開發人員需要根據具體需求選擇適合的方式來控制事務。

補充:

大事務時間過長可能會導致以下問題:
資料庫鎖定:當事務涉及到大量的數據操作時,事務可能會占用資料庫資源並長時間鎖定相關數據。這可能會導致其他事務無法訪問或修改這些數據,從而降低系統的併發性能和吞吐量。
資源耗盡:長時間運行的事務需要占用更多的系統資源,如記憶體和CPU等。如果系統資源不足,可能會導致系統出現延遲、死鎖等問題,甚至導致系統崩潰。
事務失敗概率增加:當事務時間過長時,事務執行期間可能會發生各種錯誤,如網路故障、硬體故障、操作系統問題等。此時,事務可能無法成功提交,導致數據丟失或數據不一致。
應用程式超時:應用程式通常會為每個事務設置一個超時時間,以避免事務持續時間過長。如果事務持續時間超過設定的超時時間,則應用程式可能會因為等待事務完成而阻塞,最終導致應用程式崩潰或超時。
回滾時間增加:如果事務失敗需要回滾,長時間運行的事務將需要更長的時間來進行回滾操作。這可能會導致數據不一致或丟失,並增加資料庫維護的工作量。
因此,開發人員應該儘量避免事務時間過長,合理地設置事務範圍、優化事務操作方式以及減少數據訪問次數等措施,以提高系統的併發性能和吞吐量。

方案:
大事務可以拆分小的事務,一下查詢方面的可以提取出來,操作資料庫的抽離出來專門加上事務。
也可以使用CompletableFuture組合式非同步編排來解決大事務的問題!!

四、優缺點

1. 聲明式事務

聲明式事務通常通過AOP技術實現,在方法或類級別上聲明事務屬性。
聲明式事務的優點包括:

簡化代碼:開發人員只需要關註業務邏輯,而無需手動管理事務,可以減少代碼複雜度和工作量。
可配置性強:事務屬性可以通過XML文件、註解等方式進行配置,靈活方便。
易於擴展:可以通過AOP技術輕鬆地擴展使其支持新的事務策略。

聲明式事務存在以下缺點:

限制較大:事務屬性需要在方法或類級別進行聲明,這可能會導致某些情況下難以滿足特定的業務需求。
難以調試:由於事務是在AOP層面進行管理的,因此在調試時可能難以追蹤事務管理的具體細節。

2. 編程式事務

編程式事務通常通過API介面實現,開發人員可以在代碼中顯式地管理事務。
編程式事務的優點包括:

靈活性強:開發人員可以在代碼中根據具體業務需要來控制事務的具體範圍和屬性。
易於調試:由於事務管理在代碼層面上實現,因此開發人員可以很容易地追蹤事務管理的細節。

編程式事務存在以下缺點:

代碼複雜度高:需要在代碼中手動處理事務,並處理各種異常情況,可能會增加代碼的複雜度和工作量。
可配置性差:事務的範圍和屬性需要在代碼中顯式聲明,這可能會導致一些特定的業務需求難以滿足。

總之,聲明式事務和編程式事務各有優缺點。開發人員需要根據具體業務需求和場景選擇使用合適的事務管理方式。

五、使用場景

聲明式事務通常適用於以下場景:

  • 大型企業級應用程式,需要管理多個事務。
  • 代碼結構比較複雜,使用聲明式事務可以更好地管理和維護代碼(大事務參考上方的方案)。
  • 聲明式事務可以將事務管理與業務邏輯分離,從而使得應用程式更加松耦合。

而編程式事務通常適用於以下場景:

  • 需要更精確地控制事務的範圍和處理邏輯。
  • 編程式事務通常比聲明式事務更加靈活,可以根據業務邏輯的需要來自定義事務的範圍、隔離級別以及回滾機制等。
  • 在某些高併發場景下,可以使用編程式事務僅針對需要操作的數據進行鎖定,而不是對整個業務邏輯加事務。

在實際場景中,可以根據需求綜合考慮使用聲明式事務和編程式事務的優勢來進行選擇。

根據不同的用戶量來具體選擇,在幾乎沒有併發量的系統設計一條非同步編排反而大材小用,可能造成資源的浪費;但是有需要等待遠程API的響應時,使用非同步編排可以將等待時間最小化,並使得應用程式不必阻塞等待API響應,從而提高用戶體驗。

很多事情沒有絕對化,只有相對化,只要能支持現有正常的使用,不管什麼樣的設計都是沒問題的!
可能好的設計會使系統在經受併發量增大的過程中無感,還是要調研清楚,從而設計出更好的方案,防止資源浪費!

儘管小編還沒有什麼架構經驗,但還是對架構充滿興趣,不想做架構師的開發不是好開發哈!!當然你也可以走管理!!

六、實戰

1. 聲明式事務

這裡就簡單模擬一下,為了模擬報錯,把OperIp設置為唯一!

@Transactional(rollbackFor = Exception.class)大家經常使用,就不多演示了!

@Transactional(rollbackFor = Exception.class)
@Override
public void template() {
    SysLog sysLog = new SysLog();
    sysLog.setOperIp("123");
    SysLog sysLog1 = new SysLog();
    sysLog1.setOperIp("hhh");
    log.info("插入第一條數據開始========");
    testMapper.insert(sysLog);
    log.info("插入第一條數據完成========");
    log.info("插入第二條數據開始========");
    testMapper.insert(sysLog);
    log.info("插入第二條數據完成========");

}

此時數據沒有數據,全部回滾成功!

在這裡插入圖片描述

2. 編程式事務

首先註入TransactionTemplate

@Autowired
private TransactionTemplate transactionTemplate;

後面直接使用即可:

@Override
public void template() {
    SysLog sysLog = new SysLog();
    sysLog.setOperIp("123");
    SysLog sysLog1 = new SysLog();
    sysLog1.setOperIp("hhh");
    log.info("插入第一條數據開始========");
    testMapper.insert(sysLog);
    log.info("插入第一條數據完成========");

    transactionTemplate.execute(status -> {
        log.info("編程式事務中:插入第一條數據開始========");
        testMapper.insert(sysLog1);
        log.info("編程式事務中:插入第一條數據完成========");
        log.info("編程式事務中:插入第二條數據開始========");
        int insert = testMapper.insert(sysLog);
        log.info("編程式事務中:插入第二條數據完成========");
        return insert;
    });
}

在這裡插入圖片描述
查看資料庫,第一條不在編程式事務內不會參與回滾!
在這裡插入圖片描述

七、總結

本文介紹了SpringBoot框架中的聲明式事務和編程式事務,並分析了它們的源碼實現、區別、優缺點、適用場景以及實戰。
無論是採用哪種方式來管理事務,都需要考慮到業務需求和開發團隊的實際情況,選擇合適的事務處理方式,以確保系統的可靠性和穩定性。

希望通過本文的介紹,你能夠更好地理解聲明式事務和編程式事務的概念和原理,在開發過程中選擇合適的事務處理方式,提高項目的可維護性和穩定性。


如果對你有幫助,還請動一下您的發財小手,關註一下公眾號哈!!謝謝您的關註!!文章首發看!!!

建了一個IT交流群,歡迎大家加入,過期加我拉你們進哈!

在這裡插入圖片描述


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 歸併排序和快速排序一樣,都是基於分治思想的應用。 通過遞歸,不斷將原數列分為兩個數列,然後再分別使其有序,最後通過歸併將兩個有序子數列合併為新的有序數列。 ...
  • 早在Java7的時候就被提出,但由於其複雜性,不斷跳票,直到Java9才有,那麼Java模塊化到底是什麼,在實際開發中又有什麼用呢? ...
  • 之前對接支付寶商家扣款的時候,在簽約協議的部分卡了很久,今天把之前遇到的簽約問題彙總記錄一下~ 協議簽約流程 首先幫大家捋一下簽約的順序,便於直觀理解: 其次還需要知道的是,支付寶的商家扣款的簽約介面有兩個: 一個是單獨簽約介面: 另一個是支付並簽約介面: 這兩個介面都可以簽約,主要區別在於簽約的時 ...
  • HashMap簡介 HashMap是Java語言中的一種集合類,它實現了Map介面,用於存儲Key-Value對。它基於哈希表數據結構,通過計算Key的哈希值來快速定位Value的位置,從而實現高效的插入、刪除和查找操作。下麵我們對照著JAVA1.8中的HashMap源碼來分析一下它的內部實現邏輯 ...
  • 在Go編程語言中處理數據時,經常會遇到數組和切片。這兩者是不同的數據結構,有各自的特性和用途。本文將對Go中的數組和切片進行比較,以幫助大家更好地理解它們。 1. 長度不同 一個主要的區別是長度。在Go中,數組是具有固定長度的數據結構,一旦創建,其大小不可更改。相比之下,切片具有動態大小,可以在運行 ...
  • 集合類不安全 List不安全 單線程情況下集合類和很多其他的類都是安全的,因為同一時間只有一個線程在對他們進行修改,但是如果是多線程情況下,那麼集合類就不一定是安全的,可能會出現一條線程正在修改的同時另一條線程啟動來對這個集合進行修改,這種情況下就會導致發生併發修改異常(在jdk11的環境下多次測試 ...
  • Spring Boot 是一種廣泛使用且非常流行的企業級高性能框架。以下是一些最佳實踐和一些技巧,我們可以使用它們來改進 Spring Boot 應用程式並使其更加高效。這篇文章會有點長,完整讀完文章需要一些時間。 1.正確的包目錄風格 正確的包目錄將有助於輕鬆理解代碼和應用程式的流程。 我們可以使 ...
  • 傅里葉變換是一種數學變換,它可以將一個函數或信號轉換為另一個函數或信號,它可以將時域信號轉換為頻域信號,也可以將頻域信號轉換為時域信號。在很多的領域都有廣泛的應用,例如信號處理、通信、圖像處理、電腦科學、物理學、生物學等。 它最大的功能是能夠分析和提取信號的特征,將複雜的信號分解為簡單的信號。有人 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...