事务注解@Transactional又失效了!

简介: @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来保证线程安全的,事务和当前线程绑定。

相关文章
|
1月前
|
XML Java 关系型数据库
@Transactional注解的失效场景
@Transactional注解的失效场景
56 1
|
1月前
|
Java 数据库 Spring
Spring事务失效的场景详解
Spring事务失效的场景详解
33 0
|
29天前
|
Java 编译器 数据库
在事务注解@Transactional中指定rollbackFor
在事务注解@Transactional中指定rollbackFor
19 0
|
6月前
|
数据库
通过基于注解的声明式事务实现事务功能~2
通过基于注解的声明式事务实现事务功能~
|
2月前
|
Java 数据库 Spring
@Transactional 失效场景介绍
【2月更文挑战第5天】
66 1
@Transactional 失效场景介绍
|
4月前
|
存储 Java 数据库连接
spring事务失效的几种情况与原因
spring事务失效的几种情况与原因
67 0
|
6月前
|
XML Java 关系型数据库
通过基于注解的声明式事务实现事务功能~1
通过基于注解的声明式事务实现事务功能~
通过基于注解的声明式事务实现事务功能~1
|
11月前
|
Java 数据库 Spring
@Transactional 注解失效问题
@Transactional 注解失效问题
|
XML 缓存 Java
引起Spring事务失效的可能原因
引起Spring事务失效的可能原因
198 0
|
Java 数据库 Spring
Spring事务失效场景
如果@Transactional 没有特别指定,Spring 只会在遇到运行时异常RuntimeException或者error时进行回滚,而IOException等检查异常不会影响回滚。
242 0
Spring事务失效场景