SpringBoot中事务处理@Transactional

一、什么是事务?

一个事务一旦其中有一个操作出现错误,事物的操作将全部回滚。系统将事务中对数据库的所有的已完成的操作全部撤消,回滚到事务开始的状态。

一个事务中的所有操作作为一个单元,要么完全地执行,要么完全地不执行。

事务是为了确保数据的完整性和一致性,保证成批的 SQL 语句要么全部执行,要么全部不执行。

Spring 事务管理分为编程式和声明式的两种方式。编程式事务指的是通过编码方式实现事务,声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。

声明式事务有两种方式:

  1. 在配置文件(xml)中做相关的事务规则声明
  2. 基于 @Transactional 注解的方式

二、事务ACID四种特性

ACID是Atomic(原子性)、Consistency(一致性)、Isolation(隔离性)和Durability(持久性)的英文缩写。

1、原子性(Atomicity)

事务最基本的操作单元,要么全部成功,要么全部失败,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。

2、一致性(Consistency)

事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。

3、隔离性(Isolation)

指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。

4、持久性(Durability)

指的是只要事务成功结束,它对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。

三、事务处理@Transactional注解的使用

  • Spring团队的建议是在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。当然可以在接口上使用 @Transactional 注解,但是这将只能当设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。
  • @Transactional 注解应该只被应用到 public 修饰的方法上。 如果在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。
  • Spring的AOP即声明式事务管理默认是针对unchecked exception回滚。也就是默认对RuntimeException()异常或是其子类进行事务回滚。checked异常,即Exception可try{}捕获的不会回滚,因此对于我们自定义异常,通过rollbackFor进行设定。
  • 如果需要捕获异常后,同时进行回滚,通过TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();进行手动回滚操作。
  • 使用Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint(); 设置回滚点,使用TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint); 回滚到savePoint。

四、注解@Transactional常用配置

参数名称功能描述
readOnly该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true)
rollbackForrollbackFor 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
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该属性用于设置事务的传播行为。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
isolation该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置
timeout该属性用于设置事务的超时秒数,默认值为-1表示永不超时 事物超时设置: @Transactional(timeout=30) //默认是30秒

五、Propagation的属性(事务的传播行为)

属性含义
REQUIRED默认值 在有transaction状态下执行;如当前没有transaction,则创建新的transaction;
SUPPORTS如当前有transaction,则在transaction状态下执行;如果当前没有transaction,在无transaction状态下执行;
MANDATORY必须在有transaction状态下执行,如果当前没有transaction,则抛出异常IllegalTransactionStateException;
REQUIRES_NEW创建新的transaction并执行;如果当前已有transaction,则将当前transaction挂起;
NOT_SUPPORTED在无transaction状态下执行;如果当前已有transaction,则将当前transaction挂起;
NEVER在无transaction状态下执行;如果当前已有transaction,则抛出异常IllegalTransactionStateException。

六、事务5种隔离级别

隔离级别含义
DEFAULT这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别. 另外四个与JDBC的隔离级别相对应;
READ_UNCOMMITTED最低的隔离级别。事实上我们不应该称其为隔离级别,因为在事务完成前,其他事务可以看到该事务所修改的数据。而在其他事务提交前,该事务也可以看到其他事务所做的修改。可能导致脏,幻,不可重复读
READ_COMMITTED大多数数据库的默认级别。在事务完成前,其他事务无法看到该事务所修改的数据。遗憾的是,在该事务提交后,你就可以查看其他事务插入或更新的数据。这意味着在事务的不同点上,如果其他事务修改了数据,你就会看到不同的数据。可防止脏读,但幻读和不可重复读仍可以发生。
REPEATABLE_READ比ISOLATION_READ_COMMITTED更严格,该隔离级别确保如果在事务中查询了某个数据集,你至少还能再次查询到相同的数据集,即使其他事务修改了所查询的数据。然而如果其他事务插入了新数据,你就可以查询到该新插入的数据。可防止脏读,不可重复读,但幻读仍可能发生。
SERIALIZABLE完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。代价最大、可靠性最高的隔离级别,所有的事务都是按顺序一个接一个地执行。避免所有不安全读取。
  • 脏读 : 一个事务读取到另一事务未提交的更新数据
  • 不可重复读 : 在同一事务中,多次读取同一数据返回的结果有所不同。后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是后续读取不能读到另一事务已提交的更新数据。
  • 幻读 : 一个事务读到另一个事务已提交的insert数据

七、SpringBoot @Transactional注解事务回滚不起作用

参照:SpringBoot @Transactional注解事务不回滚解决方法

查看数据库本身对应的库、表所设置的是什么引擎。MyIsam不支持事务,如果需要支持事务,必须改为InnnoDB

数据库中的存储引擎其实是对使用了该引擎的表进行某种设置,数据库中的表设定了什么存储引擎,那么该表在数据存储方式、数据更新方式、数据查询性能以及是否支持索引等方面就会有不同的“效果”。

参照:MySQL两种存储引擎:MyISAM和InnoDB

发表评论