通常,在項目中可能看到注解@Transactional(rollbackFor=Exception.class),如果類加了這個注解,那么這個類里面的方法拋出異常,就會回滾,數據庫里面的數據也會回滾(也可以細分顆粒度,到需要事務監控的方法上添加此注解而非類上)。
在@Transactional注解中如果不配置rollbackFor屬性,那么事物只會在遇到RuntimeException的時候才會回滾,加上rollbackFor=Exception.class,可以讓事物在遇到非運行時異常時也回滾
默認spring事務只在發生未被捕獲的 RuntimeExcetpion 時才回滾。也可以說spring會對unchecked異常進行事務回滾;如果是checked異常則不回滾。
一般將派生於Error或者RuntimeException的異常稱為unchecked異常,所有其他的異常成為checked異常。
那么我是不是可以理解為 不加注解,發生空指針而不捕獲時會發生回滾?(下面進行驗證)
PS: @Transactional注解 應當應用於service層實現類或實現類中的public方法上,且如果為只讀(查詢)方法無需添加注解。
PSS:着重測試注解屬性:rollbackFor :用於指定能夠觸發事務回滾的異常類型,可以指定多個異常類型,常見的runtime異常:空指針異常,數組越界異常
現將進行測試,測試的控制變量如下:
1.添加注解@Transactional(rollbackFor = Exception.class)
2.添加注解@Transactional(norollbackFor = Exception.class)
3.捕獲 runtime 異常
4.不捕獲 runtime 異常
好了,開測:
測試用例1:不添加@Transactional注解,測試異常為空指針異常,而捕獲的是runtime異常,結果是 無回滾,無異常拋出,數據順利更新。
測試用例2:不添加@Transactional注解,測試異常為空指針異常,而捕獲的是數組越界異常,結果是 無回滾,拋出空指針異常,數據可順利更新。
測試用例3:和測試用例2類似,但沒有使用try-catch去捕獲runtime異常,結果依舊是 沒有回滾。
測試用例4:添加@Transactional(rollbackFor = NullPointerException.class)注解,指定空指針類型異常,測試異常為空指針異常,而捕獲的是數組越界異常,結果是 發生回滾, 數據無更新。
測試用例5:添加@Transactional(rollbackFor = NullPointerException.class)注解,測試異常為空指針異常,而捕獲的是Exception異常(包含空指針異常),結果是 不發生回滾, 數據有更新。
測試用例6:添加@Transactional(norollbackFor = NullPointerException.class)注解,測試異常為空指針異常,而捕獲的ArrayIndexOutOfBoundsException
異常(包含空指針異常),結果是 不發生回滾, 數據有更新。
好了,以上列舉了6種情況,總結如下:
1.當沒有配置注解@Transactional時,spring默認是不開啟事物管理的。
2.配置注解后,回滾的前提是,try-catch捕獲情況符合注解中參數的規范,且方法用public修飾,要求mysql數據庫的引擎是支持事物的,比如InnoDB( 可以用show variables like 'default_storage_engine'語句來查看數據庫當前引擎)
3.配置注解后,如果catch中捕獲到了對應異常,那么即使配置注解也不會發生回滾。
3.再次聲明,unchecked exception–編譯器不要求強制處置的異常,可以理解為 java.lang.RuntimeException類及它的子類都是非受查異常,如錯誤的類型轉換異常:ClassCastException,下標越界異常:ArrayIndexOutOfBoundsException,空指針訪問異常:NullPointerException,除零溢出異常:ArithmeticException等;
4.注解屬性:
【關於事務嵌套】
有兩個事務方法,父方法中調用子方法,想實現子方法回滾而父方法不觸發回滾,可以設置如下:
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW) // 子方法觸發事務時會暫時掛起父事務;
【關於同一類中非事務方法A調用 事務方法B,事務失效問題】
// 因為 B方法被A調用,而事務需要切面實現,這時對B方法的切入失效,我們需要新建一個代理對象去調用方法B,這樣事務不會失效。(XxxxService為業務類對象) XxxxxxService self = (XxxxxxxService) AopContext.currentProxy();
需要依賴:
<!-- 切點表達式依賴 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> <scope>runtime</scope> </dependency>
附:
參考博客:https://www.cnblogs.com/clwydjgs/p/9317849.html
spring aop 異常捕獲原理:被攔截的方法需顯式拋出異常,並不能經任何處理,這樣aop代理才能捕獲到方法的異常,才能進行回滾,默認情況下aop只捕獲runtimeexception的異常,但可以通過配置來捕獲特定的異常並回滾。
在Spring FrameWork 的事務框架中推薦的事務回滾方法是,在當前執行的事務上下文中拋出一個異常。如果異常未被處理,當拋出異常調用堆棧的時候,Spring FrameWork 的事務框架代碼將捕獲任何未處理的異常,然后並決定是否將此事務標記為回滾。
在默認配置中,Spring FrameWork 的事務框架代碼只會將出現runtime, unchecked 異常的事務標記為回滾;也就是說事務中拋出的異常時RuntimeException或者是其子類,這樣事務才會回滾(默認情況下Error也會導致事務回滾)。在默認配置的情況下,所有的 checked 異常都不會引起事務回滾。
注:Unchecked Exception包括Error與RuntimeException. RuntimeException的所有子類也都屬於此類。另一類就是checked Exception。
Spring的事務管理默認是針對unchecked exception回滾,也就是默認對Error異常和RuntimeException異常以及其子類進行事務回滾,且必須對拋出異常,若使用try-catch對其異常捕獲則不會進行回滾!(Error異常和RuntimeException異常拋出時不需要方法調用throws或try-catch語句);
checked異常,checked異常必須由try-catch語句包含或者由方法throws拋出,且事務默認對checked異常不進行回滾。
在service層若不加@Transactional則spring默認是不開啟事物管理的。
從源碼來看,默認回滾的 類型確實是runtime異常和error