IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 你知道@Transactional注解的失效场景吗? -> 正文阅读

[Java知识库]你知道@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引擎。

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

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-08 22:14:58  更:2022-03-08 22:16:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:45:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码