Spring 基础 事务说明
一、事务简介
事务其实是一个并发控制单位,是用户定义的一个操作序列,这些操作要么全部完成,要不全部不完成,是一个不可分割的工作单位。事务有 ACID 四个特性,即:
- Atomicity(原子性):事务中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。
- 一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
- 事务隔离(Isolation):多个事务之间是独立的,不相互影响的。
- 持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
二、Spring 事务的管理方式
2.1、编程式事务
先看个例子
@Autowired
private PlatformTransactionManager transactionManager;
public void transactionDemo() {
// 开始事务
TransactionStatus transactionStatus = this.transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
// TODO 业务代码
// 提交事务
this.transactionManager.commit(transactionStatus);
} catch (Exception e) {
// 回滚事务
this.transactionManager.rollback(transactionStatus);
}
}
- PlatformTransactionManager:(平台)事务管理器,Spring 事务策略的核心。
- TransactionDefinition:事务定义信息(事务隔离级别、传播行为、回滚超时、是否只读、回滚规则)。
- TransactionStatus:事务运行状态。
2.2、声明式事务
通过 AOP 实现(基于@Transactional 的全注解方式使用最多)。
- @Transactional 的常用配置参数总结(只列出了 5 个我平时比较常用的):
| 属性名 | 说明 |
|---|---|
| propagation | 事务的传播行为,默认值为 REQUIRED,可选的值在上面介绍过 |
| isolation | 事务的隔离级别,默认值采用 DEFAULT,可选的值在上面介绍过 |
| timeout | 事务的超时时间,默认值为-1(不会超时)。如果超过该时间限制但事务还没有完成,则自动回滚事务。 |
| readOnly | 指定事务是否为只读事务,默认值为 false。 |
| rollbackFor | 用于指定能够触发事务回滚的异常类型,并且可以指定多个异常类型。 |
- @Transactional 的使用注意事项总结
- @Transactional 注解只有作用到 public 方法上事务才生效,不推荐在接口上使用;
- 避免同一个类中调用 @Transactional 注解的方法,这样会导致事务失效;
- 正确的设置 @Transactional 的 rollbackFor 和 propagation 属性,否则事务可能会回滚失败;
- 被 @Transactional 注解的方法所在的类必须被 Spring 管理,否则不生效;
- 底层使用的数据库必须支持事务机制,否则不生效;
三、Spring 中的隔离级别
TransactionDefinition 接口中定义了五个表示隔离级别的常量:
public interface TransactionDefinition {
......
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
......
}
- TransactionDefinition.ISOLATION_DEFAULT :使用后端数据库默认的隔离级别,MySQL 默认采用的 REPEATABLE_READ 隔离级别 Oracle 默认采用的 READ_COMMITTED 隔离级别.
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED :最低的隔离级别,使用这个隔离级别很少,因为它允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
- TransactionDefinition.ISOLATION_READ_COMMITTED : 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
- TransactionDefinition.ISOLATION_REPEATABLE_READ : 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
- TransactionDefinition.ISOLATION_SERIALIZABLE : 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
四、Spring 事务传播机制
事务的传播机制说明如下:
- REQUIRED:默认值;如果当前上下文中存在事务,那么加入该事务,如果不存在事务,创建一个事务。
- SUPPORTS:如果当前上下文存在事务,则支持事务加入事务,如果不存在事务,则使用非事务的方式执行。
- MANDATORY:如果当前上下文中存在事务,否则抛出异常。
- REQUIRES_NEW:每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
- NOT_SUPPORTED:如果当前上下文中存在事务,则挂起当前事务,然后新的方法在没有事务的环境中执行。
- NEVER:如果当前上下文中存在事务,则抛出异常,否则在无事务环境上执行代码。
- NESTED:如果当前上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。
五、Spring 事务失效场景
- 抛出编译异常
如果@Transactional 没有特别指定,Spring 只会在遇到运行时异常RuntimeException或者error时进行回滚,而IOException等编译异常不会影响回滚。- 解决方法,指定回滚异常:@Transactional(rollbackFor = Exception.class)
- 方法本身捕获了异常
- 同一类中的方法调用
Spring的事务管理功能是通过动态代理实现的,而Spring默认使用JDK动态代理,而JDK动态代理采用接口实现的方式,通过反射调用目标类。简单理解,就是saveUser()方法中调用this.doInsert(),这里的this是被真实对象,所以会直接走doInsert的业务逻辑,而不会走切面逻辑,所以事务失败。- 解决方法:解决方法可以是直接在启动方法上添加 @Transactional 注解
- 方法使用 final 或 static关键字
- 方法不是public
如果方法不是public,Spring事务也会失败,因为Spring的事务管理源码AbstractFallbackTransactionAttributeSource中有判断computeTransactionAttribute()。如果目标方法不是公共的,则TransactionAttribute返回null。 - 错误使用传播机制
Spring事务的传播机制是指在多个事务方法相互调用时,确定事务应该如何传播的策略。Spring提供了七种事务传播机制:REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。如果不知道这些传播策略的原理,很可能会导致交易失败。 - 没有被Spring管理
- 多线程调用
我们说的同一个事务,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。
参考:https://juejin.cn/post/7179080622504149029
https://javaguide.cn/system-design/framework/spring/spring-transaction.html
若有凝问或错误,请指出,我好及时改正,让我们一起进步!
email : binary_space@126.com
qq : 103 586 2795
敲门砖: 代码谱写人生
1711

被折叠的 条评论
为什么被折叠?



