天天看點

transactional注解的使用_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){    }
           
歡迎關注我的公衆号:程式那些事,更多精彩等着您!
更多内容請通路:flydean的部落格 flydean.com
transactional注解的使用_Spring Boot JPA 中transaction的使用