事务注解不生效的问题
出现该问题的主要原因主要有两点:
- Spring的事务没有生效
- 出现异常时无法正常回滚
Spring事务不生效的情况
- @Transactional用在非public方法上
- 未通过代理手段调用事务方法
事务注解用在非public方法上 @Transactional private Access createRecords(AccessDto accessDto); 未通过代理调用的情况:Service正常注入Spring中,在调用方法时使用This调用事务方法,Spring不会注入this,所以无法使用事务。自己调用自己时可以使用Controller直接调用Service //AccessServiceImpl.class //this.AccessRecords(AccessDto accessDto);
出现异常时无法回滚的情况
- 未正确处理异常,事务生效也不一定能回滚
- 多次数据库操作,未配置事务传播
代码语言:javascript复制事务生效不能回滚的情况: try/catch包裹标记了@Transactional注解的方法,方法满足一定条件时才会回滚。 一定条件: ①:只有异常传播除了标记的@Transactional注解的方法,事务才会回滚。 ②:默认情况下,出现RunTimeException(非受检异常)或Error的时候,Spring才会回滚事务。 ③:catch的作用就是捕捉方法中的异常,使回滚不传播到外层事务,以免对其他事务产生影响。
public int createUserWrong1(String name) {
try {
this.createUserPrivate(new UserEntity(name));
} catch (Exception ex) {
log.error("create user failed because {}", ex.getMessage());
}
return userRepository.findByName(name).size();
}
代码语言:javascript复制
RunTimeException
是Exception的子类,Exception包裹了所有的异常,导致异常无法传播出去,导致无法回滚。 ①的解决方案 方案一、手动设置回滚:
//catch异常时:
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
代码语言:javascript复制方案二、使用@Transactional注解的属性
//抛出任何异常时均回滚
@Transactional(rollbackFor = Exception.class)
上文的方案在这种情况下显然是不适用的:
用户注册时在主表中新增数据,同时需要在子表中关联主表数据,现在业务要求,子表执行错误时回滚,子表不影响主表事务,也就是子表不能影响主流程,需要主表和子表不在同一个事务中。
解决方案:
@Transactional(propagation = Propagation.REQUIRES_NEW)
执行到当前方法时创建一个新的事务。
官方文档:
代码语言:javascript复制/**
* Create a new transaction, and suspend the current transaction if one exists.
* Analogous to the EJB transaction attribute of the same name.
* <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
* on all transaction managers. This in particular applies to
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* which requires the {@code javax.transaction.TransactionManager} to be
* made available to it (which is server-specific in standard Java EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManage
*/