Spring事务存在原因,@Transactional
事务注解简介、注解属性意义、使用场景、不生效case、正常使用、事务传播类型等。
Spring事务存在原因
聊聊:为什么要有 Spring 事务?
为了解释清楚这个问题,我们举个简单的例子:银行里树哥要给小黑转 1000 块钱,这时候会有两个必要的操作:
- 将树哥的账户余额减少 1000 元。
- 将小黑的账户余额增加 1000 元。
这两个操作,要么一起都完成,要么都不完成。如果其中某个成功,另外一个失败,那么就会出现严重的问题。而我们要保证这个操作的原子性,就必须通过 Spring 事务来完成,这就是 Spring 事务存在的原因。
如果你深入了解过 MySQL 事务,那么你应该知道:MySQL 默认情况下,对于所有的单条语句都作为一个单独的事务来执行。我们要使用 MySQL 事务的时候,可以通过手动提交事务来控制事务范围。Spring 事务的本质,其实就是通过 Spring AOP 切面技术,在合适的地方开启事务,接着在合适的地方提交事务或回滚事务,从而实现了业务编程层面的事务操作。
@Transactional简介
@Transactional
是 spring 中声明式事务管理的注解配置方式。@Transactional
注解可以帮助我们把事务开启、提交或者回滚的操作,通过 aop 的方式进行管理。通过 @Transactional
注解就能让 spring 管理事务,免去了重复的事务管理逻辑,减少对业务代码的侵入,使开发人员能够专注于业务层面开发。
spring 定义了以 @Transactional 注解为植入点的切点,在 spring 的 bean 的初始化过程中,就需要对实例化的 bean 进行代理,并且生成代理对象。生成代理对象的代理逻辑中,进行方法调用时,需要先获取切面逻辑,@Transactional 注解的切面逻辑类似于 @Around,在 spring 中是实现一种类似代理逻辑。
@Transactional注解属性
属性名 | 说明 |
---|---|
name | 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。 |
propagation | 事务的传播行为,默认值为 REQUIRED。 |
isolation | 事务的隔离度,默认值采用 DEFAULT。 |
timeout | 事务的超时时间,默认值为 - 1。如果超过该时间限制但事务还没有完成,则自动回滚事务。 |
read-only | 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。 |
rollback-for | 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。 |
no-rollback- for | 抛出 no-rollback-for 指定的异常类型,不回滚事务。 |
@Transactional使用场景
- 数据库数据批处理
- 在方法上使用 @Transactional 注解,无异常时正常提交,有异常时数据回滚
- 在类上使用 @Transactional 注解,对整个类的方法事务起作用。无异常时正常提交,有异常时数据回滚
事务不生效case
主要讲解 5 种事务不生效的 Case:
- 类内部访问:A 类的 a1 方法没有标注 @Transactional,a2 方法标注 @Transactional,在 a1 里面调用 a2;(类内部调用,不会通过代理方式访问。)(但, @Transactional 注解作用在类上时,事务起作用)
- 私有方法:将 @Transactional 注解标注在非 public 方法上;
- 异常不匹配:@Transactional 未设置 rollbackFor 属性,方法返回 Exception 等异常;
- 多线程:主线程和子线程的调用,线程抛出异常。(给出两个不同的姿势,一个是子线程抛异常,主线程 ok;一个是子线程 ok,主线程抛异常。)
- 将异常信息使用 try-catch 包裹,异常被处理,@Transactional 注解不起作用
事务正常使用
DB操作接口
1 | // 提供的接口 |
基础测试代码
1 |
|
事务传播类型
事务传播类型 | 特性 |
---|---|
REQUIRED | 当前方法存在事务时,子方法加入该事务。此时父子方法共用一个事务,无论父子方法哪个发生异常回滚,整个事务都回滚。即使父方法捕捉了异常,也是会回滚。而当前方法不存在事务时,子方法新建一个事务。 |
REQUIRES_NEW | 无论当前方法是否存在事务,子方法都新建一个事务。此时父子方法的事务时独立的,它们都不会相互影响。但父方法需要注意子方法抛出的异常,避免因子方法抛出异常,而导致父方法回滚。 |
NESTED | 当前方法存在事务时,子方法加入在嵌套事务执行。当父方法事务回滚时,子方法事务也跟着回滚。当子方法事务发送回滚时,父事务是否回滚取决于是否捕捉了异常。如果捕捉了异常,那么就不回滚,否则回滚。 |