在使用Spring的时候,进行事务管理变得相当简单:只要在方法上加上@Transactional就可以了,Spring就帮我们做了事务的开启、提交和回滚等操作,甚至我一度认为@Transactional就是等于Spring事务,只要是见到有数据库操作的方法,默认的统统加上此注解,自以为是的就万事大吉了。你是不是也有与我相同的经历呢:)
其实,@Transactional也不是在任何的场景下都有效的,有时候会莫名的失效,在介绍之前呢,我们先来认识一下。
1、@Transactional注解可以用在哪些地方呢?
作用于类:表示所有public方法都配置相同的事务信息。
作用于方法:代表方法的事务信息,其会覆盖类的事务哦!
作用于接口:这种方法极力不推荐,因为一旦使用cglib,注解会失效。
例如以下示例:
@Service
@Slf4j
public class UserHelper extends ServiceImpl<UserMapper, UserDO> {
@Transactional(rollbackFor = Exception.class)
public Long createNewUser(RegisterRequest request) {
UserDO userDO = new UserDO();
userDO.setLoginName(request.getLoginName());
userDO.setLoginPwd(request.getLoginPwd());
userDO.setCreateTime(new Date());
userDO.setUpdateTime(new Date());
this.save(userDO);
//这里还可以保存userinfo扩展信息,双表操作,需要事务
return userDO.getId();
}
}
2、@Transactional注解还有哪些属性呢?
属性 | 描述 | 备注 | propagation | 事务的传播行为,默认值为 Propagation.REQUIRED:如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。 | 还有Propagation.SUPPORTS、Propagation.MANDATORY等 | isolation | 事务的隔离级别,默认值为 Isolation.DEFAULT,代表使用数据库默认的隔离级别 | 还有 Isolation.READ_UNCOMMITTED、Isolation.READ_COMMITTED等 | timeout | 事务的超时时间,默认-1 | 如果超过时间事务还没有执行完毕,则自动回滚事务 | readOnly | 只读事务,默认值为false | | rollbackFor | 用于指定触发事务回滚的异常类型 | 如上例,Spring默认是运行时异常才会回滚 | noRollbackFor | 用于指定不触发事务回滚的异常类型 | |
接下来,我们一起看看@Transactional失效的场景。
1、作用在非public方法上会失效
原因是在使用Spring AOP 代理时,会间接调用 AbstractFallbackTransactionAttributeSource的方法computeTransactionAttribute获取事务信息,如果是非public就直接返回了,如下源码:
@Nullable
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
if (this.allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
} else {
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
TransactionAttribute txAttr = this.findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
} else {
txAttr = this.findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
} else {
if (specificMethod != method) {
txAttr = this.findTransactionAttribute(method);
if (txAttr != null) {
return txAttr;
}
txAttr = this.findTransactionAttribute(method.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
}
return null;
}
}
}
}
2、propagation属性配置错误
TransactionDefinition.PROPAGATION_SUPPORTS:有没有事务无所谓
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:非事务方式执行
TransactionDefinition.PROPAGATION_NEVER:有事务抛异常
3、rollbackFor设置错误
Spring默认抛出了未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务;其他异常不会触发回滚事务。若需要在特定异常下回滚,则需要指定,比如第一个示例。
4、在同一个类中,方法调用
这个尤其被大家不熟悉,红色标出。
@Transactional(rollbackFor = Exception.class)
public Long createNewUser(RegisterRequest request) {
UserDO userDO = new UserDO();
userDO.setLoginName(request.getLoginName());
userDO.setLoginPwd(request.getLoginPwd());
userDO.setCreateTime(new Date());
userDO.setUpdateTime(new Date());
this.save(userDO);
//这里还可以保存userinfo扩展信息,双表操作,需要事务
addUserInfo();
return userDO.getId();
}
@Transactional(rollbackFor = Exception.class)
public void addUserInfo() {
//.....
}
原因是什么,大家可以想一想,我们下一章来分析:)
5、异常被catch给吃掉了
@Transactional(rollbackFor = Exception.class)
public Long createNewUser(RegisterRequest request) {
try {
UserDO userDO = new UserDO();
userDO.setLoginName(request.getLoginName());
userDO.setLoginPwd(request.getLoginPwd());
userDO.setCreateTime(new Date());
userDO.setUpdateTime(new Date());
this.save(userDO);
return userDO.getId();
} catch (Exception e) {
log.error("异常了",e);
}
return null;
}
6、数据库底层不支持事务,比如mysql的myisam引擎。
笔者只想到这几个场景,不知道还有没有其它的呢,大家可以在评论区留言!
|