?般情况下,@Transactional要放在service层,并且只需要放到最外层的方法上就可以了。 那么controller层到底能不能使用@Transactional注解呢?答案是可以使用的。(但是还是尽量在service层使用)
其实在controller层使用 @Transactional 用法跟在service层使用是一样的。
①使用时代码尽量不要try…catch…
②如果真的要捕获异常,可以在catch语句中增加: TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 语句,手动回滚,这样上层就无需去处理异常。
就拿下面这个controller层接口说起,这个新增用户接口,需要先新增基础信息,再新增详细信息,在两个新增操作之间模拟异常。
第一种:这种情况,发生异常代码直接捕获,异常根本不会向上抛,事务不会进行回滚,这也是正常操作。
@PostMapping("/addUser")
@Transactional
public String addUser(@RequestBody User user) {
try {
userMapper.insert(user);
int i = 1 / 0;
UserDetail userDetail = new UserDetail();
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")
@Transactional
public String addUser(@RequestBody User user) {
try {
userMapper.insert(user);
int i = 1 / 0;
UserDetail userDetail = new UserDetail();
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")
@Transactional
public String addUser(@RequestBody User user) {
userMapper.insert(user);
int i = 1 / 0;
UserDetail userDetail = new UserDetail();
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捕获进而回滚。
|