­

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;      }         问题搞定。