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註解進行修飾。