什麼是事務的傳播行爲?java
當事務方法被另外一個事務方法調用時, 必須指定事務應該如何傳播. 例如: 方法可能繼續在現有事務中運行, 也可能開啓一個新事務, 並在本身的事務中運行.spring
事務的傳播行爲能夠由傳播屬性指定. Spring 定義了 7 種類傳播行爲.數據庫
這裏比較重要的是前兩個。app
(本文出自:http://my.oschina.net/happyBKs/blog/513390)測試
REQUIRED 傳播行爲spa
當 bookService 的 purchase() 方法被另外一個事務方法 checkout() 調用時, 它默認會在現有的事務內運行. 這個默認的傳播行爲就是 REQUIRED. 所以在 checkout() 方法的開始和終止邊界內只有一個事務. 這個事務只在 checkout() 方法結束的時候被提交, 結果用戶一本書都買不了
.net
事務傳播屬性能夠在 @Transactional 註解的 propagation 屬性中定義code
REQUIRES_NEW 傳播行爲orm
另外一種常見的傳播行爲是 REQUIRES_NEW. 它表示該方法必須啓動一個新事務, 並在本身的事務內運行. 若是有事務在運行, 就應該先掛起它.xml
注意:以上傳播行爲的類別是在@Transactional註解上配置propagation來完成的,若是沒有配置默認狀況是REQUIRED。
HappyBKs的打比方: :)
是否是仍是有點暈?我打個比方吧,比如,你和一個哥們去飯前吃飯(一個事務),發現另外一個哥們正在飯店吃飯(另外一個事務),這時候,你過去有兩種選擇:一,咱們倆加入那個正在吃飯的哥們的飯桌你們一塊兒吃,這樣,咱們事務就被融入了調用咱們的事務,這就是REQUIRED傳播行爲;二,咱們不打擾他,只是打個招呼,咱們本身開一桌吃飯,這樣咱們本身有個獨立的事務,這種方式就是REQUIRES_NEW 傳播行爲。
咱們仍是用例子來講明什麼是事務的傳播行爲吧。
假如咱們在上面的例子中加入一個新的需求:一個用戶一次買多本,若是買多本的操做也是事務,那麼則存在事務中調用事務的狀況。這樣的事務該如何管理。
新定義 Cashier 接口: 表示客戶的結帳操做
修改數據表信息以下, 目的是用戶 Tom 在結帳時, 餘額只能支付第一本書, 不夠支付第二本書:
代碼編寫以下:
package com.happBKs.spring.tx; import java.util.List; public interface Cashier { public void checkout(String username, List<String> isbns); }
package com.happBKs.spring.tx; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service("cashier") public class CashierImpl implements Cashier { @Autowired private BookShopService bookShopService; @Transactional public void checkout(String username, List<String> isbns) { // TODO Auto-generated method stub for(String isbn:isbns){ bookShopService.purchase(username, isbn); } } }
測試程序:
package com.happBKs.spring.tx; import java.util.Arrays; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TransactionTest { private ApplicationContext ctx=null; private BookShopDao bookShopDao; private BookShopService bookShopService; private Cashier cashier=null; { ctx=new ClassPathXmlApplicationContext("applicationContext.xml"); bookShopDao=ctx.getBean(BookShopDao.class); bookShopService=ctx.getBean(BookShopService.class); cashier=ctx.getBean(Cashier.class); } @Test public void test5(){ cashier.checkout("HappyBks", Arrays.asList("1001","1002")); } }
好,咱們先把數據庫中的數據填好:
運行以後,一切正常:
這個方法沒問題。那麼如今若是我再買一次,會怎麼樣?HappBKs只有120元了,第一本能夠買,第二本金額不足了。那麼整個checkout方法做爲一個事務會如何管理呢,我第一本的購買事務會不會完成?仍是回滾?
好,在調用一次test5試試吧。
看到了吧,因爲默認事務的傳播行爲是REQUIRED ,因此被調用的事務(即兩個買一本書的事務)被融入到了調用它們的事務(即checkout方法)中,做爲一個事務,那麼當買第二本書出現餘額不足的異常,整個事務將被回滾。
這裏須要注意:在指定傳播行爲的時候,須要指定事務傳播行爲類型的時候應該在被調用事務的方法上的事務註解上進行設置。因此這裏的例子中應該在以下方法上進行修改,好比這裏咱們將默認改成REQUIRED 。(其實默認就是REQUIRED ,寫不寫都同樣)。完整的例子請見前一篇博客文章。
package com.happBKs.spring.tx; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service("bookShopService") public class BookShopServiceImpl implements BookShopService { @Autowired private BookShopDao bookShopDao; //添加事務註解 //使用propagation指定事務的傳播行爲,即當前事務的方法被另外一個事務方法調用時,如何使用是事務。 //使用另一個方法的事務(REQUIRED)?仍是另開一個新事物?(REQUIRES_NEW) //默認REQUIRED @Transactional(propagation=Propagation.REQUIRED) public void purchase(String username, String isbn) { // TODO Auto-generated method stub //1. 獲取書的單價 int price=bookShopDao.findBookPriceIsdn(isbn); //2. 更新書的庫存 bookShopDao.updateBookStock(isbn); //3. 更新用戶餘額 bookShopDao.updateUserAccount(username, price); } }
好,若是咱們如今將其修改成REQUIRES_NEW 傳播行爲。
package com.happBKs.spring.tx; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service("bookShopService") public class BookShopServiceImpl implements BookShopService { @Autowired private BookShopDao bookShopDao; //添加事務註解 //使用propagation指定事務的傳播行爲,即當前事務的方法被另外一個事務方法調用時,如何使用是事務。 //使用另一個方法的事務(REQUIRED)?仍是另開一個新事物?(REQUIRES_NEW) //REQUIRES_NEW:事務本身的事務,調用該事物方法的事務被掛起 @Transactional(propagation=Propagation.REQUIRES_NEW) public void purchase(String username, String isbn) { // TODO Auto-generated method stub //1. 獲取書的單價 int price=bookShopDao.findBookPriceIsdn(isbn); //2. 更新書的庫存 bookShopDao.updateBookStock(isbn); //3. 更新用戶餘額 bookShopDao.updateUserAccount(username, price); } }
咱們再來運行一次:
看到了吧,當被調用事務的傳播行爲改成REQUIRES_NEW,被調用事務創建一個本身的新事務,該事物執行期間,被調用事務是掛起的,執行完成以後,才返回被調用事務checkout。