@Transactional

东方燚明發表於2024-04-15

一、技術名稱

宣告式註解@Transactional

二、定義

@Transactional註解是Spring框架中用於宣告式事務管理的關鍵註解,主要作用於回滾。

事務四個特性:

「原子性 」表示一個事務中的所有操作要麼全部成功執行,要麼全部失敗回滾;

「一致性 」表示事務執行前後資料庫的狀態保持一致;

「隔離性 」表示多個事務併發執行時,彼此之間是相互隔離的,互不干擾;

「永續性 」表示一旦事務提交,其所做的修改將永久儲存在資料庫中。

三、功能

將此註解放到類或方法上,當出現異常時可回滾sql,保證業務安全

1、事務的隔離級別:@Transactional中透過 isolation = Isolation.XXXX 進行設定。

是資料庫系統用來定義事務在併發執行時的可見性和順序的一組規則,用於解決併發事務之間可能出現的各種併發問題,如髒讀、不可重複讀、幻讀等。

常見的隔離級別:

讀未提交(Read Uncommitted)。這是最低的隔離級別,在這個級別下,事務可以讀取到其他事務未提交的資料,可能會導致髒讀、不可重複讀和幻讀等問題。

讀已提交(Read Committed)。在這個級別下,事務只能讀取已經提交的資料,這樣可以避免髒讀問題,但仍然可能存在不可重複讀和幻讀問題。

可重複讀(Repeatable Read)。這個級別可以保證在一個事務內多次讀取相同的資料時,資料不會被其他事務修改,這可以避免髒讀和不可重複讀問題,但仍然可能存在幻讀問題。

序列化(Serializable)。這是最高的隔離級別,在這個級別下,事務會按順序一個接一個地執行,不允許併發執行。這樣可以避免髒讀、不可重複讀和幻讀等併發問題,但會降低系統的併發效能。

2、事務的傳播行為:@Transactional(propagation = Propagation.XXXX)

為了解決事務方法互相呼叫的事務問題。

Propagation.REQUIRED(預設傳播行為):如果父方法有事務,就加入父方法事務,如果沒有就新建自己獨立的事務!

Propagation.REQUIRES_NEW:不管父方法有沒有事務,都自己新建事務!也就是互不影響!

Propagation.NESTED:當前事務回滾,父事務不回滾,但父事務回滾,當前事務一定回滾!

3、事務的回滾:

回滾:@Transactional中透過 rollbackFor = xxxException.class

不回滾:noRollbackFor屬性:指定哪些異常不會回滾, 預設沒有指定

4、事務超時則回滾:

超時了多半是遇到某些問題,導致程式卡住,從而長時間佔用資料庫資源,設定超時及時釋放資源。

超時時間在@Transactional中透過 timeout進行設定。

5、事務設定只讀:

對一個查詢操作來說,如果我們把它設定成只讀,就能夠明確告訴資料庫,這個操作不涉及寫操作。這樣資料庫就能夠針對查詢操作來進行最佳化。

// readOnly = true把當前事務設定為只讀 預設是false!

@Transactional(readOnly = true)

四、使用場景

當一個方法內有多條運算元據庫的操作

事務失效場景:

1、被標註的方法的修飾符為非public

2、當前類中使用非事務方法呼叫事務方法

3、事務方法內部捕捉了異常,沒有異常丟擲方法外

4、未指定能觸發回滾的異常型別

預設RuntimeException或者Error才回滾異常

五、底層

public class TransactionalAnnotationProcessor {

private PlatformTransactionManager transactionManager;

public TransactionalAnnotationProcessor(PlatformTransactionManager transactionManager) {

this.transactionManager = transactionManager;

}

public Object process(Object target, Method method, Object[] args) throws Throwable {

Transactional transactional = method.getAnnotation(Transactional.class);

TransactionStatus transactionStatus = null;

try {

// 開啟事務

transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());

// 執行目標方法

Object result = method.invoke(target, args);

// 提交事務

transactionManager.commit(transactionStatus);

return result;

} catch (Exception ex) {

// 回滾事務

if (transactionStatus != null) {

transactionManager.rollback(transactionStatus);

}

throw ex;

}

}

}

TransactionalAnnotationProcessor類是一個簡化版本的註解解析器。它接收一個PlatformTransactionManager物件作為建構函式的引數,用於管理事務。process()方法接收目標物件、目標方法和方法引數作為引數。該方法首先透過反射獲取目標方法上的@Transactional註解,然後根據註解的配置開啟事務。接著,執行目標方法,並根據方法執行的結果決定是提交事務還是回滾事務。

六、其它

1、髒讀:

髒讀就是指當一個事務正在訪問資料,並且對資料進行了修改,而這種修改還沒有提交到資料庫中,這時,另外一個事務也訪問這個資料,然後使用了這個資料。

2、幻讀:

資料行變多或者變少了。事務 A 根據條件查詢得到了 N 條資料,但此時事務 B 刪除或者增加了 M 條符合事務 A 查詢條件的資料,這樣當事務 A 再次進行查詢的時候真實的資料集已經發生了變化,但是A卻查詢不出來這種變化,因此產生了幻讀。

3、不可重複讀:

是指在一個事務內,多次讀同一資料。在這個事務還沒有結束時,另外一個事務也訪問該同一資料。那麼,在第一個事務中的兩次讀資料之間,由於第二個事務的修改,那麼第一個事務兩次讀到的的資料可能是不一樣的。這樣就發生了在一個事務內兩次讀到的資料是不一樣的,因此稱為是不可重複讀。

不可重複讀和幻讀的區別:

不可重複讀強調的是資料行資料發生改變。

幻讀強調的是結果集發生改變。

引用文章:

1@Transactional註解超詳細

2髒讀、幻讀和不可重複讀

相關文章