Spring——事务的传播性
目录
REQUIRED
REQUIRES_NEW
NESTED
MANDATORY
事务的回滚规则
事务的传播性(Transaction Propagation)是Spring事务管理的一个重要概念,它定义了在多个事务方法相互调用时,事务应该如何传播。Spring框架提供了7种传播行为,这些行为在TransactionDefinition接口中定义。
-
PROPAGATION_REQUIRED(默认):
-
如果当前存在事务,则加入该事务。
-
如果当前没有事务,则创建一个新的事务。
-
-
PROPAGATION_SUPPORTS:
-
如果当前存在事务,则加入该事务。
-
如果当前没有事务,则以非事务的方式继续运行。
-
-
PROPAGATION_MANDATORY:
-
如果当前存在事务,则加入该事务。
-
如果当前没有事务,则抛出异常。
-
-
PROPAGATION_REQUIRES_NEW:
-
创建一个新的事务,如果当前存在事务,则把当前事务挂起。
-
新事务与原有事务相互独立,互不干扰。
-
-
PROPAGATION_NOT_SUPPORTED:
-
以非事务方式运行,如果当前存在事务,则把当前事务挂起。
-
这意味着该方法不会在事务中运行。
-
-
PROPAGATION_NEVER:
-
以非事务方式运行,如果当前存在事务,则抛出异常。
-
-
PROPAGATION_NESTED:
-
如果当前存在事务,则创建一个嵌套事务(嵌套事务是当前事务的一个子事务)。
-
如果当前没有事务,则行为与PROPAGATION_REQUIRED一样。
-
嵌套事务可以独立于外部事务进行提交或回滚。如果外部事务回滚,则嵌套事务也会回滚;而嵌套事务回滚不会影响外部事务,除非外部事务捕获了嵌套事务的异常并标记为回滚。
-
先准备两个service,在其中一个service的方法内去调用另外一个service的方法:设置传播性的方法是在@Transactional注解给propagation赋值。
AService:
public class UserService {@Autowiredprivate UserDao userDao;@Transactionalpublic void up(){User user = new User();user.setUid(1);user.setUsername("cc");userDao.update( user);}
}
BService:
@Service
public class UserService2 {@Autowiredprivate UserDao userDao;@Autowiredprivate UserService userService;@Transactionalpublic void up(){User user = new User();user.setUid(2);user.setUsername("aa");userDao.update(user);userService.up();}
}
REQUIRED
REQUIRED是spring事务默认的传播行为,如果当前存在事务,则加入该事务,如果没有事务,就会创建一个新的事务。
如该示例所示,在B中的up方法先进行一次修改操作,然后在A中的up方法内抛出异常。
启动之后,两条数据都没有修改,说明两个事务合并成一个回滚。
接下来换种方式,在B中调用A方法后抛出,看是否回滚。
结局依旧是回滚。
REQUIRES_NEW
该行为如果当前存在事务,则会把当前事务挂起。
将A事务传播性修改为REQUIRES_NEW,然后在不抛出异常的情况下先运行一遍:
结果发现程序似乎卡死了。。。
先捋一遍过程:B先开启事务B执行update语句,之后再调用A中的up方法,由于该传播行为下是两个独立的事务,所以开启事务A。
但是!这两个update的语句涉及的字段并没有索引,导致InnoDB使用的是表锁,事务B并没有提交,所以事务A卡在了update语句上。相关内容:MySQL——锁-CSDN博客
给username加上索引之后,InnoDB使用行锁,而这里操作的数据并不是同一行,所以数据成功被修改。
接下来测试该传播行为如何回滚:
在外面一层抛出异常
结果为,外面层成功回滚,而里面的事务提交。后面反过来测试一下:
发现两个事务都回滚了。当然如果里面的异常被处理了则不会回滚。
NESTED
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则等价于REQUIRED。
该事务行为就相当于外部事务的子事务,如果外部事务回滚,则子事务也会跟着回滚。里面的事务回滚的话,不会影响外部事务(前提是要处理异常)。
@Transactionalpublic void up(){User user = new User();user.setUsername("aa");user.setPassword("aaa");userDao.updateP(user);try{userService.up();}catch (Exception e){}}
MANDATORY
MANDATORY事务行为,如果当前存在事务就加入该事务,如果没有就抛出异常。
将内部事务行为改为MANDATORY,然后将外部的注解删除,尽管代码没有问题依旧会报错。
事务的回滚规则
Spring 声明式事务默认只对 “未受检异常” 回滚,具体包括:
RuntimeException
及其子类(如NullPointerException
、IndexOutOfBoundsException
);Error
及其子类(如OutOfMemoryError
)。
而对于 “受检异常”(需要显式try-catch
或throws
声明的异常,如IOException
、SQLException
),默认不会回滚,事务会正常提交。