Spring事务 内部调用事务失效、异常回滚
内部调用事务失效
同一个service内,如果service调用的主方法上没有加事务注解,主方法的调用了该service的另一个有事务注解的方法,这个注解不会生效。
主要原因在于事务是通过AOP实现的,代理对象调用的方法上有事务注解,事务才会生效。
在同一个Service类中,只有由service代理类直接调用的方法能够被增强,调用类内部的时候对象不再是代理对象而是this即目标对象本身,另一个方法不能够再被增强,所以另一个方法的事务不能生效
如果主方法又不想加事务,另一个方法又想能够使用事务,就需要获取到这个代理对象来调用加事务的方法
有以下几种办法:
1.通过Spring上下文获取指定代理对象
@Autowired private ApplicationContext applicationContext; @Override @Transactional public Integer saveUserInfo(UserInfo userInfoParam){ UserInfo userInfo = userInfoRepository.save(userInfoParam); if (true) { throw new RuntimeException(); } return userInfo.getId(); } @Override public void test(UserInfo userInfoParam) { IUserInfoService userInfoService = applicationContext.getBean(IUserInfoService.class); //调用该类加事务注解的方法 userInfoService.saveUserInfo(userInfoParam); }
2.通过Aop上下文获取当前代理对象
需要开启aop的exposeProxy属性,暴露代理对象:@EnableAspectJAutoProxy(exposeProxy = true)
使用AopContext.currentProxy()获取当前代理对象
@Override @Transactional public Integer saveUserInfo(UserInfo userInfoParam){ UserInfo userInfo = userInfoRepository.save(userInfoParam); if (true) { throw new RuntimeException(); } return userInfo.getId(); } @Override public void test(UserInfo userInfoParam) { IUserInfoService userInfoService = (IUserInfoService) AopContext.currentProxy(); //调用该类加事务注解的方法 userInfoService.saveUserInfo(userInfoParam); }
3.直接通过@Autowired注入
@Autowired private IUserInfoService userInfoService;
异常回滚
使用@Transaction注解,默认只会回滚对 RuntimeException 和 Error 类型的异常回滚
TransactionAspectSupport管理事务切面,在增强的方法中如果捕获异常进入completeTransactionAfterThrowing()方法
try { // This is an around advice: Invoke the next interceptor in the chain. // This will normally result in a target object being invoked. retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // target invocation exception completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); }
根据rollbackOn()方法判断是否回滚,默认情况下只有捕获的异常是RuntimeException、Error类型的才会返回ture
@Override public boolean rollbackOn(Throwable ex) { return (ex instanceof RuntimeException || ex instanceof Error); }
可以设置@Transaction注解的属性值,控制异常回滚:
rollbackFor
rollbackForClassName
noRollbackFor
noRollbackForClassName
例子:
下面代码会回滚抛出的Exception类型及其子类的异常,但不会回滚Error异常
@Override @Transactional(rollbackFor = Exception.class) //如果要捕获所有异常应该写成 rollbackFor = Throwable.class public Integer saveUserInfo(UserInfo userInfoParam){ UserInfo userInfo = userInfoRepository.save(userInfoParam); if (true) { throw new Error(); } return userInfo.getId(); }
下面代码会回滚除 InterruptedException、ClassNotFoundException 以外的异常
@Override @Transactional(noRollbackForClassName = {"InterruptedException","ClassNotFoundException"}) public Integer saveUserInfo(UserInfo userInfoParam) throws InterruptedException, ClassNotFoundException { UserInfo userInfo = userInfoRepository.save(userInfoParam); if (true) { throw new ClassNotFoundException(); } return userInfo.getId(); }