国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設(shè)計 > 正文

spring事務(wù)

2019-11-11 03:08:39
字體:
供稿:網(wǎng)友

1.背景

SPRing提供了編程式事務(wù)和聲明式事務(wù),但由于編程性事務(wù)的侵入性,開發(fā)中普遍會使用Spring的聲明式事務(wù),下文中所說的Spring事務(wù)也都是指聲明式事務(wù)。

Spring聲明式事務(wù)底層是建立在AOP的基礎(chǔ)上的,其本質(zhì)就是對方法前后進行攔截,然后在目標方法之前創(chuàng)建或加入一個事務(wù),在執(zhí)行完目標方法之后根據(jù)執(zhí)行執(zhí)行情況提交或回滾事務(wù)。

聲明式事務(wù)最大的優(yōu)點就是不需要在業(yè)務(wù)邏輯代碼中摻雜事務(wù)管理的代碼,Spring事務(wù)提供兩種事務(wù)管理方式:基于注解和基于xml配置,我們只需要簡單的配置即可使用。

相比于編程式事務(wù),由于聲明式事務(wù)是基于AOP實現(xiàn)的,所以只能實現(xiàn)對方法的粗粒度的控制,而無法做到代碼塊級別的細粒度控制。

Spring事務(wù)包括5中參數(shù),分別是:傳播行為,隔離級別,是否只讀,事務(wù)超時時間,回滾規(guī)則

本文分析下5種事務(wù)參數(shù),然后對具體的傳播行為給出Demo,以此給出其“細粒度”的事務(wù)實現(xiàn)。

2.Spring事務(wù)的5種參數(shù)

2.1 傳播行為

傳播行為定義了關(guān)于客戶端和被調(diào)用方法的事務(wù)邊界,Spring中定義了7中傳播行為。

傳播行為 意義
PROPAGATION_REQUIRED 默認的Spring事務(wù)傳播規(guī)則;表示當前方法必須在一個事務(wù)中運行,如果一個現(xiàn)有事務(wù)正在運行中,該方法將在那個事務(wù)中運行,否則就會新建一個事務(wù)
PROPAGATION_REQUIRES_NEW 表示當前方法必須在它自己的事務(wù)里運行。每次都會新建一個事務(wù),如果當前有一個事務(wù)在運行的話,則將在這個方法運行期間掛起,待新事務(wù)執(zhí)行完成后,上下文事務(wù)再恢復(fù)執(zhí)行
PROPAGATION_SUPPORTS 表示如果上下文中存在事務(wù),就在當前事務(wù)中執(zhí)行,否則使用非事務(wù)的方式執(zhí)行
PROPAGATION_NOT_SUPPORTED 表示該方法不應(yīng)該在一個事務(wù)中運行。如果上下文中存在事務(wù),它將在該方法執(zhí)行期間被掛起
PROPAGATION_MANDATORY 表示該方法必須在一個事務(wù)中運行,如果沒有事務(wù),將拋出異常
PROPAGATION_NEVER 表示該方法不能在一個事務(wù)中運行,如果有事務(wù),則會拋出異常
PROPAGATION_NESTED 表示如果當前正有一個事務(wù)在運行中,則該方法會運行在一個嵌套事務(wù)中。被嵌套的事務(wù)可以獨立于封裝事務(wù)進行提交或回滾。如果不存在事務(wù),則新建事務(wù)。

對嵌套事務(wù)的理解:

嵌套是子事務(wù)在父事務(wù)中執(zhí)行,子事務(wù)是父事務(wù)的一部分,在進入子事務(wù)之前,父事務(wù)建立一個回滾點,叫save point,然后執(zhí)行子事務(wù),這個子事務(wù)也算是父事務(wù)的一部分,然后子事務(wù)執(zhí)行結(jié)束,父事務(wù)繼續(xù)執(zhí)行。下面看幾個問題就懂了

如果子事務(wù)回滾,會發(fā)生什么?

答:父事務(wù)會回滾到save point,然后嘗試其他的事務(wù)或者其他的業(yè)務(wù)邏輯,父事務(wù)之前的操作不會受到影響,更不會自動回滾

如果父事務(wù)回滾,會發(fā)生什么?

答:父事務(wù)回滾,子事務(wù)也會跟著回滾。父事務(wù)結(jié)束之前,子事務(wù)不會提交。

事務(wù)的提交是什么情況?

答:子事務(wù)是父事務(wù)的一部分,所以子事務(wù)先提交,父事務(wù)再提交

2.2 隔離級別

隔離級別定義一個事務(wù)可能接受其他并發(fā)事務(wù)活動受影響的程度。也可以想象成事務(wù)對于數(shù)據(jù)處理的自私程度。

多個事務(wù)同時運行,經(jīng)常會為了完成他們的工作而操作同一個數(shù)據(jù)。并發(fā)雖然是必需的,但是會導致以下問題:

脹讀——A事務(wù)讀取數(shù)據(jù)并修改,未提交之間B事務(wù)又讀取了數(shù)據(jù)不可重復(fù)讀——A事務(wù)讀取數(shù)據(jù),B事務(wù)讀取數(shù)據(jù)并修改,A事務(wù)再讀取數(shù)據(jù)時發(fā)現(xiàn)兩次數(shù)據(jù)不一致幻讀——事務(wù)A讀取或刪除了全部數(shù)據(jù),事務(wù)B又insert一條數(shù)據(jù),這時事務(wù)A發(fā)現(xiàn)莫名其妙的多了一條數(shù)據(jù)

理想狀態(tài)下,事務(wù)之間是完全隔離的。但是完全隔離會影響性能,因為隔離的實現(xiàn)依賴于數(shù)據(jù)庫中的鎖,侵占性鎖會阻礙并發(fā),要求事務(wù)互相等待

考慮到完全隔離會影響性能,而且并不是所有的情況都要求完全隔離,所以有時候可以在事務(wù)隔離方面靈活處理。因此,就有好幾個隔離級別。

隔離級別 含義
ISOLATION_DEFAULT 使用后端數(shù)據(jù)庫默認的隔離級別
ISOLATION_READ_UNCOMMITTED 允許讀取未提交的更改。 可能導致臟讀、幻讀或不可重復(fù)讀。
ISOLATION_READ_COMMITTED 允許從已經(jīng)提交的并發(fā)事務(wù)讀取。可防止臟讀,但幻讀和不可重復(fù)讀仍可能會發(fā)生。
ISOLATION_REPEATABLE_READ 對相同字段的多次讀取的結(jié)果是一致的,除非數(shù)據(jù)被當前事務(wù)本身改變。可防止臟讀和不可重復(fù)讀,但幻影讀仍可能發(fā)生。
ISOLATION_SERIALIZABLE 完全服從ACID的隔離級別,確保不發(fā)生臟讀、不可重復(fù)讀和幻影讀。這在所有隔離級別中也是最慢的,因為它通常是通過完全鎖定當前事務(wù)所涉及的數(shù)據(jù)表來完成的。

2.3 是否只讀

如果一個事務(wù)只對后端數(shù)據(jù)庫執(zhí)行讀操作,那么該數(shù)據(jù)庫就可能利用那個事務(wù)的只讀特性,采取某些優(yōu)化措施。

由于只讀的優(yōu)化措施是在一個事務(wù)啟動時由后端數(shù)據(jù)庫實施的,因此,只有對于那些具有可能啟動一個新事物的傳播行為(PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED)的方法來說,將事務(wù)聲明為只讀才有意義

2.4 事務(wù)超時

假設(shè)事務(wù)的運行時間變得格外的長,由于事務(wù)可能涉及對后端數(shù)據(jù)庫的鎖定,所以長時間運行的事務(wù)會不必要地占用數(shù)據(jù)庫資源,這時就可以聲明一個事務(wù)在特定時間后自動回滾

由于超時時鐘在一個事務(wù)啟動的時候開始的,因此,有對于那些具有可能啟動一個新事物的傳播行為(PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED)的方法來說,聲明事務(wù)超時才有意義,默認為30S

2.5 回滾規(guī)則

回滾規(guī)則定義了哪些異常引起回滾,哪些不引起。在默認情況下,事務(wù)只出現(xiàn)運行時異常(Runtime Exception)時回滾,而在出現(xiàn)受檢查異常(Checked Exception)時不回滾。

3.事務(wù)傳播特性實例

在本節(jié)中,我們只會考慮事務(wù)的傳播規(guī)則,不會考慮到其隔離級別等其他參數(shù)。測試Service為TestServiceA和TestServiceB

3.1 PROPAGATION_REQUIRED示例

TestSeviceA代碼為:

@Autowiredprivate TransactionDAO transactionDAO;@Autowiredprivate TestServiceB testService;// 會開啟事務(wù),在事務(wù)范圍內(nèi)使用同一個事務(wù),否則開啟新事務(wù)@Transactional(propagation = Propagation.REQUIRES_NEW)public void addOrder() { transactionDAO.insertOrder(); testService.addVoucher(); throw new RuntimeException();}

TestServiceB代碼為:

@Autowired private TransactionDAO transactionDAO; @Transactional(propagation = Propagation.REQUIRES_NEW) public void addVoucher() { transactionDAO.insertOrderVoucher();// throw new RuntimeException(); }

經(jīng)測試,無論是在addOrder還是addVoucher中拋出異常,數(shù)據(jù)都會提交失敗;當且僅當兩個方法都成功時,數(shù)據(jù)提交成功。

這說明這兩個Service使用的是同一個事務(wù),并且只要方法被調(diào)用就會開啟事務(wù)。

注意:一旦有前置方法開啟了Spring事務(wù),在調(diào)用鏈中的方法即使沒有聲明事務(wù),也都會加入到事務(wù)中。

如果在ServiceA中使用了try catch語句后會是什么情況呢?即代碼如下:

TestSeviceA代碼為:

@Autowiredprivate TransactionDAO transactionDAO;@Autowiredprivate TestServiceB testService;// 會開啟事務(wù),在事務(wù)范圍內(nèi)使用同一個事務(wù),否則開啟新事務(wù)@Transactional(propagation = Propagation.REQUIRED)public void addOrder() { transactionDAO.insertOrder(); try { testService.addVoucher(); } catch (Exception e) { }}

TestServiceB代碼為:

@Autowired private TransactionDAO transactionDAO; @Transactional(propagation = Propagation.REQUIRED) public void addVoucher() { transactionDAO.insertOrderVoucher(); throw new RuntimeException(); }

在ServiceB中拋出RuntimeException異常,但是在ServiceA中使用try catch捕獲該異常。

執(zhí)行后發(fā)現(xiàn)數(shù)據(jù)沒有提交,但是出現(xiàn)了一個異常:org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

原因:在addVoucher()方法return時會將Transactional標記為Rollback only, 而addOrder()方法捕獲了bar的RuntimeException,所以Spring將會commit addOrder()的事務(wù),但是這兩個方法使用的是同一事務(wù),因此在commit addOrder()事務(wù)時,將會拋出UnexpectedRollbackException。

3.2 PROPAGATION_REQUIRES_NEW示例

TestServiceA代碼為:

@Autowiredprivate TransactionDAO transactionDAO;@Autowiredprivate TestServiceB testService;// 無論何時都會開啟事務(wù)@Transactional(propagation = Propagation.REQUIRES_NEW)public void addOrder() { transactionDAO.insertOrder(); testService.addVoucher(); throw new RuntimeException();}

TestServiceB代碼為:

@Autowired private TransactionDAO transactionDAO; @Transactional(propagation = Propagation.REQUIRES_NEW) public void addVoucher() { transactionDAO.insertOrderVoucher();// throw new RuntimeException(); }

經(jīng)測試如果在addOrder中拋出異常,order數(shù)據(jù)不能提交,voucher數(shù)據(jù)被正確提交。相反addVoucher中拋出異常,就只能提交order數(shù)據(jù)。

說明兩個Service是在兩個獨立是事務(wù)中運行,并且只要方法被調(diào)用就開啟事務(wù)。

3.3 PROPAGATION_SUPPORTS示例

TestServiceA代碼為:

@Autowiredprivate TransactionDAO transactionDAO;@Autowiredprivate TestServiceB testService;// 自身不會開啟事務(wù),在事務(wù)范圍內(nèi)則使用相同事務(wù),否則不使用事務(wù)@Transactional(propagation = Propagation.SUPPORTS)public void addOrder() { transactionDAO.insertOrder(); testService.addVoucher(); throw new RuntimeException();}

TestServiceB代碼為:

@Autowired private TransactionDAO transactionDAO; @Transactional(propagation = Propagation.SUPPORTS) public void addVoucher() { transactionDAO.insertOrderVoucher();// throw new RuntimeException(); }

由于兩個方法都被申明為SUPPORTS的傳播規(guī)則,所以即使拋出異常,數(shù)據(jù)都也都能被正確提交。說明兩個方法并沒有使用Spring事務(wù),而是使用本地事務(wù),需要注意的是,這兩個方法的本地事務(wù)并不是同一個事務(wù),一旦出現(xiàn)異常將導致數(shù)據(jù)不一致。

我們來考慮一下更多的組合情況:

- REQUIRED → SUPPORTS 這時開啟事務(wù),并使用同一個事務(wù)- REQUIRES_NEW → SUPPORTS 同a- SUPPORTS → REQUIRED ServiceA由本地事務(wù)管理,ServiceB由Spring事務(wù)管理- SUPPORTS → REQUIRES_NEW 同c

3.4 PROPAGATION_NOT_SUPPORTED示例

TestServiceA代碼為:

@Autowiredprivate TransactionDAO transactionDAO;@Autowiredprivate TestServiceB testService;// 自身不會開啟事務(wù),在事務(wù)范圍內(nèi)使用會掛起事務(wù)執(zhí)行,運行完畢恢復(fù)事務(wù)@Transactional(propagation = Propagation.REQUIRED)public void addOrder() { transactionDAO.insertOrder(); testService.addVoucher();}

TestServiceB代碼為:

@Autowired private TransactionDAO transactionDAO; @Transactional(propagation = Propagation.SUPPORTS) public void addVoucher() { transactionDAO.insertOrderVoucher(); throw new RuntimeException(); }

經(jīng)測試如果在addVoucher中拋出異常,addOrder數(shù)據(jù)提交失敗,addVoucher數(shù)據(jù)會提交成功。說明ServiceA開了事務(wù),ServiceB沒有開啟事務(wù),而是使用了本地事務(wù)。

3.5 PROPAGATION_MANDATORY示例

TestServiceA代碼

@Autowiredprivate TransactionDAO transactionDAO;@Autowiredprivate TestServiceB testService;// 自身不會開啟事務(wù),必須在事務(wù)環(huán)境使用,否則報錯@Transactional(propagation = Propagation.MANDATORY)public void addOrder() { transactionDAO.insertOrder(); testService.addVoucher();}

經(jīng)測試以上代碼報錯

org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation ‘mandatory’ 沒有找到事務(wù)環(huán)境

即該注解必須要和能創(chuàng)建事務(wù)的注解使用(PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED)

3.6 PROPAGATION_NEVER 示例

TestServiceA代碼為:

@Autowiredprivate TransactionDAO transactionDAO;@Autowiredprivate TestServiceB testService;@Transactional(propagation = Propagation.REQUIRED)public void addOrder() { transactionDAO.insertOrder(); testService.addVoucher();}

TestServiceB代碼為:

@Autowired private TransactionDAO transactionDAO; // 自身不會開啟事務(wù),在事務(wù)范圍內(nèi)使用拋出異常 @Transactional(propagation = Propagation.NEVER) public void addVoucher() { transactionDAO.insertOrderVoucher(); throw new RuntimeException(); }

經(jīng)測試代碼拋出異常

org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation ‘never’ 存在事務(wù)環(huán)境

3.7 PROPAGATION_NESTED示例

TestServiceA代碼為:

@Autowiredprivate TransactionDAO transactionDAO;@Autowiredprivate TestServiceB testService;// 新開啟一個事務(wù) @Transactional(propagation = Propagation.REQUIRED) public void addOrder() { transactionDAO.insertOrder(); try { testService.addVoucher(); } catch (Exception e) { }// throw new RuntimeException(); }

TestServiceB代碼為:

@Autowired private TransactionDAO transactionDAO; // 啟用嵌套事務(wù) @Transactional(propagation = Propagation.NESTED) public void addVoucher() { transactionDAO.insertOrderVoucher(); throw new RuntimeException(); }

經(jīng)測試,在ServiceB中拋出異常后,addVoucher數(shù)據(jù)提交失敗,addOrder數(shù)據(jù)提交成功。

如果在ServiceA中拋出異常,那么數(shù)據(jù)都會提交失敗。

4.注意事項

@Transactional 只能被應(yīng)用到public方法上, 對于其它非public的方法,如果標記了@Transactional也不會報錯,但方法沒有事務(wù)功能.僅僅 @Transactional 注解的出現(xiàn)不足于開啟事務(wù)行為,需要在spring中開啟事務(wù):默認遇到運行期例外(throw new RuntimeException(“注釋”);)會回滾,即遇到不受檢查(unchecked)的例外時回滾;事務(wù)的傳播特性會在調(diào)用同一類中不同方法時失效

5.參考文獻

https://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/ https://my.oschina.net/dongli/blog/56904 http://blog.csdn.net/aya19880214/article/details/50640596


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 二连浩特市| 河西区| 新津县| 娄烦县| 高唐县| 龙陵县| 禄劝| 读书| 米易县| 武功县| 河曲县| 江川县| 寻乌县| 岢岚县| 古交市| 台山市| 潼关县| 汉中市| 昌黎县| 岑溪市| 太湖县| 汝城县| 綦江县| 温泉县| 顺平县| 渝中区| 新丰县| 遵义县| 浦城县| 香河县| 黔西县| 洪雅县| 兴仁县| 尤溪县| 沽源县| 禄劝| 汉阴县| 扶绥县| 焉耆| 荣昌县| 屏东县|