Spring Boot JPA 中transaction的使用



Spring Boot JPA 中transaction的使用

transaction是我們在做數據庫操作的時候不能回避的一個話題,通過transaction,我們可以保證數據庫操作的原子性,一致性,隔離性和持久性。

本文我們將會深入的探討Spring Boot JPA中@Transactional注解的使用。

通過@Transactional注解,我們可以設置事物的傳播級別和隔離級別,同時可以設置timeout, read-only, 和 rollback等特性。

@Transactional的實現

Spring通過創建代理或者操縱字節碼來實現事物的創建,提交和回滾操作。如果是代理模式的話,Spring會忽略掉@Transactional的內部方法調用。

如果我們有個方法callMethod,並標記它為@Transactional,那么Spring Boot的實現可能是如下方式:

createTransactionIfNecessary();
try {
    callMethod();
    commitTransactionAfterReturning();
} catch (exception) {
    completeTransactionAfterThrowing();
    throw exception;
}

@Transactional的使用

@Transactional使用起來很簡單,可以放在class上,可以放在interface上,也可以放在方法上面。

如果放在方法上面,那么該方法中的所有public方法都會應用該Transaction。

如果@Transactional放在private方法上面,則Spring Boot將會忽略它。

Transaction的傳播級別

傳播級別Propagation定義了Transaction的邊界,我們可以很方便的在@Transactional注解中定義不同的傳播級別。

下面我們來分別看一下Transaction的傳播級別。

REQUIRED

REQUIRED是默認的傳播級別,下面的兩種寫法是等價的:

    @Transactional
    public void deleteBookWithDefaultTransaction(Long id) {
        bookRepository.deleteBookById(id);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void deleteBookWithRequired(Long id) {
    }

Spring會檢測現在是否有一個有效的transaction。如果沒有則創建,如果有transaction,則Spring將會把該放方法的業務邏輯附加到已有的transaction中。

我們再看下REQUIRED的偽代碼:

if (isExistingTransaction()) {
    if (isValidateExistingTransaction()) {
        validateExisitingAndThrowExceptionIfNotValid();
    }
    return existing;
}
return createNewTransaction();

SUPPORTS

在SUPPORTS的情況下,Spring首先會去檢測是否有存在Transaction,如果存在則使用,否則不會使用transaction。

我們看下代碼怎么使用:

    @Transactional(propagation = Propagation.SUPPORTS)
    public void deleteBookWithSupports(Long id) {
    }

SUPPORTS的實現偽代碼如下:

if (isExistingTransaction()) {
    if (isValidateExistingTransaction()) {
        validateExisitingAndThrowExceptionIfNotValid();
    }
    return existing;
}
return emptyTransaction;

MANDATORY

在MANDATORY情況下,Spring先會去檢測是否有一個Transaction存在,如果存在則使用,否則拋出異常。

我們看下代碼怎么使用:

    @Transactional(propagation = Propagation.MANDATORY)
    public void deleteBookWithMandatory(Long id) {
    }

MANDATORY的實現邏輯如下:

if (isExistingTransaction()) {
    if (isValidateExistingTransaction()) {
        validateExisitingAndThrowExceptionIfNotValid();
    }
    return existing;
}
throw IllegalTransactionStateException;

NEVER

如果是NEVER的情況下,如果現在有一個Transaction存在,則Spring會拋出異常。

使用的代碼如下:

    @Transactional(propagation = Propagation.NEVER)
    public void deleteBookWithNever(Long id) {
    }

實現邏輯代碼如下:

if (isExistingTransaction()) {
    throw IllegalTransactionStateException;
}
return emptyTransaction;

NOT_SUPPORTED

如果使用的是NOT_SUPPORTED,那么Spring將會首先暫停現有的transaction,然后在非transaction情況下執行業務邏輯。

我們這樣使用:

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void deleteBookWithNotSupported(Long id) {
    }

REQUIRES_NEW

當REQUIRES_NEW使用時,Spring暫停當前的Transaction,並創建一個新的。

我們看下代碼怎么使用:

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void deleteBookWithRequiresNew(Long id){
    }

相應的實現代碼如下:

if (isExistingTransaction()) {
    suspend(existing);
    try {
        return createNewTransaction();
    } catch (exception) {
        resumeAfterBeginException();
        throw exception;
    }
}
return createNewTransaction();

NESTED

NESTED顧名思義,是嵌套的Transaction,Spring首先檢查transaction是否存在,如果存在則創建一個savepoint,如果我們的程序拋出異常的時候,transaction將會回滾到該savepoint。如果沒有transaction,NESTED的表現和REQUIRED一樣。

我們看下怎么使用:

    @Transactional(propagation = Propagation.NESTED)
    public void deleteBookWithNested(Long id){
    }

Transaction的隔離級別

隔離級別就是我們之前提到的原子性,一致性,隔離性和持久性。隔離級別描述了改動對其他並發者的可見程度。

隔離級別主要是為了防止下面3個並發過程中可能出現的問題:

  1. 臟讀: 讀取一個transaction還沒有提交的change
  2. 不可重復讀:在一個transaction修改數據庫中的某行數據時,另外一個transaction多次讀取同一行數據,獲取到的不同的值。
  3. 幻讀: 在一個transaction添加或者刪除數據庫的數據時,另外一個transaction做范圍查詢,獲得了不同的數據行數。

READ_UNCOMMITTED

READ_UNCOMMITTED是隔離級別中最低的級別。這個級別下,並發的3個問題都可能出現。

我們這樣使用:

    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    public void deleteBookWithReadUncommitted(Long id){
    }

READ_COMMITTED

READ_COMMITTED可以防止臟讀。

我們看下代碼:

    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void deleteBookWithReadCommitted(Long id){
    }

REPEATABLE_READ

REPEATABLE_READ可以防止臟讀和不可重復讀。

使用的代碼如下:

    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public void deleteBookWithRepeatableRead(Long id){
    }

SERIALIZABLE

SERIALIZABLE是最嚴格的基本,可以防止臟讀,不可重復讀和幻讀。

我們看下怎么使用:

    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void deleteBookWithSerializable(Long id){
    }

本文的例子可以參考https://github.com/ddean2009/learn-springboot2/tree/master/springboot-transaction

更多教程請參考 flydean的博客


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM