Spring中, Java方法的事務傳播類型經過 @Transactional 註解進行指明, 並經過該註解的 propagation 屬性指明事務傳播的具體類型.@Transactional 註解的使用很是靈活, 能夠註解在服務接口上, 也能夠註解在服務類的方法上, 還能夠註解在Spring Repository的接口方法上.html
@Transactional(propagation = Propagation.REQUIRED)
Spring 中一共有七中事務傳播類型. 分別說明以下:
若是當前已經存在事務, 那麼加入該事務, 若是不存在事務, 建立一個事務, 而後執行事務操做. 這是默認的傳播屬性值. 也是最多見的選擇.
當前也就是你所聲明的服務方法被調用的時候.java
也就是說在聲明的事務方法被調用的時候, 就已經在一個事務當中了. 好比下面的僞代碼:數據庫
@Transactional public void service(){ serviceA(); serviceB(); } @Transactional serviceA(); @Transactional serviceB();
service() 方法開啓了一個事務, 當 serviceA(); serviceB(); 被調用的額時候回加入 service() 所在的事務上下文. 分佈式
注意: @Transactional 沒有指明 propagation 屬性, 取默認值 Propagation.REQUIRED.net
含義爲: 要求的, 必須的. 若是被註解的方法有這個傳播屬性, 它的行爲是:代理
概括爲: 有就用, 沒有就建立(事務).
也就能夠理解 Propagation.REQUIRED 的意思了 -- 要求的. 無論怎樣它可以保證操做老是在一個事務中進行的.日誌
應用場景: 不知道方法的調用者是否建立了事務, 可是要求當前被調用的方法必須在一個事務當中執行.code
支持當前事務,若是當前沒有事務,就拋出異常
對於這個類型, 它通常做爲一個事務的一部分定義. 不能獨立執行. 通常做爲一個事務中的子操做. 好比經典的轉帳做爲例子.orm
兩個帳戶 A 和 B 之間轉帳, 從 A 轉 1000 到 B , 拆分爲兩個操做, 而且要在一個事務中執行.htm
操做1: 先把 1000 加到 B 帳戶上, 若是 B 帳戶增長成功, 執行操做2
操做2: 若是 B 帳戶增長成功, 那麼 A 帳戶扣除 1000.
當兩個操做同時成功時, 事務執行成功, 不然, 任意一步錯誤, 回滾以前的所有操做. 那麼對應的操做1和操做2咱們能夠實現爲兩個 Propagation.MANDATORY
類型的Java方法. 而且把這兩個方法放在一個 Propagation.REQUIRED
類型的方法中, 例如:
// 服務接口 interface AccountService { TransactionLog transfer(Long accountA, Long accountB, BigDecimal amount); } // 服務實現 @Service class AccountServiceImpl implements AccountService { // 父事務, 用於組織多個子事務. @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public TransactionLog transfer(Long accountA, Long accountB, BigDecimal amount) { // 先加後減 addAmount(accountB, amount) addAmount(accountA, amount.negate()) } } // 數據庫訪問對象 @Repository class interface AccountRepository extends JpaRepository<Account, Long> { // 餘額操做(自增, 自減) @Modifying @Transactional(propagation = Propagation.MANDATORY) @Query(value = "UPDATE account a SET a.balance = a.balance + ?2 WHERE a.id = ?1") void addAmount(Long id, BigInteger amount); }
新建事務,若是當前存在事務,把當前事務掛起
特徵: 啓動一個新的, 不依賴於環境的 "內部" 事務. 這個事務將被徹底 提交 或 回滾 而不依賴於外部事務,它擁有本身的隔離範圍, 本身的鎖, 等等.當內部事務開始執行時, 外部事務將被掛起, 內務事務結束時, 外部事務將繼續執行. Propagation.REQUIRES_NEW 經常使用於日誌記錄,或者交易失敗仍須要留痕.還有就是 時序控制, 支付依賴於已經建立的訂單, 無訂單不支付. 先要有訂單才能支付. 常見於事務步驟 要求時序 的狀況.
開啓一個全新的事務, 通常用於分佈式事務中的一個前期步驟. 好比一鍵購功能. 主要步驟包括:
建立訂單
發起支付
下面的僞代碼模擬了一個外層事務和內層事務的業務過程.
// 一鍵購服務 class BuyService { @Transactional(propagation = Propagation.REQUIRED) public void buyDirectly() { Order OrderService.createOrder(OrderDto orderDto); PayService.pay(PayDto payDto); } } interface OrderService { void createOrder(OrderDto orderDto); } interface PayService { @Transactional(propagation = Propagation.REQUIRES_NEW) Payment pay(PayDto payDto); }
OrderService.createOrder 運行在外層事務中, 若是建立訂單失敗, 就不必發起支付了, 直接回滾了, 根本就到不了支付這一步.
當綁定的銀行卡餘額不足的狀況, PayService.pay(); 是能夠回滾的, 而不會影響 buyDirectly 整個事務, OrderService.createOrder(); 成功. 向銀行卡不足金額後, 能夠從新發起支付, 完成購買過程.
注意: Propagation.REQUIRES_NEW 若是做爲一個子事務運行, 調用者和被調這不要在同一個服務類中(由於Spring AOP動態代理的限制, 在同一個類中事務是不起做用的)Propagation.REQUIRES_NEW 的通常使用場景是做爲內層事務能夠單獨回滾. 而不是回滾整個外層事務. 所以若是調用者和被調用者若是在一個類中, Propagation.REQUIRES_NEW 註解的方法並 不會 開啓一個新的事務. 所以就達不到內層事務單獨回滾的目的.
概括: 內層事務能夠獨立回滾, 不影響外層事務.
前提是外層事務的方法不能和內層事務的方法在同一個服務類中
以非事務方式執行操做, 若是當前存在事務, 就把當前事務掛起
若是當前存在事務, 則拋出異常, 不然在無事務環境上執行代碼
對於方法中只存在只讀操做, 咱們可使用這個. 通常事務註解爲:
@Transactional(propagation = Propagation.NEVER, readOnly = true)
嵌套事務, 它的做用至關於 Propagation.REQUIRED 和 Propagation.REQUIRES_NEW 的合體
特徵: Propagation.NESTED 啓動一個 "嵌套的" 事務, 它是已經存在事務的一個真正的子事務. 潛套事務開始執行時, 它將取得一個存儲點(Savepoint). 若是這個嵌套事務失敗, 咱們將回滾到此 存儲點. 潛套事務是外部事務的一部分, 只有外部事務結束後它纔會被提交. 因而可知, Propagation.REQUIRES_NEW 和 Propagation.NESTED 的最大區別在於, Propagation.REQUIRES_NEW 徹底是一個新的事務, 而 Propagation.NESTED 則是外部事務的子事務, 若是外部事務提交, 嵌套事務也會被,這個規則一樣適用於回滾.
注意: 使用此種事務傳播類型, 須要設置事務管理器的 nestedTransactionAllowed 屬性爲 true
/** * TransactionConfig.java * * 事務配置 */ @Configuration @EnableTransactionManagement public class Transaction { @Bean public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(entityManagerFactory); transactionManager.setNestedTransactionAllowed(true); return transactionManager; } }
表示當前方法沒必要須要具備一個事務上下文, 可是若是有一個事務的話, 它也能夠在這個事務中運行
概括: 有沒有父級事務(外層事務)均可以接受
這個類型通常用於不會修改(UPDATE, DELETE)數據庫狀態的Java方法裏面. 若是說在上述 AccountServiceImpl 實現類的 transfer 方法中還要經過調用其餘的方法(包含SELECT語句)去返回某些數據,那麼該方法能夠標註爲 Propagation.SUPPORTS 類型.
關鍵字: 建立, 掛起, 恢復, 非事務執行, 事務執行.
@Transactional(propagation=Propagation.REQUIRED)
若是有事務, 那麼加入事務, 沒有的話新建一個(默認狀況下)
@Transactional(propagation=Propagation.NOT_SUPPORTED)
容器不爲這個方法開啓事務
@Transactional(propagation=Propagation.REQUIRES_NEW)
不論是否存在事務,都建立一個新的事務,原來的掛起,新的執行完畢,繼續執行老的事務
@Transactional(propagation=Propagation.MANDATORY)
必須在一個已有的事務中執行,不然拋出異常
@Transactional(propagation=Propagation.NEVER)
必須在一個沒有的事務中執行,不然拋出異常(與Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS)
若是其餘bean調用這個方法,在其餘bean中聲明事務,那就用事務.若是其餘bean沒有聲明事務,那就不用事務.