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個并發過程中可能出現的問題:
- 髒讀: 讀取一個transaction還沒有送出的change
- 不可重複讀:在一個transaction修改資料庫中的某行資料時,另外一個transaction多次讀取同一行資料,擷取到的不同的值。
- 幻讀: 在一個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
