關於controller的異常處理及service層的事務控制方式
controller異常處理及service層的事務控制
最近寫代碼涉及到一些事務,上午終於把代碼給理順瞭,之前不太清楚在哪裡做異常處理,導致代碼遍地try-catch,相當難看。
還是基於controller-service-dao三層來寫代碼,從入口開始,controller層的方法對應的是某個url,面向的是應用人員,應該返回他們能讀懂的信息,所以controller必須做異常處理,一般來說會有統一的異常處理方法;
service層面向的是controller,service層中的某些方法,必須保證其事務,所以在service層進行事務控制是相當必要的,對於多條sql進行事務控制,如果某個sql執行失敗,那麼應當對已經執行的sql語句進行回滾;
dao層更多是單一的sql語句,沒有必要進行事務控制,因為事務開銷並不便宜(官方原話);
基於以上三點,回頭再思考關於異常的處理,一般情況應該把異常網上拋,一直拋到最終處理的那一層,所以對於dao層和service其實是沒有必要進行try-catch的,直接往上拋異常就可以。
與之對應的,是spring的事務配置,默認情況下,spring隻對運行時異常進行回滾,如果在dao層處理瞭異常,那麼需要進行額外的配置,spring才會對異常進行回滾,常用的配置是@Transactional(rollbackFor=Exception.class)
順便提一個java知識點,關於try-catch-finally中,finally的作用,finally設計之初就是為瞭關閉資源,如果在finally中使用return語句,會覆蓋try或者catch的返回值,最常見的就是覆蓋異常,即便catch往上拋瞭異常,也會被覆蓋,返回finally中return語句的返回值。
controller層Exception異常事務回滾失效問題
Spring的@Transactional源碼中寫道
By default, a transaction will be rolling back on {@link RuntimeException}and {@link Error} but not on checked exceptions (business exceptions).
默認情況下,如果在事務中拋出瞭未檢查異常(繼承自 RuntimeException 的異常)或者 Error,則 Spring 將回滾事務;除此之外,Spring 不會回滾事務。
測試①
默認spring事務隻在發生未被捕獲的 RuntimeException 時才回滾。
// 測試回滾成功案例,基於IllegalArgumentException(RuntimeException)實現回滾 @GetMapping("/testSuccess") @Transactional// 如果不加,就不會回滾 public R testSuccess(@RequestParam("type") Integer type){ eduTeacherService.removeById("2"); if (type == 1){ throw new IllegalArgumentException("測試回滾成功案例!"); } eduTeacherService.removeById("3"); return R.ok(); }
測試②
Exception異常,事務回滾失敗;
// 測試回滾失敗案例,基於Exception實現回滾; @GetMapping("/testFail") @Transactional public R testFail(@RequestParam("type") Integer type) { try { eduTeacherService.removeById("2"); if (type == 1){ throw new Exception("測試回滾失敗案例!"); } eduTeacherService.removeById("3"); } catch (Exception e) { e.printStackTrace(); } return R.ok(); }
測試③
用rollbackFor解決Exception不進行事務回滾
rollbackFor = Exception.class + throws Exception
@GetMapping("/testFailRollbackFor") // 配置rollbackFor @Transactional(propagation= Propagation.REQUIRED,rollbackFor = Exception.class) public R testFailRollbackFor(@RequestParam("type") Integer type) throws Exception { eduTeacherService.removeById("2"); if (type == 1){ throw new Exception("測試回滾失敗rollbackFor成功案例!"); } eduTeacherService.removeById("3"); return R.ok(); }
測試④
手動回滾解決Exception不進行事務回滾
catch: TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
@GetMapping("/testSuccessByHand") @Transactional public R testSuccessByHand(@RequestParam("type") Integer type) { try { eduTeacherService.removeById("2"); if (type == 1){ throw new Exception("測試回滾失敗案例!"); } eduTeacherService.removeById("3"); } catch (Exception e) { e.printStackTrace(); //手動回滾,如果sql2()拋瞭異常,sql1()會回滾,不影響事物正常執行 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } return R.ok(); }
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- spring 中事務註解@Transactional與trycatch的使用
- 使用@Transactional 設置嵌套事務不回滾
- Spring事務@Transactional註解四種不生效案例場景分析
- Spring中@Transactional(rollbackFor=Exception.class)屬性用法介紹
- spring在service層的方法報錯事務不會回滾的解決