內部調用事務失效
同一個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(); }