Spring支持兩種類型的事務管理:
- 編程式事務管理: 直接使用PlatformTransactionManager實現或使用TransactionTemplate模板類
- 聲明式事務管理: 這意味着你的業務代碼將於事務管理分開,只用注解或基於XML配置來管理事務
自從有了基於aop的事務注解,事務的使用變得更簡單,相信大家都喜歡這貨。夠輕、夠好用,哪里需要事務只需要一個注解即可,可以在類或者是方法上使用它。確實它足夠好用,不過還是有其不足的地方,這個稍后再探討。我們先看下spring的事務傳播行為類型:
事務傳播行為類型
事務傳播行為類型 |
說明 |
PROPAGATION_REQUIRED |
如果當前沒有事務,就新建一個事務。如果已經存在一個事務中,加入到這個事務中。這是最常見的選擇。 |
PROPAGATION_REQUIRES_NEW |
如果當前沒有事務,就新建一個事務。如果當前存在事務,就把當前事務掛起,另建一個事務。 |
PROPAGATION_NESTED |
如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則執行與 PROPAGATION_REQUIRED 類似的操作。外套的事務異常可使內嵌事務回滾,反之不會。(底層的數據源必須基於 JDBC 3.0 ,並且實現者需要支持保存點事務機制) |
PROPAGATION_SUPPORTS |
支持當前事務,如果當前沒有事務,就以非事務方式執行。 |
PROPAGATION_NOT_SUPPORTED |
以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。 |
PROPAGATION_NEVER |
以非事務方式執行,如果當前存在事務,則拋出異常。 |
PROPAGATION_MANDATORY |
使用當前的事務,如果當前沒有事務,就拋出異常。 |
readOnly
事務屬性中的readOnly標志表示對應的事務應該被最優化為只讀事務。這是一個最優化提示 。在一些情況下,一些事務策略能夠起到顯著的最優化效果,例如在使用Object/Relational映射工具(如:Hibernate或TopLink)時避免dirty checking(試圖“刷新”)。
Timeout
在事務屬性中還有定義“timeout”值的選項,指定事務超時為幾秒。在JTA中,這將被簡單地傳遞到J2EE服務器的事務協調程序,並據此得到相應的解釋。
隔離級別的相關內容可以參考我的另一篇文章數據庫事務【隔離級別】
其中,spring的默認事務傳播方式和隔離級別分別是PROPAGATION_REQUIRED和ISOLATION_DEFAULT(使用數據庫默認的事務隔離級別),readOnly為false。
好了,上面基本介紹完了spring的相關事務配置參數,我來講講我在項目中碰到的一個奇怪的問題,不知你碰到沒,就是spring事務失效了。一般來說我們只要在類或相應的方法上設置了@Transactional,那便可以使用上spring事務了,我一開始也是這么認為的,我寫了兩個方法,serviceA.methodA沒加事務,serviceA.methodB加了事務注解,其中methodA調用了methodB,我讓Controller調用了serviceA.methodA,然后故意讓methodB拋出了異常,咦,結果並不如預期設想,事務沒有回滾。我一開始還以為是事務的傳播類型出了差錯,但是變換了始終沒有效果。之前一直都是這么使用的啊,也測試過。於是,我查了之前的代碼,發現跟這次寫的代碼的不同之處在於之前Controller調用的方法上有加了事務,或者是serviceA.methodA(事務)調用了serviceB.methodA(事務)。那我就在想會不會是aop攔截出的問題,於是上網找了許久,終於找到問題的原因,現在我們分析下:
Spring的AOP實現方式有兩種:1、Java代理方式;2、Cglib動態增強方式,這兩種方式在Spring中是可以無縫自由切換的。Java代理方式的優點是不依賴第三方jar包,缺點是不能代理類,只能代理接口。
Spring通過AopProxy接口,抽象了這兩種實現,實現了一致的AOP方式:
現在看來,這種抽象同樣帶了一個缺陷,那就是抹殺了Cglib能夠直接創建普通類的增強子類的能力,Spring相當於把Cglib動態生成的子類,當普通的代理類了,這也是為什么會創建兩個對象的原因。下圖顯示了Spring的AOP代理類的實際調用過程:
因此,從上面的分析可以看出,methodB沒有被AopProxy通知到,導致最終結果是:被Spring的AOP增強的類,在同一個類的內部方法調用時,其被調用方法上的增強通知將不起作用。
而這種結果,會造成的影響有:
1:內部調用時,被調用方法的事務聲明將不起作用
2:換句話說,你在某個方法上聲明它需要事務的時候,如果這個類還有其他開發者,你將不能保證這個方法真的會在事務環境中
3:再換句話說,Spring的事務傳播策略在內部方法調用時將不起作用。不管你希望某個方法需要單獨事務,是RequiresNew,還是要嵌套事務,要Nested,等等,統統不起作用。
4:不僅僅是事務通知,所有你自己利用Spring實現的AOP通知,都會受到同樣限制。。。。
知道了原因,問題就迎刃而解了,你可以使用以下方式來避免這個缺陷:
1、分開兩個方法調用,即serviceA.methodA不配置事務,調用配置上事務的serviceB.methodA
2、方法內使用編程式事務