事務的基本概念
事務(Transaction)是由一系列對系統中數據進行訪問與更新的操作所組成的一個程序 執行邏輯單元(Unit)。一方面,當多個應用程序並發訪問數據庫時,事務可以在這些應用程序之間提供一個隔離方法,以防止彼此的操作互相干擾。另一方面,事務為數據庫操作序列提供了一個從失敗中恢復到正常狀態的方法, 同時提供了數據庫即使在異常狀態下仍能保持數據一致性的方法。
事務具有四個特征,分別是原子性(Atomicity )、一致性(Consistency )、隔離性(Isolation) 和持久性(Durability),簡稱為事務的ACID特性。
- 原子性(Atomicity):原子性是指事務是一個不可分割的工作單位,事務中的操作要么都發生,要么都不發生。
- 一致性(Consistency):事務前后數據的完整性必須保持一致。
- 隔離性(Isolation):事務的隔離性是多個用戶並發訪問數據庫時,數據庫為每一個用戶開啟的事務,不能被其他事務的操作數據所干擾,多個並發事務之間要相互隔離。
- 持久性(Durability):持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來即使數據庫發生故障也不應該對其有任何影響
分析源碼前,先從理論上大概分析下,純JDBC操作數據庫的基本步驟:
- 獲取連接 Connection conn = DriverManager.getConnection()
- 開啟事務conn.setAutoCommit(true/false);
- 執行CRUD
- 提交事務/回滾事務 conn.commit() / conn.rollback();
- 關閉連接 conn.close();
@Transactional分析

1 @Target({ElementType.TYPE, ElementType.METHOD}) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Inherited 4 @Documented 5 public @interface Transactional { 6 7 /** 8 * Alias for {@link #transactionManager}. 9 */ 10 @AliasFor("transactionManager") 11 String value() default ""; 12 13 /** 14 * A <em>qualifier</em> value for the specified transaction. 15 * 事務管理器 16 */ 17 @AliasFor("value") 18 String transactionManager() default ""; 19 20 /** 21 * The transaction propagation type. 22 * 傳播行為定義,枚舉類型,是spring獨有的事務行為設計,默認為PROPAGATION_REQUIRED(支持當前事務,不存在則新建) 23 */ 24 Propagation propagation() default Propagation.REQUIRED; 25 26 /** 27 * The transaction isolation level. 28 * 隔離級別,對應數據庫的隔離級別實現,mysql默認的隔離級別是 read-committed 29 */ 30 Isolation isolation() default Isolation.DEFAULT; 31 32 /** 33 * The timeout for this transaction (in seconds).超時時間,默認使用數據庫的超時,mysql默認的事務等待超時為5分鍾 34 */ 35 int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; 36 37 /** 38 * A boolean flag that can be set to {@code true} if the transaction is 39 * effectively read-only 是否只讀,默認是false 40 */ 41 boolean readOnly() default false; 42 43 /** 44 * Defines zero (0) or more exception {@link Class classes}, which must be subclasses of {@link Throwable}, indicating which exception types must cause 45 * a transaction rollback. 異常回滾列表,默認的是RuntimeException異常回滾 46 */ 47 Class<? extends Throwable>[] rollbackFor() default {}; 48 49 /** 50 * Defines zero (0) or more exception names (for exceptions which must be a subclass of {@link Throwable}), indicating which exception types must cause 51 * a transaction rollback. 導致事務回滾的異常類名字數組 52 */ 53 String[] rollbackForClassName() default {}; 54 55 /** 56 * Defines zero (0) or more exception {@link Class Classes}, which must be subclasses of {@link Throwable}, indicating which exception types must 57 * <b>not</b> cause a transaction rollback.不會導致事務回滾的異常類數組 58 */ 59 Class<? extends Throwable>[] noRollbackFor() default {}; 60 61 /** 62 * Defines zero (0) or more exception names (for exceptions which must be a subclass of {@link Throwable}) indicating which exception types must <b>not</b> 63 * cause a transaction rollback. 不會導致事務回滾的異常類名字數組,必須繼承自Throwable 64 */ 65 String[] noRollbackForClassName() default {}; 66 67 }
事務傳播行為
事務傳播行為(propagation behavior)指的就是當一個事務方法被另一個事務方法調用時,這個事務方法應該如何進行。
Propagation.REQUIRED | 如果有事務, 那么加入事務, 沒有的話新建一個(默認情況) |
Propagation.SUPPORTS | 如果其他bean調用這個方法,在其他bean中聲明事務,那就用事務。如果其他bean沒有聲明事務,那就不用事務 |
Propagation.MANDATORY | 必須在一個已有的事務中執行,否則拋出異常 |
Propagation.REQUIRES_NEW | 不管是否存在事務,都創建一個新的事務,原來的掛起,新的執行完畢,繼續執行老的事務 |
Propagation.NOT_SUPPORTED | 容器不為這個方法開啟事務 |
Propagation.NEVER | 必須在一個沒有的事務中執行,否則拋出異常(與Propagation.MANDATORY相反) |
Propagation.NESTED | 如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則進行與PROPAGATION_REQUIRED類似的操作。 |
PROPAGATION_NESTED 與PROPAGATION_REQUIRES_NEW的異同:
- 二者的相同點是如果不存在一個活動的事務,都會開啟一個新的事務。
- 使用 PROPAGATION_REQUIRES_NEW時,內層事務與外層事務就像兩個獨立的事務一樣,一旦內層事務進行了提交后,外層事務不能對其進行回滾。
- 使用PROPAGATION_NESTED時,外層事務的回滾可以引起內層事務的回滾。而內層事務的異常並不會導致外層事務的回滾,它是一個真正的嵌套事務。
事務隔離級別
大多數數據庫默認的事務隔離級別是Read committed,比如Sql Server , Oracle。MySQL的默認隔離級別是Repeatable read,原理可以參照:MySQL 事務。
隔離級別 | 說明 | 臟讀 | 不可重復讀 | 幻讀 |
Isolation.DEFAULT | 使用數據庫默認的事務隔離級別 | |||
Isolation.READ_UNCOMMITTED |
讀未提交,最低的隔離級別, 它充許令外一個事務可以看到這個事務未提交的數據。
|
√ | √ | √ |
Isolation.READ_COMMITTED | 讀已提交,保證一個事務修改的數據提交后才能被另外一個事務讀取 | × | √ | √ |
Isolation.REPEATABLE_READ | 可重復讀 | × | × | √ |
Isolation.SERIALIZABLE | 供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接着一個地執行,不能並發執行 | × | × | × |
1. 臟讀 :臟讀就是指當一個事務正在訪問數據,並且對數據進行了修改,而這種修改還沒有提交到數據庫中,這時,另外一個事務也訪問這個數據,然后使用了這個數據。
2. 不可重復讀 :是指在一個事務內,多次讀同一數據。在這個事務還沒有結束時,另外一個事務也訪問該同一數據。那么,在第一個事務中的兩次讀數據之間,由於第二個事務的修改,那么第一個事務兩次讀到的的數據可能是不一樣的。這樣就發生了在一個事務內兩次讀到的數據是不一樣的情況。
3. 幻讀 : 是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的全部數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那么,以后就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺一樣。
事務失效場景
1、數據庫引擎不支持事務
以 MySQL 為例,其 MyISAM 引擎是不支持事務操作的,InnoDB 才是支持事務的引擎,一般要支持事務都會使用 InnoDB。
2、類沒有被 Spring 管理
方法被標注了@Transactional,但是類沒有注解,沒有被Spring管理

// @Service public class OrderServiceImpl implements OrderService { @Transactional public void updateOrder(Order order) { // update order } }
3、方法調用問題
對象內部方法互相調用不會被Spring的AOP攔截,@transactional注解無效;
事務方法或者類不是public,無法被外部包訪問到,或者是final無法繼承,@transactional注解無效。
4、事務管理器配置問題
沒有指定transactionManager參數,默認的transactionManager並不是期望的,以及一個事務中涉及到了多個數據庫。
5、異常被吃了

@Service public class OrderServiceImpl implements OrderService { @Transactional public void updateOrder(Order order) { try { // update order } catch { } } }
6、異常類型錯誤

@Service public class OrderServiceImpl implements OrderService { @Transactional public void updateOrder(Order order) { try { // update order } catch { throw new Exception("更新錯誤"); } } }
默認回滾的是:RuntimeException,如果想觸發其他異常的回滾,需要在注解上配置一下,如:
@Transactional(rollbackFor = Exception.class)