大白話解讀Spring事務(wù)傳播原理,讓你事務(wù)管理不再懵逼
大家好,我是小米,一個(gè)熱愛技術(shù)分享的程序員。今天我們來(lái)談一下Spring事務(wù)傳播。在使用Spring進(jìn)行數(shù)據(jù)庫(kù)操作時(shí),我們經(jīng)常會(huì)遇到需要使用事務(wù)的情況,而Spring對(duì)事務(wù)的管理非常方便,其中就包括事務(wù)傳播機(jī)制。
什么是事務(wù)傳播
事務(wù)傳播指的是在一個(gè)方法調(diào)用另一個(gè)方法時(shí),事務(wù)應(yīng)該如何進(jìn)行傳播。在Spring框架中,事務(wù)傳播有多種策略,它們用于控制不同方法之間事務(wù)的關(guān)系。在使用Spring事務(wù)的時(shí)候,我們需要了解這些事務(wù)傳播機(jī)制的特點(diǎn)和使用場(chǎng)景,以便更好地控制事務(wù)的傳播和管理。
事務(wù)的不同分類
Spring事務(wù)傳播機(jī)制根據(jù)傳播的不同情況,可以分為三類:支持當(dāng)前事務(wù)、不支持當(dāng)前事務(wù)、嵌套事務(wù)。
支持當(dāng)前事務(wù):是指當(dāng)前方法需要在一個(gè)事務(wù)內(nèi)執(zhí)行,如果當(dāng)前沒有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。如果當(dāng)前存在事務(wù),則沿用當(dāng)前事務(wù)。
不支持當(dāng)前事務(wù):是指當(dāng)前方法需要在沒有事務(wù)的情況下執(zhí)行,如果當(dāng)前存在事務(wù),則掛起當(dāng)前事務(wù),執(zhí)行當(dāng)前方法,執(zhí)行完畢后再恢復(fù)原先的事務(wù)。
嵌套事務(wù):是指在當(dāng)前事務(wù)中開啟一個(gè)新的事務(wù),這個(gè)新的事務(wù)可以看做是當(dāng)前事務(wù)的子事務(wù)。如果當(dāng)前沒有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。
事務(wù)的傳播方式
Spring事務(wù)的傳播方式用的比較多的是以下三種:
- Required(默認(rèn)):表示當(dāng)前方法必須在一個(gè)事務(wù)內(nèi)執(zhí)行,如果當(dāng)前沒有事務(wù),則新開啟一個(gè)事務(wù),如果當(dāng)前存在事務(wù),則沿用當(dāng)前事務(wù)。它是Spring事務(wù)傳播機(jī)制的默認(rèn)選項(xiàng)。這種事務(wù)傳播機(jī)制的使用場(chǎng)景是:多個(gè)操作需要在同一事務(wù)中進(jìn)行,例如對(duì)訂單進(jìn)行下單和扣款操作。
- Requires_new:表示當(dāng)前方法必須在一個(gè)新的事務(wù)中執(zhí)行,如果當(dāng)前存在事務(wù),則掛起當(dāng)前事務(wù),開啟新的事務(wù),執(zhí)行完畢后再恢復(fù)原先的事務(wù)。這種事務(wù)傳播機(jī)制的使用場(chǎng)景是:需要將當(dāng)前事務(wù)掛起,執(zhí)行獨(dú)立的操作,例如對(duì)商品進(jìn)行庫(kù)存調(diào)整和日志記錄。
- Nested:表示當(dāng)前方法必須在一個(gè)嵌套事務(wù)中執(zhí)行,如果當(dāng)前沒有事務(wù),則新開啟一個(gè)事務(wù),如果當(dāng)前存在事務(wù),則在當(dāng)前事務(wù)的基礎(chǔ)上創(chuàng)建一個(gè)嵌套事務(wù)。這種事務(wù)傳播機(jī)制的使用場(chǎng)景是:需要對(duì)當(dāng)前事務(wù)進(jìn)行子事務(wù)的操作,例如對(duì)訂單進(jìn)行部分退款操作。
需要注意的是,Nested只在當(dāng)前事務(wù)是一個(gè)真正的事務(wù)時(shí)才有效,如果當(dāng)前事務(wù)并不是一個(gè)真正的事務(wù)(例如使用TransactionDefinition.PROPAGATION_NOT_SUPPORTED或TransactionDefinition.PROPAGATION_NEVER時(shí)),Nested和Required的效果是一樣的。
除了上述傳播方式外,Spring還支持其他的傳播方式,例如supports、not_supported、mandatory、never等,它們的作用和含義可以根據(jù)具體的業(yè)務(wù)場(chǎng)景進(jìn)行選擇和使用。
判斷內(nèi)外方法是否在同一事務(wù)
在使用Spring事務(wù)時(shí),我們需要注意內(nèi)部方法和外部方法是否在同一個(gè)事務(wù)中。
- 如果內(nèi)部方法和外部方法在同一個(gè)事務(wù)中,那么當(dāng)內(nèi)部方法發(fā)生異常時(shí),外部方法也會(huì)受到影響,此時(shí)需要將異常統(tǒng)一在外層處理。
- 如果內(nèi)部方法和外部方法不在同一個(gè)事務(wù)中,那么內(nèi)部方法的異常不會(huì)影響到外部方法,但是外部方法的異??赡軙?huì)影響到內(nèi)部方法。
- 然而,Nested有一個(gè)特殊情況,即當(dāng)內(nèi)部方法使用Nested傳播機(jī)制時(shí),內(nèi)部方法和外部方法不在同一個(gè)事務(wù)中,但是內(nèi)部方法的異常仍然會(huì)影響到外部方法。
案例:內(nèi)外方法在同一個(gè)事務(wù)
為了更好地理解Spring事務(wù)的傳播機(jī)制,我們可以通過(guò)一個(gè)簡(jiǎn)單的電商項(xiàng)目來(lái)演示上述三種情況。
在該電商項(xiàng)目中,我們有兩個(gè)Service,一個(gè)是OrderService,一個(gè)是GoodsService。OrderService負(fù)責(zé)生成訂單,而GoodsService負(fù)責(zé)扣減庫(kù)存。兩個(gè)Service中都有一個(gè)reduceStock方法,用來(lái)扣減庫(kù)存。在扣減庫(kù)存的同時(shí),我們還需要判斷庫(kù)存是否充足。如果庫(kù)存不足,我們需要拋出一個(gè)RuntimeException。
首先,我們來(lái)看一下內(nèi)部方法和外部方法在同一個(gè)事務(wù)中的情況。在這種情況下,我們可以使用required傳播機(jī)制。具體實(shí)現(xiàn)如下:
在這個(gè)例子中,如果庫(kù)存不足,會(huì)拋出一個(gè)RuntimeException,整個(gè)事務(wù)會(huì)回滾。如果扣減庫(kù)存失敗,同樣會(huì)拋出一個(gè)RuntimeException,整個(gè)事務(wù)也會(huì)回滾。如果訂單生成成功,整個(gè)事務(wù)會(huì)被提交。
案例:內(nèi)外方法不在同一個(gè)事務(wù)
接下來(lái),我們來(lái)看一下內(nèi)部方法和外部方法不在同一個(gè)事務(wù)中的情況。在這種情況下,我們可以使用requires_new傳播機(jī)制。具體實(shí)現(xiàn)如下:
在這個(gè)例子中,我們將reduceStock方法的事務(wù)傳播機(jī)制設(shè)置為REQUIRES_NEW。在執(zhí)行該方法時(shí),Spring會(huì)將當(dāng)前事務(wù)掛起,創(chuàng)建一個(gè)新的事務(wù)來(lái)執(zhí)行reduceStock方法。如果reduceStock方法執(zhí)行成功,則會(huì)提交新的事務(wù)。如果reduceStock方法執(zhí)行失敗,則會(huì)回滾新的事務(wù),但不會(huì)影響當(dāng)前事務(wù)。
在這個(gè)例子中,我們將需要扣減的庫(kù)存數(shù)量加了1。這樣在扣減庫(kù)存時(shí),就會(huì)發(fā)現(xiàn)庫(kù)存不足。此時(shí),reduceStock方法會(huì)拋出一個(gè)RuntimeException,新的事務(wù)會(huì)回滾,但當(dāng)前事務(wù)不會(huì)受到影響。因此,訂單生成成功,但庫(kù)存并沒有被扣減。
嵌套事務(wù)
最后,我們來(lái)看一下嵌套事務(wù)的情況。在這種情況下,我們可以使用nested傳播機(jī)制。具體實(shí)現(xiàn)如下:
在這個(gè)例子中,我們將reduceStock方法的事務(wù)傳播機(jī)制設(shè)置為NESTED。在執(zhí)行該方法時(shí),Spring會(huì)創(chuàng)建一個(gè)嵌套事務(wù)來(lái)執(zhí)行reduceStock方法。如果reduceStock方法執(zhí)行成功,則嵌套事務(wù)會(huì)提交。如果reduceStock方法執(zhí)行失敗,則嵌套事務(wù)會(huì)回滾,但不會(huì)影響外層事務(wù)。
在這個(gè)例子中,我們?cè)赾reateOrder方法中調(diào)用了reduceStock方法,并將需要扣減的庫(kù)存數(shù)量加了2。這樣在扣減庫(kù)存時(shí),就會(huì)發(fā)現(xiàn)庫(kù)存不足。此時(shí),reduceStock方法會(huì)拋出一個(gè)RuntimeException,嵌套事務(wù)會(huì)回滾,但當(dāng)前事務(wù)不會(huì)受到影響。因此,訂單生成失敗,庫(kù)存也沒有被扣減。
結(jié)論
綜上所述,Spring事務(wù)傳播機(jī)制提供了靈活的事務(wù)管理方式,可以根據(jù)不同的業(yè)務(wù)場(chǎng)景選擇不同的傳播機(jī)制來(lái)控制事務(wù)的行為。其中,REQUIRES_NEW和NESTED是比較特殊的傳播機(jī)制,可以在需要的時(shí)候使用。
在使用Spring事務(wù)時(shí),需要注意以下幾點(diǎn):
- 事務(wù)傳播機(jī)制的選擇要根據(jù)業(yè)務(wù)場(chǎng)景來(lái)確定。
- 如果內(nèi)層方法和外層方法在同一個(gè)事務(wù)中,那么內(nèi)層方法拋出異常時(shí),異常應(yīng)該由外層方法來(lái)處理。
- 如果內(nèi)層方法和外層方法在不同的事務(wù)中,那么內(nèi)層方法拋出異常時(shí),不會(huì)影響到外層方法。
- NESTED傳播機(jī)制是一種比較特殊的傳播機(jī)制,需要慎重使用。
在實(shí)際開發(fā)中,我們需要根據(jù)不同的業(yè)務(wù)場(chǎng)景選擇合適的事務(wù)傳播機(jī)制,并且要根據(jù)實(shí)際情況來(lái)處理事務(wù)異常,以保證事務(wù)的正確執(zhí)行。