课程名称:Java架构师-技术专家
课程章节:第1周 架构初知与单体架构设计
主讲老师:慕课讲师团:Geely、风间影月、阿神……
课程内容:
Spring 声明式事务 @Transactional 注解
@Transactional
是 Spring 事务管理提供的注解,在一个方法中加上了这个注解,那么这个方法就是有事务的,方法内的操作要么一起提交,要么回滚。
Propagation
事务的传播行为,表示被调用者的事务和调用者的事务之间的关系。
表示当一个事务传播行为修饰的方法被另一个方法调用时,事务如何进行传播。不同的 propagation 配置会有不同的效果
- methodB 是事务修饰的方法
- methodA 调用了 methodB ,那么事务应该如何传播呢?
:::tip
99% 的情况下都使用 Propagation.REQUIRED
。
:::
1. 默认传播行为:Propagation.REQUIRED
- 支持当前事务;如果当前没有事务,则新建一个事务。(日常开发中最常使用的配置)
2. Propagation.REQUIRE_NEW
- 新建事务;如果当前存在事务,则把当前事务挂起
- 由于 methodA、methodB 它们的方法使用是两个不同的事务,当 methodB 方法提交后,即使 methodA 方法失败回滚了,
也不会导致 methodB 方法出现回滚;当 methodB 方法失败回滚以后,如果methodA未捕捉到methodB抛出的异常,
导致methodA 继续抛出该异常,methodA方法也会被回滚,如果methodA方法捕获了methodB方法所抛出的异常,则methodA所在的事务还是有可能提交成功的,因为当前的事务已经被挂起了。
3. Propagation.SUPPORTS
- 支持当前事务;否则将以非事务方式执行
4. Propagation.MANDATORY
- 支持当前事务;如果没有将抛出 IllegalTransactionStateException 异常
- 会打断 methodA 方法的执行,除非在 methodA 方法中捕获并处理这个异常。
5.Propagation.NOT_SUPPORT
- 不支持当前事务,而是始终以非事务的方式执行
6. Propagation.NEVER
- 以非事务的方式执行;如果当前存在事务,则抛出 IllegalTransStateException 异常
- 要求调用者方法不存在事务注解,很少使用
7. Propagation.NESTED
- 如果当前存在事务,则对于该传播行为修饰的方法会依然使用当前事务
- 基本上不会使用到
isolation
事务的隔离级别,决定了事务的完整性,默认使用当前数据库默认的隔离级别,通常不会去修改这个属性,
timeout
事务的过期时间或者超时时间,默认是当前数据库默认的过期时间或者超时时间,通常不需要额外定制,使用数据库的即可。
readOnly
指定是否为只读事务,默认是 false ,如果在一次执行单条查询语句,则没有必要启用事务支持,数据库默认启用 SQL 执行期间的读一致性。
如果一次执行多条查询语句,比如统计查询,报表查询;在这种场景下,多条查询的 SQL 必须保证整体的一致性,否则在前一条SQL查询之后,后一条 SQL 查询之前,数据被其它用户改变,这次整体的统计查询将会出现读
数据不一致的状态,此时应该启用只读事务的支持。
对于只读查询,可以指定事务类型为 readOnly, 由于只读事务不存在数据的修改,所以数据库将为只读事务添加一些优化手段。
rollbackFor、noRollbackFor
rollbackFor 指定那些异常会导致事务回滚。
noRollbackFor 指定那些异常不会导致事务回滚。@Transactional
注解默认只针对 RuntimeException 、Error 及其子类进行回滚,如果希望对检查型的异常进行回滚就需要对 rollbackFor 属性进行配置。
noRollbackFor 属性正好相反,用于指定那些异常不发生回滚。
@Transactional 注解最常见的应用
- 可以标注在类、方法和接口(但接口上会失效,所以不能这样用)上;且方法上的注解会覆盖类上的注解
- 标注在方法上,标识开启事务功能,正常则提交、异常则回滚
- 自行指定rollbackFor属性,让Checked Exception 也能够实现回滚
- 让 TestCase 也能够实现回滚(只需要在测试方法上面加上注解)。如果已经有了
@Transactional
注解在类上面。但是,还是想要不回滚某个单元测试,加上 @Rollback(value=false)
@Transactional注解失效的场景
场景一: 把注解标注在非 public 修饰的方法上
把注解标注在非 public 修饰的方法上,SpringAOP 代理时,最终会调用一个方法(TransactionalRepositoryProxyPostProcessor#AbstractFallbackTransactionAttributeSource#computeTransactionAttribute(Method method, Class<?> targetClass)
)去计算注解的属性是否符合要求,这个方法的第一行就是去检查方法是不是 public 的,如果不是则不能获取 @Transactional 注解的配置信息。虽然事务没有生效,但是编译运行代码不会有任何错误。java private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) { if (this.allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null; } }
场景二:propagation(传播行为)属性配置错误
- Propagation.SUPPORTS: 如果调用方没有事务,那么会以非事务的方式运行。
- Propagation.NOT_SUPPORTS : 会挂起当前事务,那么会以非事务的方式运行
- Propagation.NEVER: 如果有事务,会抛出异常
场景三:rollbackFor属性设置错误
rollbackFor 能够指定触发事务回滚的异常类型,Spring默认抛出了非检查型的异常或者是Error 才会回滚事务。如果在事务中抛出了其它类型的异常,但却希望Spring能够回滚事务就需要自己指定 rollbackFor属性
场景四:在同一个类中方法调用,导致事务失效
比如有一个类有两个方法 methodA 和 methodB,methodA 去调用类里面的 methodB ,不管 methodB 的访问修饰符是什么,如果 methodA 没有声明注解事务,而 methodB 有 ,则 外部调用 methodA 以后,
methodB 的事务是不会起作用的。
只有事务方法被当前类以外的代码调用的时候,才会由 Spring 生成的代理对象去管理
场景五:自己主动去catch,代表 【没有异常】,导致事务失效
场景六:数据库引擎本身就不支持事务(例如 MyISAM),当然也不会生效
事务从根本上就失效了,因为 Spring 也要依赖基础设施。
学习收获:
今天详细学习了Spring的事务
- 事务的传播特性,
- 事务的rollbackFor等属性
共同学习,写下你的评论
评论加载中...
作者其他优质文章