Spring事务注解@Transactional又失效了!

举报
LoneWalker、 发表于 2023/08/15 14:47:21 2023/08/15
【摘要】 Spring事务注解@Transactional又失效了!

1、相关基本知识

1.1事务的传播方式

//如果有事务, 那么加入事务, 没有的话新建一个(默认)
@Transactional(propagation=Propagation.REQUIRED)
//容器不为这个方法开启事务 
@Transactional(propagation=Propagation.NOT_SUPPORTED)
//不管是否存在事务, 都创建一个新的事务, 原来的挂起, 新的执行完毕, 继续执行老的事务 
@Transactional(propagation=Propagation.REQUIRES_NEW) 
//必须在一个已有的事务中执行, 否则抛出异常
@Transactional(propagation=Propagation.MANDATORY) 
//必须在一个没有的事务中执行, 否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.NEVER) 
//如果其他bean调用这个方法, 在其他bean中声明事务, 那就用事务, 如果其他bean没有声明事务, 那就不用事务
@Transactional(propagation=Propagation.SUPPORTS) 

1.2事务的隔离级别

// 读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
// 读取已提交数据(会出现不可重复读和幻读) Oracle默认
@Transactional(isolation = Isolation.READ_COMMITTED)
// 可重复读(会出现幻读) MySQL默认
@Transactional(isolation = Isolation.REPEATABLE_READ)
// 串行化
@Transactional(isolation = Isolation.SERIALIZABLE)

ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.

ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读

ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生

ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。

ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

1.3其他属性

  • timeout 事务的超时时间,默认值为 -1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
  • readOnly 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
  • rollbackFor 用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。
  • noRollbackFor 抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。

2、事务失效场景

2.1未被Spring管理

事务方法所在类并没有被Spring管理,则Spring事务会失效。这种对于Springboot项目其实不会的,你把@Service注释掉,Controller中注入UserService是会报错的。

2.2方法不能被重写

这里的意思是带@Transactional注释的方法必须是可重写的,那我们知道private、static、final修饰的方法均不能被重写。

2.3同一个类中方法相互调用

这里我们在同一个类中方法A调用了方法B,在方法B上使用@Transactional,此时方法B中出现异常,我们是希望他能回滚的。

可惜并没有回滚事务

声明式事务实现原理是面向切面编程,通过cglib创建代理proxy,当我们访问带 @Transactional方法,如果通过spring容器获取bean,实际访问的是代理对象,代理对象已经在带 @Transactional方法前后增加了事务相关的逻辑。而当调用带 @Transactional方法的调用方是同类方法时,调用的是this对象的方法,没有通过spring容器获取bean,就无法访问到代理对象,事务也就没有生效。

我们把@Transactional注解放到方法A上就可以了。此时即便方法B是private修饰的也无所谓

2.4异常被catch了

基于2.3的例子,假如把方法B中的异常catch了却没有抛出异常,事务也是不会回滚的。

解决办法:通常我们会在Springboot项目中自定义一个异常,在catch语句块中抛出自定义异常即可。

2.5rollbackFor使用不当

rollbackFor默认值为UncheckedException,包括了RuntimeException和Error。

在使用@Transactional注解时不指定rollbackFor,Exception及其子类都不会触发回滚。

继承自Runtime Exception或 Error 的是非检查型异常,而继承自 Exception 的则是检查型异常。

2.6数据库引擎不支持事务

比如MySQL数据库选用MyISAM存储引擎,而MyISAM存储引擎本身不支持事务,事务肯定不会生效。

2.7多线程问题

如果你去面试,面试官问你多线程事务如何回滚,你要是回答用@Transactional注解,就可以直接回去了。Spring的事务是通过ThreadLocal来保证线程安全的,事务和当前线程绑定。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。