Spring MVC Controller層事物註解不生效

最近在寫一個管理台頁面,是從頁面提交多個form到controller層的,這些form要麼都能提交成功,要麼都失敗。controller層需要進行事物處理,於是簡單的加了@Transactional註解,測試的時候,我故意把最後一個表單的某個欄位長度設置超長,後台肯定會報data too long exception。程式碼主體簡要如下:

@RestController  @RequestMapping("/chart")  @Transactional  public class ChartController {        @RequestMapping(value = "/addPie", method = RequestMethod.POST)      public ResponseEntity addPie(@RequestBody ReqPieDto pieDto) {           try {                weChatService.insertCharData(wxChart);                wxPieService.insertWxPie(pieData);                wxConditionService.insertWxCondition(conditions);           } catch (Exception e) {              rsp=new ResponseEntity("fail", HttpStatus.GONE);              logger.error("pie chart config fail:",e);           }           return rsp;      }  }        這個程式碼存在很明顯的問題,首先對Spring的事物機制沒有理解。默認spring事務只在發生未被捕獲的 runtimeexcetpion時才回滾,spring aop異常捕獲原理:被攔截的方法需顯式拋出異常,並不能經任何處理,這樣aop代理才能捕獲到方法的異常,才能進行回滾,默認情況下aop只捕獲runtimeexception的異常,但可以通過配置來捕獲特定的異常並回滾。換句話說在service的方法中不使用try catch 或者在catch中最後加上throw new runtimeexcetpion(),這樣程式異常時才能被aop捕獲進而回滾。      解決方案:       方案1.例如service層處理事務,那麼service中的方法中不做異常捕獲,或者在catch語句中最後增加throw new RuntimeException()語句,以便讓aop捕獲異常再去回滾,並且在service上層(webservice客戶端,view層action)要繼續捕獲這個異常並處理      方案2.在service層方法的catch語句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();語句,手動回滾,這樣上層就無需去處理異常。      那就修改程式碼,Controller層修改後程式碼如下:    @RestController  @RequestMapping("/chart")  @Transactional  public class ChartController {        @RequestMapping(value = "/addPie", method = RequestMethod.POST)      public ResponseEntity addPie(@RequestBody ReqPieDto pieDto) {           try {                weChatService.insertCharData(wxChart);                wxPieService.insertWxPie(pieData);                wxConditionService.insertWxCondition(conditions);           } catch (Exception e) {                rsp=new ResponseEntity("fail", HttpStatus.GONE);                logger.error("pie chart config fail:",e);                throw new SystemException("添加餅圖配置失敗");           }           return rsp;      }  }        Service層程式碼也要拋出異常:        public void insertWxCondition(WxConditions conditions){          try {              mapper.insertSelective(conditions);          } catch (Exception e) {              logger.error("insert into report config conditions data fail");              throw new SystemException("insert into report config conditions data fail", e);          }      }        這時候,事物是回滾了,但是頁面顯示的返回結果卻是這樣的:            到這裡,事物問題雖然解決了,但是頁面的返回資訊太不友好了。這是因為Controller方法拋出異常後,程式就中斷了,中斷後,直接把異常拋給前台頁面了。如此看來,在Contrller層進行這種事物處理的時候,既要保證事物的執行,又不要拋出異常、返回自定義消息給前台頁面,這二者不可兼得。那就只有一個辦法了,把3個service封裝到另外一個service層進行事物控制,然後拋出異常,程式碼如下:    public void insertPieCharData(ReqPieDto reqPieDto) {          try {              this.insertCharData(wxChart);              wxPieService.insertWxPie(pieData);              wxConditionService.insertWxCondition(conditions);              }            } catch (Exception e) {                logger.error("pie chart config fail:",e);              throw new SystemException("添加餅圖配置失敗");          }        }         然後Controller層去掉trasaction註解,否則異常資訊還是會被拋到前台頁面,在catch exception中處理異常,程式碼如下:        @RequestMapping(value = "/addPie", method = RequestMethod.POST)      public ResponseEntity addPie(@RequestBody ReqPieDto reqPieDto) {            logger.info("receive request pie config dto:{}",JsonUtil.toFullJson(reqPieDto));          ResponseEntity rsp=new ResponseEntity("SUCCESS", HttpStatus.OK);            try {              wxChartService.insertPieCharData(reqPieDto);          } catch (Exception e) {              rsp=new ResponseEntity("系統異常", HttpStatus.BAD_REQUEST);          }            logger.info("返回消息:{}",JsonUtil.toFullJson(rsp));          return rsp;      }         問題搞定。