Spring 學習筆記之 @Transactional詳解

一、數據庫事務基礎

數據庫事務(Transaction)是數據庫管理系統中用于確保數據一致性和完整性的一種機制。它是一組操作的集合,這些操作要么全部成功,要么全部失敗,從而保證數據庫狀態的正確性。

1.1 事務的基本概念

  1. 定義

    事務是用戶定義的一個操作序列,這些操作要么全部執行,要么全部不執行。它是數據庫運行的基本單位。例如,在銀行轉賬操作中,從一個賬戶扣除金額和向另一個賬戶增加金額必須同時成功或同時失敗,這就需要通過事務來保證。
    ?
  2. 事務的生命周期
  • 開始事務:事務的執行開始,通常由用戶或應用程序發起。

  • 執行事務:事務中的各個操作依次執行,如插入、更新、刪除等。

  • 提交事務:如果事務中的所有操作都成功完成,事務被提交,所有操作對數據庫的更改將永久生效。

  • 回滾事務:如果事務中的某個操作失敗,事務將被回滾,所有已經執行的操作都會被撤銷,數據庫恢復到事務開始前的狀態。

1.2 事務的特性(ACID)

事務的特性是通過 ACID 原則來保證的,即原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持久性(Durability)。

1. 原子性(Atomicity)

  • 原子性是指事務中的所有操作要么全部成功,要么全部失敗。事務是一個不可分割的最小執行單位。例如,在一個訂單系統中,創建訂單和扣款操作是一個事務。如果扣款成功但創建訂單失敗,那么整個事務會回滾,扣款操作也會被撤銷,以保證系統的狀態不會出現部分操作成功的情況。

2. 一致性(Consistency)

  • 一致性是指事務執行前后,數據庫從一個一致的狀態轉換到另一個一致的狀態。事務必須保證數據庫的完整性約束沒有被破壞。例如,在一個庫存管理系統中,庫存數量不能為負。如果一個事務試圖將庫存數量減少到負數,那么這個事務應該被回滾,以保證數據庫的一致性。

3. 隔離性(Isolation)

  • 隔離性是指多個并發事務之間是相互隔離的,一個事務的執行不會受到其他事務的干擾。數據庫系統提供了不同的隔離級別來控制事務之間的隔離程度。常見的隔離級別包括:

    • 讀未提交(Read Uncommitted):最低的隔離級別,允許一個事務讀取另一個事務未提交的數據。這種情況下可能會出現臟讀(dirty read),即讀取到其他事務未提交的錯誤數據。

    • 讀已提交(Read Committed):一個事務只能讀取到其他事務已經提交的數據,避免了臟讀。但可能會出現不可重復讀(non-repeatable read),即在同一個事務中,多次讀取同一數據可能得到不同的結果。

    • 可重復讀(Repeatable Read):保證在同一個事務中,多次讀取同一數據的結果是一致的。但可能會出現幻讀(phantom read),即在同一個事務中,查詢滿足某個條件的記錄時,可能會出現新插入的記錄。

    • 可串行化(Serializable):最高的隔離級別,事務之間完全隔離,按照串行的順序執行,避免了臟讀、不可重復讀和幻讀。但這種隔離級別會帶來較大的性能開銷。

4. 持久性(Durability)

  • 持久性是指事務一旦提交,其對數據庫的更改將永久生效,即使系統發生故障也不會丟失。數據庫系統通常通過日志(log)來保證持久性。當事務提交時,數據庫會將事務的操作記錄到日志中,即使系統崩潰,也可以通過日志恢復數據。

1.3 事務的并發控制

在多用戶環境中,多個事務可能會同時對數據庫進行操作,這就需要并發控制機制來保證事務的隔離性和一致性。常見的并發控制方法包括:

1. 鎖機制

  • 共享鎖(Shared Lock,S鎖):當一個事務對數據加上共享鎖后,其他事務可以讀取該數據,但不能修改它。多個事務可以同時對同一數據加共享鎖。

  • 排他鎖(Exclusive Lock,X鎖):當一個事務對數據加上排他鎖后,其他事務不能對該數據加任何鎖,即不能讀取也不能修改。排他鎖用于寫操作,保證數據的獨占訪問。

  • 鎖的粒度可以是行級鎖、表級鎖或數據庫級鎖。行級鎖的粒度最小,鎖的沖突概率較低,但管理開銷較大;表級鎖的粒度較大,鎖的沖突概率較高,但管理開銷較小。

2. 樂觀鎖和悲觀鎖

  • 悲觀鎖(Pessimistic Locking):假設沖突很可能會發生,因此在事務開始時就對數據加鎖,直到事務結束才釋放鎖。悲觀鎖適用于寫操作較多的場景,但可能會導致鎖的沖突和等待。

  • 樂觀鎖(Optimistic Locking):假設沖突較少發生,因此在事務開始時不加鎖,只有在提交時才檢查是否有沖突。如果發現沖突,則回滾事務。樂觀鎖通常通過版本號(Version Number)或時間戳(Timestamp)來實現。樂觀鎖適用于讀操作較多的場景,可以減少鎖的開銷。

1.4 事務的實現機制

數據庫系統通過日志(log)和回滾段(rollback segment)等機制來實現事務的特性。

1. 日志(Log)

  • 日志記錄了事務對數據庫的所有操作,包括修改操作的前值和后值。當事務提交時,數據庫會將日志寫入磁盤,以保證持久性。如果系統發生故障,可以通過日志恢復數據。日志的寫入順序與事務的執行順序一致,因此可以保證事務的原子性和持久性。

2. 回滾段(Rollback Segment)

  • 回滾段用于存儲事務執行過程中數據的舊值。當事務回滾時,數據庫可以從回滾段中恢復數據到事務開始前的狀態。回滾段的大小和數量會影響事務的性能和并發能力。

1.5 事務的使用

在實際的數據庫應用中,事務的使用通常由應用程序通過 SQL 語句來控制。

1. 顯式事務

  • 顯式事務是指用戶明確地定義事務的開始和結束。例如,在 SQL 中可以使用以下語句:

    -- 開始事務
    BEGIN TRANSACTION;-- 執行事務中的操作
    INSERT INTO table_name (column1, column2) VALUES (value1, value2);
    UPDATE table_name SET column1 = value1 WHERE condition;-- 提交事務
    COMMIT;

    如果事務中的某個操作失敗,可以通過以下語句回滾事務:

    ROLLBACK;

2. 隱式事務

  • 隱式事務是指數據庫系統自動為每個單獨的 SQL 語句啟動一個事務。如果語句成功執行,則事務自動提交;如果語句失敗,則事務自動回滾。隱式事務適用于簡單的數據庫操作,但對于復雜的業務邏輯,顯式事務更能保證事務的完整性和一致性。

二、@Transactional介紹

@Transactional 是 Spring 中用于聲明式事務管理的核心注解。它允許開發者通過簡單的注解方式,將事務管理邏輯與業務邏輯分離,從而簡化事務的管理。

2.1?作用

@Transactional 注解用于聲明事務的邊界。它可以讓 Spring 容器在方法執行前后自動管理事務的開啟、提交和回滾。具體來說:

  • 開啟事務:在方法執行前,Spring 會創建一個新的事務(或加入已有的事務)。

  • 提交事務:如果方法正常執行完成,Spring 會提交事務。

  • 回滾事務:如果方法拋出異常,Spring 會根據配置決定是否回滾事務。

2.2 使用場景

@Transactional 通常用于服務層(@Service 注解的類)/ 數據庫訪問層(@Repository注解的類) 的方法上,因為事務管理通常與業務邏輯密切相關。例如:

@Service
public class UserService {@Transactionalpublic void updateUser(User user) {// 更新用戶信息userRepository.save(user);}
}

2.3. 常見屬性

@Transactional代碼如下:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Reflective
public @interface Transactional {@AliasFor("transactionManager")String value() default "";@AliasFor("value")String transactionManager() default "";String[] label() default {};Propagation propagation() default Propagation.REQUIRED;Isolation isolation() default Isolation.DEFAULT;int timeout() default -1;String timeoutString() default "";boolean readOnly() default false;Class<? extends Throwable>[] rollbackFor() default {};String[] rollbackForClassName() default {};Class<? extends Throwable>[] noRollbackFor() default {};String[] noRollbackForClassName() default {};
}

@Transactional 注解提供了多個屬性,用于配置事務的行為。以下是一些常用的屬性:

2.3.1 value/transactionManager

value和transactionManager屬性可以用來指定使用的事務管理器。


value: 這個屬性用于指定事務管理器的名稱。當你的應用中配置了多個事務管理器時,可以通過value屬性來指定使用哪一個。如果只有一個事務管理器,可以省略這個屬性。
transactionManager: 這個屬性與value屬性的作用相同,都是用于指定事務管理器的名稱。通常情況下,value和transactionManager可以互換使用,但transactionManager屬性更明確地表達了其用途。


假設你的應用中配置了兩個事務管理器,分別是transactionManagerA和transactionManagerB,你可以這樣使用@Transactional注解:

@Service
public class MyService {@Transactional("transactionManagerA")public void methodA() {// 使用 transactionManagerA}@Transactional(transactionManager = "transactionManagerB")public void methodB() {// 使用 transactionManagerB}
}

默認事務管理器: 如果沒有指定value或transactionManager,Spring會使用默認的事務管理器。默認的事務管理器通常是第一個被定義的事務管理器。
事務管理器的配置: 確保在Spring配置中正確配置了事務管理器,并且名稱與@Transactional注解中的指定名稱一致。
通過合理使用value或transactionManager屬性,可以靈活地控制不同方法或類使用不同的事務管理器,從而更好地管理事務。

2.3.2?propagation(事務傳播行為)

在 Spring 中,@Transactional 注解的 propagation 屬性用于定義事務傳播行為,它決定了當一個事務方法被另一個事務方法調用時,事務應該如何處理。

后面我們描述外層的Transaction為父事務,內層被調用的事務為子事務。

1.?Propagation.REQUIRED

  • 描述:這是?@Transactional?注解的默認傳播行為。如果當前存在事務,方法將加入該事務;如果當前沒有事務,會創建一個新事務。

  • 示例場景:多個業務操作需要在同一個事務中完成,保證數據的一致性。比如在一個訂單處理服務中,創建訂單和扣減庫存的操作需要在同一個事務里,若其中一個操作失敗,整個事務回滾。

@Service
public class OrderService {@Autowiredprivate InventoryService inventoryService;@Transactional(propagation = Propagation.REQUIRED)public void createOrder() {// 創建訂單的業務邏輯inventoryService.reduceInventory();// 其他業務邏輯}
}@Service
public class InventoryService {@Transactional(propagation = Propagation.REQUIRED)public void reduceInventory() {// 扣減庫存的業務邏輯}
}

當調用?OrderService?的?createOrder?方法時,如果當前沒有事務,會創建一個新事務。調用?InventoryService?的?reduceInventory?方法時,由于當前存在事務,reduceInventory?方法會加入到這個事務中。若在任何一個方法中出現異常,整個事務會回滾。

結論:

1)Propagation.REQUIRED 子事務任何一個失敗回滾,所有事務都會回滾。

2)Propagation.REQUIRED 父事務失敗回滾,所有子事務都會回滾。

2.?Propagation.NESTED

  • 描述:如果當前存在事務,在嵌套事務中執行;如果當前沒有事務,和?REQUIRED?一樣創建新事務。嵌套事務是當前事務的子事務,有自己的保存點。當嵌套事務回滾時,不會影響外部事務,但外部事務回滾時,嵌套事務也會回滾。

  • 示例場景:某些操作可以獨立回滾,但又依賴于外部事務的上下文。例如在批量處理數據時,部分數據處理失敗可以只回滾這部分操作,而不影響其他數據的處理。

  • 注意:需要數據庫支持保存點(如 MySQL InnoDB、Oracle)。

@Service
public class BatchService {@Autowiredprivate SubBatchService subBatchService;@Transactional(propagation = Propagation.REQUIRED)public void batchProcess() {try {subBatchService.subProcess();} catch (Exception e) {// 處理子批量處理異常}// 其他批量處理邏輯}
}@Service
public class SubBatchService {@Transactional(propagation = Propagation.NESTED)public void subProcess() {// 子批量處理邏輯throw new RuntimeException("子批量處理異常");}
}

當調用?BatchService?的?batchProcess?方法時會創建一個事務,調用?SubBatchService?的?subProcess?方法時會創建一個嵌套事務。subProcess?拋出異常時,subProcess?中的操作會回滾,但?batchProcess?中的其他操作不受影響。

結論:

1)Propagation.NESTED 子事務失敗回滾,不影響父事務的狀態。

2)Propagation.NESTED 父事務失敗回滾,所有子事務都會回滾。

3.?Propagation.REQUIRES_NEW

  • 描述:無論當前是否存在事務,都會創建一個新事務,并掛起當前事務(如果存在)。新事務和當前事務相互獨立,一個事務的回滾或提交不會影響另一個事務。

  • 示例場景:當某個操作需要獨立于外部事務時使用,比如記錄日志操作,即使主業務事務失敗,日志記錄也應該保存。

@Service
public class MainService {@Autowiredprivate LogService logService;@Transactional(propagation = Propagation.REQUIRED)public void mainOperation() {try {logService.recordLog();} catch (Exception e) {// 處理日志記錄異常}// 主業務邏輯throw new RuntimeException("主業務異常");}
}@Service
public class LogService {@Transactional(propagation = Propagation.REQUIRES_NEW)public void recordLog() {// 記錄日志的業務邏輯}
}

調用?MainService?的?mainOperation?方法時會創建一個事務,調用?LogService?的?recordLog?方法時會掛起?mainOperation?的事務,創建一個新事務。mainOperation?拋出異常時,主業務事務回滾,但日志記錄事務不受影響。

結論:

1)Propagation.REQUIRES_NEW 子事務失敗回滾,不影響父事務的狀態。

2)Propagation.REQUIRES_NEW 父事務失敗回滾,不影響所有子事務狀態。

4.Propagation.SUPPORTS

  • 描述:如果當前存在事務,方法將加入該事務;如果當前沒有事務,方法將以非事務方式執行。

  • 示例場景:某些查詢操作可以選擇在事務中執行以保證數據的一致性,也可以在非事務環境下執行以提高性能。

@Service
public class QueryService {@Transactional(propagation = Propagation.SUPPORTS)public List<Product> getProducts() {// 查詢產品列表的業務邏輯return null;}
}

如果調用?getProducts?方法時存在事務,它會加入該事務;如果不存在事務,它會以非事務方式執行。

5.?Propagation.NOT_SUPPORTED

  • 描述:方法將以非事務方式執行,如果當前存在事務,會掛起當前事務。

  • 示例場景:一些不需要事務管理的操作,如讀取配置信息等,可以避免事務帶來的開銷。

@Service
public class ConfigService {@Transactional(propagation = Propagation.NOT_SUPPORTED)public String getConfig() {// 獲取配置信息的業務邏輯return null;}
}

如果在事務環境中調用?getConfig?方法,當前事務會被掛起,getConfig?方法以非事務方式執行。

6.?Propagation.MANDATORY

  • 描述:如果當前存在事務,方法將加入該事務;如果當前沒有事務,會拋出?IllegalTransactionStateException?異常。

  • 示例場景:確保方法必須在一個已存在的事務中執行,例如一些數據更新操作依賴于外部事務的上下文。

@Service
public class UpdateService {@Transactional(propagation = Propagation.MANDATORY)public void updateData() {// 更新數據的業務邏輯}
}

如果在沒有事務的情況下調用?updateData?方法,會拋出異常。

7.?Propagation.NEVER

  • 描述:方法以非事務方式執行,如果當前存在事務,會拋出?IllegalTransactionStateException?異常。

  • 示例場景:確保方法不應該在事務中執行,例如一些簡單的計算操作。

@Service
public class CalculationService {@Transactional(propagation = Propagation.NEVER)public int calculate(int a, int b) {return a + b;}
}

如果在事務環境中調用?calculate?方法,會拋出異常。

2.3.3?isolation(事務隔離級別)

指定事務的隔離級別,控制當前事務與其他事務之間的隔離程度。默認值為 Isolation.DEFAULT,即數據庫默認的隔離級別。(即數據庫隔離性里面的具體分類)

隔離級別描述
DEFAULT數據庫默認的隔離級別。
READ_UNCOMMITTED讀未提交,允許并發事務讀取未提交的數據(可能出現臟讀)。
READ_COMMITTED讀已提交,允許并發事務讀取已提交的數據。
REPEATABLE_READ可重復讀,保證在同一個事務中多次讀取同一數據的結果是一致的。
SERIALIZABLE串行化,最高級別的隔離,完全隔離并發事務。
2.3.4?timeout(事務超時時間)

指定事務的超時時間(以秒為單位)。如果事務執行時間超過指定值,事務將被回滾。默認值為 -1,表示使用數據庫默認的超時時間。

2.3.5?readOnly(只讀事務)

指定事務是否為只讀事務。如果設置為 true,則事務不會修改數據,從而可以提高性能。默認值為 false

2.3.6?rollbackFor(回滾異常類)

指定哪些異常會導致事務回滾。默認情況下,Error子類和運行時異常(RuntimeException 及其子類)會導致事務回滾,而檢查型異常不會導致事務回滾。可以通過該屬性指定額外的異常類型。

2.3.7?noRollbackFor(不回滾異常類)

指定哪些異常不會導致事務回滾。即使這些異常是運行時異常,事務也不會回滾。

2.4. 使用方式

@Transactional 可以作用于類或方法上:

2.4.1 作用于方法
@Service
public class UserService {@Transactionalpublic void updateUser(User user) {// 更新用戶信息userRepository.save(user);}
}
2.4.2 作用于類

如果將 @Transactional 作用于類上,則該類的所有方法都將默認使用相同的事務配置。

@Transactional
@Service
public class UserService {public void updateUser(User user) {// 更新用戶信息userRepository.save(user);}public void deleteUser(Long id) {// 刪除用戶userRepository.deleteById(id);}
}

2.5 @Transactional面試問題

1)@Transactional propagation REQUIRED/NETESD/REQUIRES_NEW的區別?

2)propagation = REQUIRED, 子事務失敗的情況。看看下面代碼的執行結果是什么?
?

@Service
public class OrderService {...@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)public void createOrder(Order order) {// Propagation.REQUIREDorderRepository.createOrder(order);try {// Propagation.REQUIREDinventoryRepository.deductStock(order.getProductId(), order.getQuantity());} catch (Exception e) {log.error("扣減庫存失敗", e);}}
}@Repository
public class OrderRepository {...@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)public void createOrder(Order order) {orderMapper.insert(order);}
}@Repository
public class InventoryRepository {...@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)public void deductStock(Long productId, Integer quantity) {inventoryMapper.deductStock(productId, quantity);throw new RuntimeException("出現未知錯誤");}}

OrderRepository.createOrder, InventoryRepository.deductStock, OrderService.createOrder的事務有哪些能成功提交,為什么?

三個事務都不能成功提交,因為InventoyRepository.deductStock事務因為異常會回滾,導致外層事務失敗,然后所有事務都會回滾。

詳細日志如下:?

2025-04-19T10:11:57.650+08:00 ?INFO 35024 --- [nio-8080-exec-1] com.example.controller.OrderController ? : 創建訂單:Order(id=null, orderNumber=order-004, productId=1001, quantity=100, totalAmount=180, createdAt=2025-04-18T21:50:16)
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@e59868]
JDBC Connection [HikariProxyConnection@1303670389 wrapping com.mysql.cj.jdbc.ConnectionImpl@1e79d43] will be managed by Spring
==> ?Preparing: INSERT INTO orders (order_number, product_id, quantity, total_amount) VALUES (?, ?, ?, ?)
==> Parameters: order-004(String), 1001(Long), 100(Integer), 180(BigDecimal)
<== ? ?Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@e59868]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@e59868] from current transaction
==> ?Preparing: UPDATE inventory SET stock_quantity = stock_quantity - ? WHERE product_id = ?
==> Parameters: 100(Integer), 1001(Long)
<== ? ?Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@e59868]
2025-04-19T10:11:57.754+08:00 ERROR 35024 --- [nio-8080-exec-1] com.example.service.OrderService ? ? ? ? : 扣減庫存失敗

java.lang.RuntimeException: 出現未知錯誤
?? ?at com.example.repository.InventoryRepository.deductStock(InventoryRepository.java:31) ~[classes/:na]
?? ?at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
?? ?at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
?? ?at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
?? ?at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
?? ?at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359) ~[spring-aop-6.2.2.jar:6.2.2]
?? ?at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.2.2.jar:6.2.2]
?? ?at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.2.2.jar:6.2.2]
?? ?at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:138) ~[spring-tx-6.2.0.jar:6.2.0]
?? ?at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.2.2.jar:6.2.2]
?? ?at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:380) ~[spring-tx-6.2.0.jar:6.2.0]
?? ?at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.2.0.jar:6.2.0]
?? ?at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.2.2.jar:6.2.2]
?? ?at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:727) ~[spring-aop-6.2.2.jar:6.2.2]
?? ?at com.example.repository.InventoryRepository$$SpringCGLIB$$0.deductStock(<generated>) ~[classes/:na]
?? ?at com.example.service.OrderService.createOrder(OrderService.java:33) ~[classes/:na]
?? ?at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
?? ?at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
?? ?at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
?? ?at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
?? ?at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359) ~[spring-aop-6.2.2.jar:6.2.2]
?? ?at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) ~[spring-aop-6.2.2.jar:6.2.2]
?? ?at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-6.2.2.jar:6.2.2]
?? ?at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:380) ~[spring-tx-6.2.0.jar:6.2.0]
?? ?at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.2.0.jar:6.2.0]
?? ?at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.2.2.jar:6.2.2]
?? ?at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:727) ~[spring-aop-6.2.2.jar:6.2.2]
?? ?at com.example.service.OrderService$$SpringCGLIB$$0.createOrder(<generated>) ~[classes/:na]
?? ?at com.example.controller.OrderController.createOrder(OrderController.java:21) ~[classes/:na]
?? ?
?? ?
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@e59868]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@e59868]
2025-04-19T10:11:57.773+08:00 ERROR 35024 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] ? ?: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only] with root cause

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
?? ?at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:938) ~[spring-tx-6.2.0.jar:6.2.0]
?? ?at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:754) ~[spring-tx-6.2.0.jar:6.2.0]
?? ?at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:698) ~[spring-tx-6.2.0.jar:6.2.0]
?? ?at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:416) ~[spring-tx-6.2.0.jar:6.2.0]
?? ?at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.2.0.jar:6.2.0]
?? ?at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.2.2.jar:6.2.2]
?? ?at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:727) ~[spring-aop-6.2.2.jar:6.2.2]
?? ?at com.example.service.OrderService$$SpringCGLIB$$0.createOrder(<generated>) ~[classes/:na]
?? ?at com.example.controller.OrderController.createOrder(OrderController.java:21) ~[classes/:na]
?? ?

3)progation = NESTED, 子事務失敗的情況。以下代碼的執行結果?

@Service
public class OrderService {...@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)public void createOrder(Order order) {// Propagation.NESTEDorderRepository.createOrder(order);try {// Propagation.NESTEDinventoryRepository.deductStock(order.getProductId(), order.getQuantity());} catch (Exception e) {log.error("扣減庫存失敗", e);}}
}@Repository
public class OrderRepository {...@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)public void createOrder(Order order) {orderMapper.insert(order);}
}@Repository
public class InventoryRepository {...@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)public void deductStock(Long productId, Integer quantity) {inventoryMapper.deductStock(productId, quantity);throw new RuntimeException("出現未知錯誤");}}

OrderRepository.createOrder, InventoryRepository.deductStock, OrderService.createOrder的事務有哪些能成功提交,為什么??

OrderRepository.createOrder 成功提交

InventoryRepository.deductStock 失敗回滾

OrderService.createOrder 成功提交

因為NESTED標記的事務失敗不會影響外層事務的結果。

參考文檔:
Transaction Propagation :: Spring Framework?

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

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

相關文章

【Openlayers】Openlayers 入門教程

Openlayers 入門教程 -系列文章列表 openlayers 入門教程&#xff08;一&#xff09;&#xff1a;openlayers簡介 openlayers 入門教程&#xff08;二&#xff09;&#xff1a;Map 篇 openlayers 入門教程&#xff08;三&#xff09;&#xff1a;View 篇 openlayers 入門教程&a…

【Lua語言】Lua語言快速入門

初始Lua Lua是一種輕量小巧的腳本語言&#xff0c;他使用標準C語言編寫并以源代碼形式開放。這意味著Lua虛擬機可以很方便的嵌入別的程序中&#xff0c;從而為應用程序提供靈活的擴展和定制功能。同時&#xff0c;在目前腳本引擎中&#xff0c;Lua的運行速度占有絕對優勢。 變…

車載診斷新架構--- SOVD初入門(上)

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 周末洗了一個澡,換了一身衣服,出了門卻不知道去哪兒,不知道去找誰,漫無目的走著,大概這就是成年人最深的孤獨吧! 舊人不知我近況,新人不知我過…

linux查看目錄相關命令

查看目錄命令 學習目標 能夠使用Linux命令查看目錄信息 1. 查看目錄命令的使用 命令說明ls查看當前目錄信息tree以樹狀方式顯示目錄信息 ls命令效果圖: tree命令效果圖: 2. 查看當前目錄路徑 命令說明pwd查看當前目錄路徑 pwd命令效果圖: 3. 清除終端內容 命令說明clear…

JavaScript中的Event事件對象詳解

一、事件對象&#xff08;Event&#xff09;概述 1. 事件對象的定義 event 對象是瀏覽器自動生成的對象&#xff0c;當用戶與頁面進行交互時&#xff08;如點擊、鍵盤輸入、鼠標移動等&#xff09;&#xff0c;事件觸發時就會自動傳遞給事件處理函數。event 對象包含了與事件…

OSPF綜合實驗(HCIP)

1&#xff0c;R5為ISP&#xff0c;其上只能配置Ip地址&#xff1b;R4作為企業邊界路由器&#xff0c; 出口公網地址需要通過ppp協議獲取&#xff0c;并進行chap認證 2&#xff0c;整個OSPF環境IP基于172.16.0.0/16劃分&#xff1b; 3&#xff0c;所有設備均可訪問R5的環回&…

2024-04-19| Java: Documented注解學習 JavaDoc

在 Java 中&#xff0c;Documented 是一個元注解&#xff08;meta-annotation&#xff09;&#xff0c;用于標記其他注解&#xff0c;表明這些注解應該被包含在 JavaDoc 文檔中。以下是關于 Documented 注解的作用的簡要說明&#xff1a; 作用 記錄注解信息到 JavaDoc&#x…

15.Chromium指紋瀏覽器開發教程之WebAudio指紋定制

WebAudio指紋概述 瀏覽器中的 WebAudio API 提供了豐富的功能&#xff0c;其中包括了大量生成和處理音頻數據的API。WebAudio API 的音頻指紋技術是一種利用音頻信號的特征來唯一標識音頻的技術。因為WebAudio API 提供了豐富的音頻處理功能&#xff0c;包括合成、過濾、分析等…

2025年贛教云智慧作業微課PPT模板

江西的老師們注意&#xff0c;2025年贛教云智慧作業微課PPT模版和往年不一樣&#xff0c;千萬不要搞錯了&#xff0c;圖上的才是正確的2025年的贛教云智慧作業微課PPT模版&#xff0c;贛教云智慧作業官網有問題&#xff0c;無法正確下載該模板&#xff0c;需要該模板的&#xf…

2.5.1DOS下常用工具 curl,netstat,telnet命令使用

curl命令 Win10及以上系統默認已安裝Curl&#xff0c;打開命令提示符輸入 curl --help&#xff0c;若顯示幫助信息則無需安裝 ??手動安裝方法?? 官網下載&#xff1a;訪問 curl官網 選擇Windows版本curl for Windows若需在 Windows XP 等舊系統使用&#xff0c;需選擇更…

使用Redis實現實時排行榜

為了實現一個實時排行榜系統&#xff0c;我們可以使用Redis的有序集合&#xff08;ZSet&#xff09;&#xff0c;其底層通常是使用跳躍表實現的。有序集合允許我們按照分數&#xff08;score&#xff09;對成員&#xff08;member&#xff09;進行排序&#xff0c;因此非常適合…

Linux——firewalld防火墻(筆記)

目錄 一&#xff1a;Firewalld防火墻的概述 &#xff08;1&#xff09;firewalld簡介 &#xff08;2&#xff09;firewalld&iptables的關系 &#xff08;3&#xff09;firewalld與iptables service的區別 1. ?規則管理方式? 2. ?默認策略與設計邏輯? 3. ?配置文…

JS中實現類似sleep、wait、delay的延時功能

前言 編寫代碼時很多時候需要進行流程化的操作&#xff0c;各個流程間通常需要等待一定時間&#xff0c;這在很多語言中通常可以使用 sleep 、 wait 、 delay 等函數來實現。JavaScript原生并沒有類似的功能&#xff0c;想要延時通常就是使用 setTimeout(functionRef, delay) …

Elasticsearch:使用 ES|QL 進行搜索和過濾

本教程展示了 ES|QL 語法的示例。請參考 Query DSL 版本&#xff0c;以獲得等效的 Query DSL 語法示例。 這是一個使用 ES|QL 進行全文搜索和語義搜索基礎知識的實踐介紹。 有關 ES|QL 中所有搜索功能的概述&#xff0c;請參考《使用 ES|QL 進行搜索》。 在這個場景中&#x…

Java 動態代理實現

Java 動態代理實現 一、JDK動態代理二、CGLIB動態代理三、動態代理的應用場景四、JDK代理與CGLIB代理比較 動態代理是Java中一種強大的技術&#xff0c;它允許在運行時創建代理對象&#xff0c;用于攔截對目標對象的方法調用。 一、JDK動態代理 JDK動態代理是Java標準庫提供的代…

Apache IoTDB V2.0.2/V1.3.4 發布|新增表模型權限管理、UDF、嵌套查詢功能

Release Announcement Version 2.0.2/1.3.4 Apache IoTDB V2.0.2、V1.3.4 已經發布&#xff01; V2.0.2 作為樹表雙模型正式版本&#xff0c;主要新增表模型權限管理、用戶管理以及相關操作鑒權&#xff0c;并新增了表模型 UDF、系統表和嵌套查詢等功能。 V1.3.4 主要新增模式…

鴻蒙開發11-ARKUI框架

ARKUI&#xff08;方舟 UI 框架&#xff09;是 HarmonyOS Next&#xff08;原 OpenHarmony&#xff09;的核心 UI 開發框架&#xff0c;基于聲明式編程范式&#xff0c;支持 ArkTS 語言&#xff0c;能夠高效構建跨設備的響應式應用。以下是對 ARKUI 框架及開發的詳細介紹&#…

Linux 進程間通信詳解

一.進程間通信介紹 1. 進程間通信概念 進程間通信&#xff08;Inter-Process Communication, IPC&#xff09;是指在不同進程之間傳遞或交換信息的一種機制。在操作系統中&#xff0c;進程是資源分配和獨立運行的基本單位&#xff0c;它們擁有各自獨立的內存空間和系統資源。…

從0開始掌握動態規劃

動態規劃的核心思想 -- 以空間換時間 復雜點說通過分解問題為子問題并存儲子問題解來優化復雜計算的算法策略。 簡單看個問題。 一&#xff0c;初始&#xff1a;求最長連續遞增子序列 nums [10,9,2,5,3,7,101,18] 求上面數組中的最長連續遞增子序列&#xff0c;輸出其長度 …

Python Requests 庫:從安裝到精通

摘要 本文詳細介紹 Python Requests 庫的安裝與使用&#xff0c;通過常見示例讓你輕松掌握。 一、引言 在當今的互聯網時代&#xff0c;與各種 Web 服務進行交互是非常常見的需求。Python 作為一門功能強大且易于學習的編程語言&#xff0c;提供了許多用于網絡請求的庫&…