如何合理使用Spring的事務(wù)方式
Spring Framework 提供了全面的事務(wù)管理功能,以確保在應(yīng)用程序中操作的原子性、一致性、隔離性和持久性(即 ACID 特性)。
事務(wù)管理是重要的,尤其是在涉及到數(shù)據(jù)庫操作時,以確保數(shù)據(jù)的完整性和一致性。
1、介紹
1.1、底層構(gòu)造
Spring事務(wù)管理高層抽象主要包括3個接口,Spring的事務(wù)主要是由他們共同完成的:
PlatformTransactionManager:事務(wù)管理器—主要用于平臺相關(guān)事務(wù)的管理TransactionDefinition:事務(wù)定義信息(隔離、傳播、超時、只讀)—通過配置如何進行事務(wù)管理。TransactionStatus:事務(wù)具體運行狀態(tài)—事務(wù)管理過程中,每個時間點事務(wù)的狀態(tài)信息。
1.1.事務(wù)管理器

PlatformTransactionManager該接口提供三個方法:
commit:提交事務(wù)rollback:回滾事務(wù)getTransaction:獲取事務(wù)狀態(tài)
1.2.事務(wù)定義信息

TransactionDefinition該接口主要提供的方法:
getIsolationLevel:隔離級別獲取getPropagationBehavior:傳播行為獲取getTimeout:獲取超時時間isReadOnly是否只讀(保存、更新、刪除—對數(shù)據(jù)進行操作-變成可讀寫的,查詢-設(shè)置這個屬性為true,只能讀不能寫)
1.3.事務(wù)狀態(tài)
TransactionStatus代表獲取事務(wù)的狀態(tài)。
try {
操作
} catch (){
rollback
} finally {
commit
}1.4.聯(lián)系
用戶管理事務(wù),先配置事務(wù)管理方案TransactionDefinition、管理事務(wù)通過TransactionManager完成,TransactionManager根據(jù) TransactionDefinition進行事務(wù)管理,
在事務(wù)運行過程中,每個時間點都可以通過獲取TransactionStatus了解事務(wù)運行狀態(tài)!
1.2、特點
定義
- 事務(wù)是一個操作序列,這些操作要么全部成功,要么全部失敗。無法將部分操作視為成功,其他部分則視為失敗。因此,事務(wù)是一種確保數(shù)據(jù)一致性和完整性的重要手段。
ACID 特性
- 原子性(Atomicity): 事務(wù)內(nèi)的所有操作要么全部成功,要么全部失敗。
- 一致性(Consistency): 事務(wù)必須使數(shù)據(jù)庫從一個一致的狀態(tài)轉(zhuǎn)變到另一個一致的狀態(tài)。
- 隔離性(Isolation): 一個事務(wù)的執(zhí)行不能被其他事務(wù)干擾,確保并發(fā)執(zhí)行下的每個事務(wù)都是在其自己的上下文中執(zhí)行。
- 持久性(Durability): 事務(wù)一旦提交,其結(jié)果便永久保存,即使系統(tǒng)故障也不會丟失。
1.3、原理
常見面試題:事務(wù)的原理
Spring 事務(wù)的底層實現(xiàn)主要使用的技術(shù):AOP(動態(tài)代理) + ThreadLocal + try/catch。
1.動態(tài)代理AOP:
基本所有要進行邏輯增強的地方都會用到動態(tài)代理,AOP 底層也是通過動態(tài)代理實現(xiàn)。
2.事務(wù)同步管理器(TransactionSynchronizationManager)
Spring使用線程局部變量(ThreadLocal)來管理事務(wù)資源:
- ThreadLocal:主要用于線程間的資源隔離,以此實現(xiàn)不同線程可以使用不同的數(shù)據(jù)源、隔離級別等等。
public abstract class TransactionSynchronizationManager {
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<>("Current transaction name");
private static final ThreadLocal<Boolean> currentTransactionReadOnly =
new NamedThreadLocal<>("Current transaction read-only status");
private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
new NamedThreadLocal<>("Current transaction isolation level");
private static final ThreadLocal<Boolean> actualTransactionActive =
new NamedThreadLocal<>("Actual transaction active");
}3.try/catch:
最終是執(zhí)行 commit 還是 rollback,是根據(jù)業(yè)務(wù)邏輯處理是否拋出異常來決定。
Spring 事務(wù)的核心邏輯偽代碼如下:
public void invokeWithinTransaction() {
// 1.事務(wù)資源準備
try {
// 2.業(yè)務(wù)邏輯處理,也就是調(diào)用被代理的方法
} catch (Exception e) {
// 3.出現(xiàn)異常,進行回滾并將異常拋出
} finally {
// 現(xiàn)場還原:還原舊的事務(wù)信息
}
// 4.正常執(zhí)行,進行事務(wù)的提交
// 返回業(yè)務(wù)邏輯處理結(jié)果
}4.流程
@Transactional方法調(diào)用 │ ↓ TransactionInterceptor攔截 │ ↓ 獲取事務(wù)屬性(@Transactional配置) │ ↓ 根據(jù)傳播行為決定是否創(chuàng)建新事務(wù) │ ↓ TransactionSynchronizationManager綁定資源到當前線程 │ ↓ 執(zhí)行業(yè)務(wù)方法 │ ┌─────────┴──────────┐ ↓ ↓ 方法成功 方法拋出異常 │ │ ↓ ↓ 提交事務(wù) 判斷是否需要回滾 │ │ ↓ ↓ 清理線程資源 回滾事務(wù) │ │ ↓ ↓ 返回結(jié)果 拋出異常
2. Spring 的事務(wù)管理
Spring 提供了強大的事務(wù)管理功能,包括編程式和聲明式事務(wù)管理。
1、聲明式事務(wù)管理
(xml和注解形式)
特點
- 易于使用:只需在方法上添加注解或在配置文件中定義事務(wù)規(guī)則即可啟用事務(wù)管理。
- 非侵入性:事務(wù)管理邏輯與業(yè)務(wù)邏輯分離,不會干擾業(yè)務(wù)邏輯代碼。
- 可配置性:可以靈活地配置事務(wù)的傳播行為、隔離級別、超時和回滾規(guī)則等。
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Transactional // 默認傳播行為為 REQUIRED
public void createUser(String username) {
System.out.println("User " + username + " created.");
// 調(diào)用另一個方法
addUser(username);
}
@Transactional(propagation = Propagation.REQUIRES_NEW) // 新事務(wù)
public void addUser(String username) {
System.out.println("User " + username + " added in a new transaction.");
}
@Transactional(propagation = Propagation.NESTED) // 嵌套事務(wù)
public void updateUser(String username) {
System.out.println("Updating user: " + username);
}
}
輸出:
在調(diào)用 createUser 方法之后,如果內(nèi)部調(diào)用 addUser,因為 addUser 不會使用aop代理,
這也意味著,addUser 的成功或失敗不會影響到 createUser 所在的原有事務(wù)。
2、編程式事務(wù)
通過TransactionTemplate手動管理事務(wù)。這里不過多講解,實際使用較少。
在代碼中顯式地管理事務(wù)的開始、提交和回滾。這種方式更加靈活,但同時也增加了代碼的復(fù)雜性。
有興趣的可以參考:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PlatformTransactionManager transactionManager;
public void createUser(User user) {
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
userRepository.save(user);
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
}
}和JDBC相比:
- 不需要開始或是關(guān)閉connection(可能還有try-finally的語法),相對應(yīng)的,使用的是Transaction callbacks。
- 也不需要手動的catch SQLExceptions,因為Spring會把這個異常當作runtime exceptions來處理。
- 當然想要使用TransactionTemplate,需要先設(shè)置TransactionManager,并需要傳入dataSource。這些都可以配置成Spring的bean,放到context中。
3、事務(wù)傳播行為
Spring 提供了多種事務(wù)傳播行為來定義一個事務(wù)方法應(yīng)如何參與現(xiàn)有事務(wù)。常用的傳播行為包括:
對事務(wù)比較積極的三個傳播行為:
REQUIRED: 使用現(xiàn)有的事務(wù),如果沒有,則創(chuàng)建一個新的事務(wù)(默認值)。REQUIRES_NEW: 總是創(chuàng)建一個新的事務(wù),并掛起當前事務(wù)。MANDATORY: 必須存在一個當前事務(wù),若沒有則拋出異常。
觀望者:
NESTED: 在當前事務(wù)中執(zhí)行一個嵌套事務(wù)(支持保存點)。
比較佛系的三個傳播行為
SUPPORTS: 存在則加入,不存在,則非事務(wù)運行。PROPAGATION_NOT_SUPPORTED:不能參與。PROPAGATION_NEVER:不參與,參與事務(wù),則拋出異常。
3、事務(wù)的隔離級別
1、并發(fā)事務(wù)的問題

2、隔離級別


3、如何設(shè)置
(1) 聲明式事務(wù)(通過 @Transactional 注解)
@Transactional(isolation = Isolation.READ_COMMITTED)
public void transferMoney() {
// 業(yè)務(wù)邏輯
}(2) 編程式事務(wù)(通過 TransactionTemplate)
@Autowired
private TransactionTemplate transactionTemplate;
public void executeInTransaction() {
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
transactionTemplate.execute(status -> {
// 業(yè)務(wù)邏輯
return null;
});
}4、選擇隔離級別
優(yōu)先使用默認值(ISOLATION_DEFAULT)
- 除非有明確需求,否則依賴數(shù)據(jù)庫默認配置,避免框架與數(shù)據(jù)庫行為不一致。
平衡性能與一致性
- 高并發(fā)場景 →
READ_COMMITTED(減少鎖競爭)。 - 強一致性場景 →
REPEATABLE_READ或SERIALIZABLE(需測試性能影響)。
5、查看隔離級別
- 數(shù)據(jù)庫日志:執(zhí)行
SHOW VARIABLES LIKE 'transaction_isolation';(MySQL)或等效命令。 - 代碼調(diào)試:通過
TransactionSynchronizationManager.getCurrentTransactionIsolationLevel()獲取。
4、事務(wù)失效
1. 直接調(diào)用同一類中的事務(wù)方法
在同一類中直接調(diào)用被事務(wù)注解的方法會導(dǎo)致事務(wù)失效。因為 Spring 的 AOP 代理只在通過 Spring 管理的 Bean 進行調(diào)用時才會生效。
代碼示例:
@Service
public class UserServiceTxImpl implements UserServiceTx {
@Transactional(rollbackFor = RuntimeException.class) // 默認傳播行為為 REQUIRED
public void createUser(String username) {
System.out.println("User " + username + " created.");
// 調(diào)用另一個方法
addUser(username);
System.out.println("User " + username + " createUsed ");
}
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = RuntimeException.class) // 新事務(wù)
public void addUser(String username) {
System.out.println("User " + username + " added in a new transaction.");
throw new RuntimeException("Exception in innerMethod");
}
}解決方案
1、可以在本bean里面直接使用spring注入的方式:不過會導(dǎo)致循環(huán)依賴。
2、 AopContext.currentProxy();
@Service
public class UserServiceTxImpl implements UserServiceTx {
@Autowired
private UserServiceTxImpl userService;
@Transactional(rollbackFor = RuntimeException.class) // 默認傳播行為為 REQUIRED
public void createUser(String username) {
System.out.println("User " + username + " created.");
// 調(diào)用另一個方法
userService.addUser(username);
System.out.println("User " + username + " createUsed ");
}
@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = RuntimeException.class) // 新事務(wù)
public void addUser(String username) {
System.out.println("User " + username + " added in a new transaction.");
throw new RuntimeException("Exception in innerMethod");
}
}UserServiceTx o = (UserServiceTx) AopContext.currentProxy();
2. 直接使用 SQL 操作
如果你在事務(wù)管理的方法中執(zhí)行了原生的 SQL 操作(例如通過 JDBC),而沒有通過 Spring 的事務(wù)管理進行處理,事務(wù)可能會失效。
@Service
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional
public void createUser(String username) {
System.out.println("In createUser method");
// 直接使用 JDBC 操作,不通過 Spring 數(shù)據(jù)庫訪問
jdbcTemplate.execute("INSERT INTO users (username) VALUES ('" + username + "')");
// 模擬異常
throw new RuntimeException("Intentional exception"); // 模擬拋出異常
}
}這些操作不通過 Spring 的數(shù)據(jù)訪問層,將會導(dǎo)致事務(wù)管理操作失效。
3. 使用不正常的異常處理
Spring 只對運行時異常(RuntimeException)自動回滾,而對檢查性異常(Exception)默認不回滾。
如果需要回滾需要rollbackFor指定回滾異常。
@Transactional(rollbackFor = RuntimeException.class) // 默認傳播行為為 REQUIRED
public void createUser(String username) {
System.out.println("User " + username + " created.");
// 調(diào)用另一個方法
userService.addUser(username);
System.out.println("User " + username + " createUsed ");
}4、非public方法,目前事務(wù) 只支持pub

5、mysql的存儲引擎說mysaim不支持事務(wù)。
6、對異常進行try catch捕獲,導(dǎo)致無法生效。
7、事務(wù)處理的響應(yīng)超時。
總結(jié)
Spring 的事務(wù)管理功能為開發(fā)者提供了一種高效且靈活的方式來處理復(fù)雜的數(shù)據(jù)庫操作,確保數(shù)據(jù)的一致性和完整性,通過注解或 XML 配置,可以很容易地查看哪些方法是事務(wù)性的,邏輯結(jié)構(gòu)清晰。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java 數(shù)據(jù)結(jié)構(gòu)進階二叉樹題集上
二叉樹可以簡單理解為對于一個節(jié)點來說,最多擁有一個上級節(jié)點,同時最多具備左右兩個下級節(jié)點的數(shù)據(jù)結(jié)構(gòu)。本文將帶你通過實際題目來熟練掌握2022-04-04
SpringBoot集成內(nèi)存數(shù)據(jù)庫hsqldb的實踐
hsqldb只需要添加對應(yīng)的依賴,然后在配置文件進行配置。不需要安裝一個數(shù)據(jù)庫,本文就來介紹一下具體使用,感興趣的可以了解一下2021-09-09
mybatis自定義參數(shù)類型轉(zhuǎn)換器數(shù)據(jù)庫字段加密脫敏
這篇文章主要為大家介紹了mybatis自定義參數(shù)類型轉(zhuǎn)換器數(shù)據(jù)庫字段加密脫敏,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09
springboot+springmvc實現(xiàn)登錄攔截
這篇文章主要介紹了springboot+springmvc實現(xiàn)登錄攔截,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-10-10
Mybatis-Plus中and()和or()的使用與原理詳解
最近發(fā)現(xiàn)MyBatisPlus還是挺好用的,下面這篇文章主要給大家介紹了關(guān)于Mybatis-Plus中and()和or()的使用與原理的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-09-09

