Spring 事务
Spring 事务管理
事务就是一系列操作的原子性执行,要么全部完成,要么全部不完成,不会结束在中间某个点。
Spring 事务管理是主要基于以下概念:
-
事务的传播行为(Propagation Behavior):
定义了当一个事务方法被另一个事务方法调用时,事务如何传播,例如加入已存在的事务或创建一个新的事务。
-
事务的隔离级别(Isolation Level):
定义了事务在执行过程中对数据的可见性,以及并发事务之间的隔离程度。
-
事务的超时机制(Timeout):
定义了事务允许的最大执行时间,超过这个时间事务将被自动回滚。
-
事务的只读属性(Read-only):
标记事务为只读,这可以提高事务的性能,并且在某些情况下可以避免数据不一致。
-
事务的回滚规则(Rollback Rules):
定义了哪些异常会导致事务回滚,哪些异常不会。
Spring 事务管理的实现方式
-
1.声明式事务管理:
声明式事务将事务管理代码从业务方法中分离出来,通过aop进行封装。
Spring 声明式事务使得我们无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作。
-
注解方式(推荐)
目前主流的方式是通过 @Transactional 注解实现。
(1) 配置启用事务管理,通过配置类上的 @EnableTransactionManagement 注解来实现。
(2) 定义事务管理器(PlatformTransactionManager接口的实现类,例如DataSourceTransactionManager)
(3) 在类或方法上使用@Transactional注解,定义事务的传播行为、隔离级别、超时时间、只读属性、回滚规则等Spring配置类 import org.springframework.context.annotation.Configuration; @Configuration @EnableTransactionManagement //1)启用事务管理 public class SpringConfig { //2)定义事务管理器 @Bean public PlatformTransactionManager transactionManager(DataSource datasource){ return new DataSourceTransactionManager(dataSource); } } 实现类 import org.springframework.transaction.annotation.Transactional; public class UserService { @Transactional(rollbackFor = Exception.class)//查看源码了解所有属性配置 public void updateUser(User user) { // 数据更新操作 } }
-
xml配置文件的实现方式,已不推荐使用。
-
-
2.编程式事务管理:
-
通过 PlatformTransactionManager 接口手动管理事务的创建、提交和回滚。这种方式更灵活,但代码更复杂。
-
通过TransactionTemplate的excute方法管理事务。
-
Spring 的事务传播行为
在TransactionDefinition接口中定义了七个事务传播行为:
-
PROPAGATION_REQUIRED 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。如果嵌套调用的两个方法都加了事务注解,并且运行在相同线程中,则这两个方法使用相同的事务中。如果运行在不同线程中,则会开启新的事务。(默认)
-
PROPAGATION_SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。
-
PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果不存在事务,则抛出异常IllegalTransactionStateException。
-
PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。需要使用JtaTransactionManager作为事务管理器。
-
PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。需要使用JtaTransactionManager作为事务管理器。
-
PROPAGATION_NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常。
-
PROPAGATION_NESTED 如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务, 则按PROPAGATION_REQUIRED 属性执行。
Spring 事务隔离级别
在TransactionDefinition接口中定义了五个隔离级别(实际只有四个隔离级别)
-
ISOLATION_DEFAULT 使用底层数据库的默认隔离级别。
-
ISOLATION_READ_UNCOMMITTED 读取未提交数据,可能会出现脏读, 不可重复读、幻读。基本不适用
-
ISOLATION_READ_COMMITTED 读取已提交数据,可能会出现不可重复读和幻读。Oracle默认
-
REPEATABLE_READ 可重复读,会出现幻读。 MySQL默认
-
Isolation.SERIALIZABLE 串行化
并发事务的带来的事务问题
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对统一数据进行操作)。并发虽然是必须的,但可能会导致一下的问题。
-
脏读(Dirty read): 指的是A事务读取到B事务尚未提交的数据,“脏数据”指的是未提交的数据。
-
不可重复读(Unrepeatableread): 指的是A事务使用相同条件多次读取同一数据,得到的结果不一致。
-
幻读(Phantom read): 指的是A事务使用相同条件多次读取某一批数据,得到的记录条数不一致。幻读与不可重复读类似,区别在于不可重复读的重点是修改,幻读的重点在于新增或者删除。
不可重复度和幻读区别:
例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。
例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2 又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读。
事务的隔离级别与并发问题的关系
隔离级别 | 脏读(Dirty Read) | 不可重复读(NonRepeatable Read) | 幻读(Phantom Read) |
---|---|---|---|
未提交读(Read uncommitted) | ✅ | ✅ | ✅ |
已提交读(Read committed) | ❌ | ✅ | ✅ |
可重复读(Repeatable read) | ❌ | ❌ | ✅ |
可串行化(Serializable ) | ❌ | ❌ | ❌ |
Spring 事务的失效场景
-
一 事务不生效
-
事务方法非public访问权限
-
事务方法被关键字final修饰
-
在同一个类中的方法直接内部调用,调用方未开启事务
-
事务方法的对象未被Spring IoC容器管理
-
数据库不支持事务
-
项目未开启事务支持
-
-
二 事务不回滚
-
设置的事务传播类型不支持事务,只有REQUIRED,REQUIRES_NEW,NESTED这三种传播类型才会创建新事务
-
异常被手动捕获,如try..catch
-
异常被手动捕获后,抛出的异常非RuntimeException(运行时异常)或Error(错误)
-
抛出的异常不属于自定义的事务回滚异常
-
嵌套事务,回滚多了。(理解请看参考)
-
注:不同版本可能会有不同的结果。建议具体项目,具体分析。