1. 程式人生 > >Spring,為內部方法新起一個事務,此處應有坑

Spring,為內部方法新起一個事務,此處應有坑

事務的作用,使我們操作能夠連貫起來。而spring則是提供了一個更簡單的方法,只要使用 @Transactional 一個註解,就可以保證操作的連貫性了。

普通用法,稍後再說,這裡要說的是: 在最外面的方法中,有一個@Transactional 的註解,當有丟擲異常時,則進行回滾操作:

@Transactional(readOnly = false, rollbackFor = Throwable.class, isolation = Isolation.REPEATABLE_READ)

原本這個方法執行得好好的,但是有一天,我們需要在這個方法裡新增一個新業務操作,而且這個業務操作是不要求回滾的,類似於做日誌記錄一類的。WHAT SHOULD I DO ?

由於業務的獨特性,我能夠快速想到的是,在這個類裡面加一個private方法,然後直接去呼叫就ok了,如果說還是考慮到回滾的話,我也快速想到 @Transactional 的NOT_SUPPORTED傳播特性,如:

@Transactional(propagation = Propagation.NOT_SUPPORTED)

private void doMyExJob(UserDebitCardBean userDebitCard) {

    System.out.println("do my job...");

    //do my job...

}

這看起來很合理,沒毛病。

然而就是執行不起來,只要外面呼叫的方法一丟擲異常,那麼這個新方法的資料操作將會被回滾。媽蛋,到底哪裡出了問題???仔細查了下資料,原來 @Transactional 註解由於原理決定了他只能作用於public方法中,而這裡改為private,就完全被忽略無視了。OK,改唄:

@Transactional(propagation = Propagation.NOT_SUPPORTED)

public void doMyExJob(UserDebitCardBean userDebitCard) {

    System.out.println("do my job...");

    //do my job...

}

感覺應該好了,然而並沒有。我也是醉了,這個問題,如果仔細花時間,找原理是沒有問題的,但是在關鍵時刻來這麼一下,還是很不爽的。 網上看到一哥們說,還必須要將方法寫到另一個類中,而且要通過spring的注入方式進行呼叫,才可以。好吧,那我就按照他的來,結果真的成功了。

//在介面中進行了定義,能夠注入

@Override

@Transactional(propagation = Propagation.NOT_SUPPORTED)

public void doMyExJob(UserDebitCardBean userDebitCard) {

    System.out.println("do my job...");

    //do my job...

}

總算可以了,在趕時間的時候,能夠解決問題的,就是好方法。至此,問題解決。1. 使用public訪求;2. 寫在外部類中,可被呼叫; 3. 使用注入的方式進行該方法的執行。

說實話,spring這種事務還是有點不太好用的,要求太多,當然了,有很大部分原因是我沒有理解其精髓。OK,下面我們來看看spring事務的講解:

在配置檔案中,預設情況下,<tx:annotation-driven>會自動使用名稱為transactionManager的事務管理器。所以,如果定義的事務管理器名稱為transactionManager,那麼就可以直接使用<tx:annotation-driven/>。如下:

<!-- 配置事務管理器 -->

<beanid="transactionManager"

    class="org.springframework.jdbc.datasource.DataSourceTransactionManager"

    p:dataSource-ref="dataSource">

</bean>

<!-- enables scanning for @Transactional annotations -->

<tx:annotation-driven/>

<tx:annotation-driven>一共有四個屬性如下,

  • mode:指定Spring事務管理框架建立通知bean的方式。可用的值有proxy和aspectj。前者是預設值,表示通知物件是個JDK代理;後者表示Spring AOP會使用AspectJ建立代理

  • proxy-target-class:如果為true,Spring將建立子類來代理業務類;如果為false,則使用基於介面的代理。(如果使用子類代理,需要在類路徑中新增CGLib.jar類庫)

  • order:如果業務類除事務切面外,還需要織入其他的切面,通過該屬性可以控制事務切面在目標連線點的織入順序。

  • transaction-manager:指定到現有的PlatformTransaction Manager bean的引用,通知會使用該引用

@Transactional的屬性

isolation 列舉org.springframework.transaction.annotation.Isolation的值 事務隔離級別

noRollbackFor Class<? extends Throwable>[] 一組異常類,遇到時不回滾。預設為{}

noRollbackForClassName Stirng[] 一組異常類名,遇到時不回滾,預設為{}

propagation 列舉org.springframework.transaction.annotation.Propagation的值 事務傳播行為

readOnly boolean 事務讀寫性

rollbackFor Class<? extends Throwable>[] 一組異常類,遇到時回滾

rollbackForClassName Stirng[] 一組異常類名,遇到時回滾

timeout int 超時時間,以秒為單位

value String 可選的限定描述符,指定使用的事務管理器

@Transactional標註的位置

@Transactional註解可以標註在類和方法上,也可以標註在定義的介面和介面方法上。

如果我們在介面上標註@Transactional註解,會留下這樣的隱患:因為註解不能被繼承,所以業務介面中標註的@Transactional註解不會被業務實現類繼承。所以可能會出現不啟動事務的情況。所以,spring建議我們將@Transaction註解在實現類上。

在方法上的@Transactional註解會覆蓋掉類上的@Transactional。

注意:

@Transactional 可以作用於介面、介面方法、類以及類方法上。當作用於類上時,該類的所有 public 方法將都具有該型別的事務屬性,同時,我們也可以在方法級別使用該標註來覆蓋類級別的定義。

雖然 @Transactional 註解可以作用於介面、介面方法、類以及類方法上,但是 Spring 建議不要在介面或者介面方法上使用該註解,因為這隻有在使用基於介面的代理時它才會生效。另外, @Transactional 註解應該只被應用到 public 方法上,這是由 Spring AOP 的本質決定的。如果你在 protected、private 或者預設可見性的方法上使用 @Transactional 註解,這將被忽略,也不會丟擲任何異常。

預設情況下,只有來自外部的方法呼叫才會被AOP代理捕獲,也就是,類內部方法呼叫本類內部的其他方法並不會引起事務行為,即使被呼叫方法使用@Transactional註解進行修飾。