之前遇到了一个事务错误,我认为还算是比较稀有和有意义的,现在有时间了正好写一下。下面是异常
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:526)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:518)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
at com.kq.highnet2.openApi.mobile.framework.base.loginInfo.LoginInfoAction$$EnhancerBySpringCGLIB$$87638463.login(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
这个错误的原因是子事务抛出异常并回滚,但是外层事务依然提交造成的
下面是错误代码
@PostMapping("/POST/Login")
@ResponseBody
@Transactional
public MobileWebApiBaseResult login(
@RequestParam(value="type",required = true)String type
,@RequestParam(value="LoginCode",required = true)String loginCode
,@RequestParam(value = "PhoneNumber",required = true)String mobile
,@RequestParam(value = "PassWord",required = true)String password
,@RequestParam(value = "localphone",required = false)String localPhone
,@RequestParam(value = "version",required = false)String version
) {
MobileWebApiBaseResult baseResult = new MobileWebApiBaseResult();
LoginInfoModel loginInfoModel = new LoginInfoModel();
//TOKEN值用UUID表示
String token = UUID.randomUUID().toString();
TransactionStatus s = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
boolean notNewAccessKey = false;
//1:取得参数并调用业务层的方法
BaseUserModel baseUser = baseUserRuntimeService.checkAppLogin(mobile, password, loginCode, localPhone, type, version);
//删除loginInfo
loginInfoService.loginOut(baseUser.getUserId());
loginInfoModel.setToken(token);
loginInfoModel.setUserId(baseUser.getUserId());
loginInfoModel.setLoginTime(new Date());
loginInfoModel.setDeviceType(type);
loginInfoService.login(loginInfoModel);
baseUserRuntimeService.updateLoginCode(token, mobile);
baseResult.setMsg("登录成功!");
List<Map> list = new ArrayList<>();
Map map = new HashMap<String, String>();
map.put("key", token);
map.put("RealName", baseUser.getRealName());
map.put("UserImage", baseUser.getUserImage());
list.add(map);
baseResult.setData(list);
transactionManager.commit(s);
}catch (Exception e) {
transactionManager.rollback(s);
logger.error(e.getMessage(),e);
baseLogService.saveSysLog("APP接口", "", e.getMessage(), "Login");
// LogUtils.addSysLog("APP接口", "", e.getMessage(), "Login");
baseResult.setCode("0");
baseResult.setMsg(e.getMessage());
}
return baseResult;
}
当时因为写手机接口,想直接调用service层方法,因为用了不同的service,所以需要在外层加上事务,但是发现@transactional注解加在controller层上不起作用,现在想想原因应该是我已经用try-catch捕捉了异常,所以我就使用transactionManager来实现编程事务,结果当service层报错以后就报了上面的错误。我一直以为是transactionManager层的问题,后来发现其实是@transactional注解忘了去掉,因为action层捕捉了异常@transactional注解的事务以为事务是正常的就提交了,于是就报错了。
总结,事务里面最好不要有try-catch,有异常直接抛到最外层,即使有try-catch,try-catch里面不能有事务存在