SpringBoot事务回滚
在开发过程中有时会遇到,多次操作数据时,某次操作出现问题导致程序终止,但是之前的数据已经操作,需要回滚使数据复原。基于这种情况,SpringBoot提供了一个注解@Transactional,帮助回滚事务。
原理
在应用系统调用声明了@Transactional的目标方法时,Spring Framework 默认使用AOP代理,在代码运行时生成一个代理对象,根据@Transactional的属性配置信息,这个代理对象决定该声明 @Transactional 的目标方法是否由拦截器TransactionInterceptor来使用拦截,在TransactionInterceptor拦截时,会在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑,最后根据执行情况是否出现异常,利用抽象事务管理器AbstractPlatformTransactionManager操作数据源DataSource提交或回滚事务。
开启事务
启动类加上@EnableTransactionManagement注解,开启事务支持。
SpringBoot默认开启事务,直接在接口、类、方法上使用@Transactional注解即可。
自动回滚
1 |
|
手动回滚
1 |
|
部分回滚
1 |
|
@Transactional参数
参数名称 | 描述 |
---|---|
readOnly | 用于设置当前事务是否为只读事务。true表示只读,false则表示可读写,默认为false。例如:@Transactional(readOnly=true) |
rollbackFor | 用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。默认为非检测性异常unchecked exceptions(RuntimeException和Error)。 例如: 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class}) |
transactionManager / value | 多个事务管理器托管在Spring容器中时,指定事务管理器的bean名称 |
rollbackForClassName | 用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。 例如: 指定单一异常类名称:@Transactional(rollbackForClassName=”RuntimeException”) 指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”, “Exception”}) |
noRollbackFor | 用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。 例如: 指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) |
noRollbackForClassName | 用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。 例如: 指定单一异常类名称:@Transactional(noRollbackForClassName=”RuntimeException”) 指定多个异常类名称:@Transactional(noRollbackForClassName={“RuntimeException”, “Exception”}) |
propagation | 用于设置事务的传播行为。默认为REQUIRED。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED, readOnly=true) |
isolation | 用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置。 |
timeout | 用于设置事务的超时秒数。单位为秒,-1表示永不超时,默认为-1。例如:@Transactional(timeout=30) |
@Propagation参数(事务的传播行为)
参数 | 描述 |
---|---|
REQUIRED | 默认的传播行为。如果有事务,则加入事务。没有事务,则新建事务 |
SUPPORTS | 如果有事务,则加入事务。没有事务,则不用事务 |
MANDATORY | 必须在一个已有的事务中执行。如果当前存在事务,则加入该事务,如果当前不存在事务,则抛出异常 |
REQUIRES_NEW | 不管是否存在事务,都新建事务执行。如果已有事务,则挂起当前事务,再新建事务,新的执行完毕, 再执行老的事务 |
NOT_SUPPORTED | 必须在一个没有的事务中执行,如果当前已有事务,则将当前事务挂起 |
NEVER | 必须在一个没有的事务中执行,否则抛出异常IllegalTransactionStateException |
@isolation参数(事务的隔离级别)
参数 | 描述 |
---|---|
DEFAULT | 默认的隔离级别。使用数据库默认的事务隔离级别。MYSQL默认隔离级别为REPEATABLE_READ,Oracle 默认隔离级别为READ_COMMITTED,SQLSERVER默认隔离级别为READ_COMMITTED |
READ_UNCOMMITTED(读取未提交数据() | 最低的隔离级别。在事务完成前,其他事务可以看到该事务所修改的数据。而在其他事务提交前,该事务也可以看到其他事务所做的修改。可能导致脏读,幻读,不可重复读 |
READ_COMMITTED(读取已提交数据) | 大多数数据库的默认级别。在事务完成前,其他事务无法看到该事务所修改的数据。在该事务提交后,你就可以查看其他事务插入或更新的数据。如果其他事务修改了数据,就会看到不同的数据。可防止脏读,但幻读和不可重复读仍可以发生 |
REPEATABLE_READ(可重复读) | 比READ_COMMITTED更严格,该隔离级别确保如果在事务中查询了某个数据集,你至少还能再次查询到相同的数据集,即使其他事务修改了所查询的数据。然而如果其他事务插入了新数据,你就可以查询到该新插入的数据。可防止脏读,不可重复读,但幻读仍可能发生 |
SERIALIZABLE(串行化) | 完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。代价最大、可靠性最高的隔离级别。所有的事务都是按顺序一个接一个地执行。避免所有不安全读取 |
注意
- @Transactional注解只能被应用到public方法上,对于其它的方法,标记了@Transactional注解也不会报错,但事务无法回滚。
- 外部调用的公共方法A未声明事务@Transactional,方法B若是其他类的方法且声明事务,则事务由子方法B控制。
- 外部调用的公共方法A未声明事务@Transactional,子方法B若是本类的方法,则无论子方法B是否声明事务,事务均不会生效。(可以使用applicationContext.getBean(service.class)直接从IOC容器中将类取出来,然后再调用方法B即可,这样就能用上Spring AOP生成的代理对象)
- 外部调用的公共方法A声明事务@Transactional,无论子方法B是不是本类的方法或者是否声明事务,事务均由公共方法A控制。
- 异常被try/catch捕获而没有被抛出,则事务无法回滚。
- 多线程下事务管理因为线程不属于spring托管,所以事物无法回滚。
- 数据库要支持事务,如果是mysql,要使用innodb引擎,myisam不支持事务。