1、事务方法内部捕捉了异常,没有抛出新的异常,导致事务操作不会进行回滚。
代码示例
@Override
@Transactional
public ResponseTemplate add(String grantUser){
AuthEntity authEntity = JSON.parseObject(grantUser, AuthEntity.class);
try {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd HH:mm");
Date start_due = format.parse(authEntity.getStartTime());
Date over_due = format.parse(authEntity.getStartTime());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String startDue = dateFormat.format(start_due);
String overDue = dateFormat.format(over_due);
AgentPersonalDto personalDto = new AgentPersonalDto();
personalDto.setPerId(authEntity.getGrantorEmpNo());
personalDto.setFactor(authEntity.getGranteeEmpNo());
personalDto.setStartdue(startDue);
personalDto.setOverdue(overDue);
personalDto.setFactorWfoid(0);
personalDto.setProId("js");
JSONObject jsonObject = null;
jsonObject = agentPersonalService.updateFormData(personalDto);
AgentPersonalDto agentPersonalDto = (AgentPersonalDto) jsonObject.get("form");
authEntity.setCfgPersonalId(agentPersonalDto.getId());
int i = 1/0;
goOutAuthorizationMapper.insert(authEntity);
} catch (Exception e) {
e.printStackTrace();
}
return ResSuccessTemplate.builder().data(authEntity.toString()).build();
}
测试代码
@Test
public void test(){
String grantUser ="{dataId:'11a8bc95-7872-419c-8e50-78a21930b2e1', grantorName: '张三', grantorId: 'zhangsan', grantorEmpNo: '23000000', grantorDeptCode:'00230057000000000000', grantorDeptName:'办公室',startTime: '20170705 18:00', endTime: '20170706 18:00', granteeName: '李四', granteeId: 'lisi', granteeEmpNo: '23000001',granteeDeptCode:'00230057000000000000', granteeDeptName:'办公室',systemId:'OA'}";
ResponseTemplate add = null;
try {
add = goOutAuthorizationService.add(grantUser);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("----------------"+add);
}
运行测试用例发现,虽然抛出异常,但是异常被捕捉了,没有抛出到方法外,所以goOutAuthorizationService.add(grantUser)操作并没有回滚。
解决方案
@Transactional(rollbackFor = Exception.class)
事务方法内部捕捉了异常,没有抛出新的异常,导致事务操作不会进行回滚。
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
//开启事务
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
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
//异常时,在catch逻辑中回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
//提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
//....................
}
}
如上所述:
事务想要回滚,必须能够在这里捕捉到异常才行,如果异常中途被捕捉掉,那么事务将不会回滚。
@Transactional注解指定了 rollbackFor为某个具体的异常类型,则最终需要保证异常时对外抛出相匹配的异常类型,就可以触发事务处理逻辑。
2、Transactional注解标注方法修饰符为非public时,@Transactional注解会不起作用。
@Test
@Transactional
void test(){
String grantUser ="{dataId:'11a8bc95-7872-419c-8e50-78a21930b2e1', grantorName: '张三', grantorId: 'zhangsan', grantorEmpNo: '23000000', grantorDeptCode:'00230057000000000000', grantorDeptName:'办公室',startTime: '20170705 18:00', endTime: '20170706 18:00', granteeName: '李四', granteeId: 'lisi', granteeEmpNo: '23000001',granteeDeptCode:'00230057000000000000', granteeDeptName:'办公室',systemId:'OA'}";
ResponseTemplate add = null;
try {
add = goOutAuthorizationService.add(grantUser);
int i = 1/0;
add = goOutAuthorizationService.add(grantUser);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("----------------"+add);
}
@Transactional注解标注方法修饰符为非public时,@Transactional注解将会不起作用。@Transactional是基于动态代理实现的,@Transactional注解实现原理中分析了实现方法,在bean初始化过程中,对含有@Transactional标注的bean实例创建代理对象,这里就存在一个spring扫描@Transactional注解信息的过程,标注@Transactional的方法如果修饰符不是public,那么就默认方法的@Transactional信息为空,那么将不会对bean进行代理对象创建或者不会对方法进行代理调用
3、在类内部调用调用类内部@Transactional标注的方法。这种情况下也会导致事务不开启。
@Override
@Transactional
public ResponseTemplate add(String grantUser) throws Exception {
AuthEntity authEntity = JSON.parseObject(grantUser, AuthEntity.class);
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd HH:mm");
Date start_due = format.parse(authEntity.getStartTime());
Date over_due = format.parse(authEntity.getStartTime());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String startDue = dateFormat.format(start_due);
String overDue = dateFormat.format(over_due);
AgentPersonalDto personalDto = new AgentPersonalDto();
personalDto.setPerId(authEntity.getGrantorEmpNo());
personalDto.setFactor(authEntity.getGranteeEmpNo());
personalDto.setStartdue(startDue);
personalDto.setOverdue(overDue);
personalDto.setFactorWfoid(0);
personalDto.setProId("js");
JSONObject jsonObject = null;
//创建外出授权
jsonObject = agentPersonalService.updateFormData(personalDto);
AgentPersonalDto agentPersonalDto = (AgentPersonalDto) jsonObject.get("form");
authEntity.setCfgPersonalId(agentPersonalDto.getId());
int i = 1 / 0;
//插入外出授权记录
goOutAuthorizationMapper.insert(authEntity);
return ResSuccessTemplate.builder().data(authEntity.toString()).build();
}
@Override
public ResponseTemplate delete() throws Exception {
return this.add("");
}
既然事务管理是基于动态代理对象的代理逻辑实现的,那么如果在类内部调用类内部的事务方法,这个调用事务方法的过程并不是通过代理对象来调用的,而是直接通过this对象来调用方法,绕过的代理对象,肯定就是没有代理逻辑了。
4、Spring事务控制,配置手动回滚
try{
//出现异常
} catch (Exception e) {
e.printStackTrace();
//设置手动回滚
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
//此时return语句能够执行
return xxx;
当我们需要在事务控制的service层类中使用try catch 去捕获异常后,就会使事务控制失效,因为该类的异常并没有抛出,就不是触发事务管理机制。怎样才能即使用try catch去捕获异常,而又让出现异常后spring回滚呢,这里就要用到
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();