⼀般情况下,@Transactional要放在service层,并且只需要放到最外层的方法上就可以了。
那么controller层到底能不能使用@Transactional注解呢?答案是可以使用的。(但是还是尽量在service层使用)其实在controller层使用 @Transactional 用法跟在service层使用是一样的。
①使用时代码尽量不要try…catch…
②如果真的要捕获异常,可以在catch语句中增加:
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 语句,手动回滚,这样上层就无需去处理异常。
就拿下面这个controller层接口说起,这个新增用户接口,需要先新增基础信息,再新增详细信息,在两个新增操作之间模拟异常。
第一种:这种情况,发生异常代码直接捕获,异常根本不会向上抛,事务不会进行回滚,这也是正常操作。
@PostMapping("/addUser")@TransactionalpublicStringaddUser(@RequestBodyUser user){try{
userMapper.insert(user);//代码异常int i =1/0;UserDetail userDetail =newUserDetail();
userDetail.setUserId(user.getId());
userDetail.setQq(String.valueOf(System.currentTimeMillis()).substring(0,10));
userDetailMapper.insert(userDetail);
user.setUserDetail(userDetail);}catch(Exception e){
log.error("insert false:"+ e.getMessage());return"fail";}return"success";}
第二种:要是还想像第一种这种写法,还想做事务回滚,只能在catch语句中添加一行TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();进行手动回滚操作,如下:
@PostMapping("/addUser")@TransactionalpublicStringaddUser(@RequestBodyUser user){try{
userMapper.insert(user);//代码异常int i =1/0;UserDetail userDetail =newUserDetail();
userDetail.setUserId(user.getId());
userDetail.setQq(String.valueOf(System.currentTimeMillis()).substring(0,10));
userDetailMapper.insert(userDetail);
user.setUserDetail(userDetail);}catch(Exception e){
log.error("insert false:"+ e.getMessage());// 手动回滚事务 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();return"fail";}return"success";}
第三种:就是代码异常不管,可以正常事务回滚,如下:
@PostMapping("/addUser")@TransactionalpublicStringaddUser(@RequestBodyUser user){
userMapper.insert(user);//代码异常int i =1/0;UserDetail userDetail =newUserDetail();
userDetail.setUserId(user.getId());
userDetail.setQq(String.valueOf(System.currentTimeMillis()).substring(0,10));
userDetailMapper.insert(userDetail);
user.setUserDetail(userDetail);return"success";}
原因:
默认spring事务只在发生未被捕获的 RuntimeException 时才回滚。
spring aop 异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获 RuntimeException 的异常,但可以通过配置来捕获特定的异常并回滚,换句话说在service层的方法中不使用try catch 或者在catch中加上throw new RuntimeExcetpion(),这样程序异常时才能被aop捕获进而回滚。
版权归原作者 Likelong~ 所有, 如有侵权,请联系我们删除。