SpringBoot事务使用和回滚
Springboot中事务的使用:
-
启动类加上@EnableTransactionManagement注解,开启事务支持(其实默认是开启的)。
-
在使用事务的public(只有public支持事务)方法(或者类-相当于该类的所有public方法都使用)加上@Transactional注解。
在实际使用中一般是在service中使用
@Transactional
,那么对于controller->service流程中:
如果controller未开启事务,service中开始了事务,service成功执行,controller在之后的运行中出现异常(错误),不会自动回滚。
也就是说,只有在开启事务的方法中出现异常(默认只有非检测性异常才生效-RuntimeException )(错误-Error)才会自动回滚。
如果想要对抛出的任何异常都进行自动回滚(而不是只针对RuntimeException),只需要在使用@Transactional(rollbackFor = Exception.class)即可。
开启事务的方法中事务回滚的情况:
①未发现的异常,程序运行过程中自动抛出RuntimeException或者其子类,程序终止,自动回滚。
②使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();进行手动回滚。
③注意:如果在try-catch语句中对可能出现的异常(RuntimeException)进行了处理,没有再手动throw异常,spring认为该方法成功执行,不会进行回滚,此时需要调用②中方法进行手动回滚,如下图:
另外,如果try-catch语句在finally中进行了return操作,那么catch中手动抛出的异常也会被覆盖,同样不会自动回滚。
在这里插入代码片
//不会自动回滚
try{ throw new RuntimeException(); }catch(RuntimeException e){ e.printStackTrace(); }finally{ }
//会自动回滚
try{ throw new RuntimeException(); }catch(RuntimeException e){ e.printStackTrace(); throw new RuntimeException(); }finally{ }
springboot调用链中存在多个@Transactional的事务回滚
在Spring Boot中,@Transactional
注解是用来定义事务边界的主要手段。它可以应用于类或者方法级别,以声明性地控制事务。当在调用链中存在多个@Transactional
注解时,Spring的事务传播行为将决定这些事务是如何相互作用的。下面我将一步一步解释这个过程。
1. @Transactional
注解基础
@Transactional
注解告诉Spring,被注解的方法或类中的所有方法应该运行在一个事务上下文中。如果该方法或类中的操作失败,则事务可以被回滚,即取消所有操作,以保持数据的一致性。
如果@Transactional
中没有配置自定义回滚规则,则事务将在RuntimeException和Error上回滚,但不会在检查异常上回滚。
2. 事务传播行为(Propagation Behavior)
在调用链中,当一个@Transactional
方法调用另一个@Transactional
方法时,事务是如何管理的?答案在于事务的传播行为。Spring定义了几种传播行为:
REQUIRED
(默认):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。REQUIRES_NEW
:总是创建一个新的事务,并暂停当前的事务(如果存在)。SUPPORTS
:如果当前存在事务,则加入该事务;如果没有,则以非事务方式执行。NOT_SUPPORTED
:总是以非事务方式执行,并暂停当前的事务(如果存在)。MANDATORY
:如果当前存在事务,则加入该事务;如果没有,则抛出异常。NEVER
:总是以非事务方式执行,如果当前存在事务,则抛出异常。NESTED
:如果当前存在事务,则在嵌套事务内执行;如果没有,则表现如REQUIRED
。
3. 事务回滚
当在@Transactional
注解的方法中发生异常时,事务默认将回滚。Spring框架默认对运行时异常(RuntimeException
)和错误(Error
)触发回滚,而对检查型异常(Exception
的直接子类,除了RuntimeException
)不回滚,除非明确指定。
4. 调用链中的事务回滚
- 同一事务内的回滚:如果所有方法都在同一个事务上下文中(例如,都使用
REQUIRED
传播行为,且第一个方法启动了事务),那么任何方法中的失败都会导致整个事务回滚。 - 独立事务的回滚:如果某个方法配置为
REQUIRES_NEW
,它将在自己的事务中运行。如果这个独立事务失败了,它会回滚,但不会影响其他事务的执行或回滚。 - 嵌套事务的回滚:使用
NESTED
传播行为时,外部事务可以成功提交,而嵌套事务失败时只回滚嵌套事务的更改。
独立事务和嵌套事务
独立事务(
REQUIRES_NEW
)回滚特性:
- 当方法B以
REQUIRES_NEW
传播行为被调用时,无论外部事务A的状态如何,B都会启动一个新的事务。- 如果B事务失败,仅B事务会回滚。这个回滚操作不会影响到事务A的状态;A可以独立于B继续执行,并根据自己的逻辑成功提交或回滚。
- B的回滚仅限于B事务内的操作。即使B失败并回滚,A事务可以无视B的失败,根据自己的业务逻辑决定是否成功提交。
- 独立事务(REQUIRES_NEW),如果B成功了(即B事务已经提交),然后A失败了,B是不会回滚的。这是因为B事务已经独立于A事务成功提交,其操作结果已经被永久保存。
使用场景:
- 当你需要保证某个业务操作完全独立于当前的事务环境时,例如,记录日志、发送通知等操作,这些操作即使失败也不应该影响主业务流程的执行。
嵌套事务(
NESTED
)回滚特性:
- 嵌套事务依赖于外部事务A的存在。当B以
NESTED
传播行为被调用时,它在A中创建一个保存点,作为嵌套事务执行。- 如果B事务失败,它会回滚到B开始执行时的保存点,这个回滚只会撤销B事务内的更改,不影响A事务中B之外的操作。
- 如果外部事务A决定回滚,那么包括B在内的所有更改都会被撤销,即整个嵌套事务会被回滚。
使用场景:
- 当你的业务逻辑中包含了一系列的步骤,这些步骤需要作为整体被成功提交,但是在这个过程中某些步骤可能需要单独地进行回滚和重试时。嵌套事务允许这种细粒度的控制。
核心区别
- 事务依赖性:独立事务与外部事务完全独立,而嵌套事务依赖于外部事务。
- 回滚点:独立事务的回滚不影响其他事务,嵌套事务的回滚则是回到其在外部事务中的保存点,但不影响外部事务的其他部分。
- 使用场景:独立事务适用于需要完全隔离的操作,嵌套事务适用于需要事务内部细粒度控制的场景。