你知道@Transactional注解的失效场景吗?

在使用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引擎。

笔者只想到这几个场景,不知道还有没有其它的呢,大家可以在评论区留言!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值