Spring對於事務的控制@Transactional註解詳解

引用自:https://blog.csdn.net/fanxb92/article/details/81296005spring

先簡單介紹一下Spring事務的傳播行爲:

所謂事務的傳播行爲是指,若是在開始當前事務以前,一個事務上下文已經存在,此時有若干選項能夠指定一個事務性方法的執行行爲。在TransactionDefinition定義中包括了以下幾個表示傳播行爲的常量:

TransactionDefinition.PROPAGATION_REQUIRED:若是當前存在事務,則加入該事務;若是當前沒有事務,則建立一個新的事務。這是默認值。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:建立一個新的事務,若是當前存在事務,則把當前事務掛起。
TransactionDefinition.PROPAGATION_SUPPORTS:若是當前存在事務,則加入該事務;若是當前沒有事務,則以非事務的方式繼續運行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,若是當前存在事務,則把當前事務掛起。
TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,若是當前存在事務,則拋出異常。
TransactionDefinition.PROPAGATION_MANDATORY:若是當前存在事務,則加入該事務;若是當前沒有事務,則拋出異常。
TransactionDefinition.PROPAGATION_NESTED:若是當前存在事務,則建立一個事務做爲當前事務的嵌套事務來運行;若是當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。
而後說一下Spring事務的回滾機制:

Spring的AOP即聲明式事務管理默認是針對unchecked exception回滾。Spring的事務邊界是在調用業務方法以前開始的,業務方法執行完畢以後來執行commit or rollback(Spring默認取決因而否拋出runtimeException)。

若是你在方法中有try{}catch(Exception e){}處理,那麼try裏面的代碼塊就脫離了事務的管理,若要事務生效須要在catch中throw new RuntimeException ("xxxxxx");

再簡單介紹一下@Transactional註解底層實現方式吧,毫無疑問,是經過動態代理,那麼動態代理又分爲JDK自身和CGLIB,這個也很少贅述了,畢竟今天的主題是如何將@Transactional對於事物的控制應用到爐火純青。哈哈~

第一點要注意的就是在@Transactional註解的方法中,再調用本類中的其餘方法method2時,那麼method2方法上的@Transactional註解是不!會!生!效!的!可是加上也並不會報錯,拿圖片簡單幫助理解一下吧


@Transactional註解經過AOP實現方式圖解1
經過代理對象在目標對象先後進行方法加強,也就是事務的開啓提交和回滾。那麼繼續調用本類中其餘方法是怎樣呢,以下圖


@Transactional註解經過AOP實現方式圖解2
可見目標對象內部的自我調用,也就是經過this.指向的目標對象將不會執行方法的加強。

先說第二點須要注意的地方,等下說如何解決上面第一點的問題。第二點就是@Transactional註解的方法必須是公共方法,就是必須是public修飾符!!!

至於這個的緣由,發表下我的的理解吧,由於JVM的動態代理是基於接口實現的,經過代理類將目標方法進行加強,想一下也是啦,沒有權限訪問那麼你讓我怎麼進行,,,好吧,這個我也沒有深刻研究底層,我的理解我的理解。

在這裏我也放個問題吧,但願有高手能夠回覆指點指點我,由於JVM動態代理是基於接口實現的,那麼是否是service層都要按照接口和實現類的開發模式,註解纔會生效呢,就是說controller層直接調用沒有接口的service層,加了註解也同樣不起做用吧,這個懶了,沒有測試,其一是由於沒有人會這麼開發吧,其二是我就認爲是不起做用的,哈哈,,,

下面來解決一下第一點的問題,如何在方法中調用本類中其餘方法呢。

經過AopContext.currentProxy ()獲取到本類的代理對象,再去調用就好啦。由於這個是CGLIB實現,因此要開啓AOP,固然也很簡單,在springboot啓動類上加上註解@EnableAspectJAutoProxy(exposeProxy = true)就能夠啦,這個依賴你們自行搜一下就好啦。要注意,注意,代理對象調用的方法也要是public修飾符,不然方法中獲取不到注入的bean,會報空指針錯誤。

emmmm,我先把調用的方式和結果說下吧。本身簡單寫了代碼,有點粗糙,就不要介意啦,嘿嘿。。。

Controller中調用Service

@RestController
public class TransactionalController {
 
    @Autowired
    private TransactionalService transactionalService;
 
    @PostMapping("transactionalTest")
    public void transacionalTest(){
        transactionalService.transactionalMethod();
    }
}
Service中實現對事務的控制:接口

public interface TransactionalService {
    void transactionalMethod();
}
Service中實現對事務的控制:實現類        (各類狀況的說明都寫在圖片裏了,這樣方便閱讀,有助於快速理解吧)





上面兩種狀況無論使不使用代理調用方法1和方法2,方法transactionalMethod都處在一個事務中,四條更新操做所有失敗。

那麼有人可能會有疑問了,在方法1和方法2上都加@Transactional註解呢?答案是結果和上面是一致的。

小結只要方法transactionalMethod上有註解,而且方法1和方法2都處於當前事務中(不使用代理調用,方法1和方法2上的@Transactional註解是不生效的;使用代理,須要方法1和方法2都處在transactionalMethod方法的事務中,默認或者嵌套事務都可,固然也能夠不加@Transactional註解),那麼總體保持事務一致性。

若是想要方法1和方法2均單獨保持事務一致性怎麼辦呢,剛說過了,若是不是用代理調用@Transactional註解是不生效的,因此必定要使用代理調用實現,而後讓方法1和方法2分別單獨開啓新的事務,便OK啦。下面擺上圖片。





這兩種狀況都是方法1和方法2均處在單獨的事務中,各自保持事務的一致性。

接下來進行進一步的優化,能夠在transactionalMethod方法中分別對方法1和方法2進行控制。要將代碼的藝術發揮到極致嘛,下面裝逼開始。



代碼太長了,超過屏幕了,粘貼出來截的圖,紅框註釋須要仔細看,但願不要影響你的閱讀體驗,至此,本篇關於@Transactioinal註解的使用就到此爲止啦,

簡單總結一下吧:

一、就是@Transactional註解保證的是每一個方法處在一個事務,若是有try必定在catch中拋出運行時異常。

二、方法必須是public修飾符。不然註解不會生效,可是加了註解也沒啥毛病,不會報錯,只是沒卵用而已。

三、this.本方法的調用,被調用方法上註解是不生效的,由於沒法再次進行切面加強。

springboot

相關文章
相關標籤/搜索