文章目录
- Spring 在 TransactionDefinition 接口中规定了 7 种类型的事务传播行为
- 事务传播行为是Spring框架独有的事务增强特性,不属于数据库行为
- 通过这 7 种 传播行为,可以使我们更灵活的控制哪些行为在同一个事务内、哪些行为不在同一事务内、各事务之间的关系,从而使得我们的业务逻辑更加灵活
1 Spring中七种事务传播行为
- 事务传播行为规定了在一个嵌套方法内部,哪些方法属于同一个事务,哪些方法不属于同一个事务,以及事务之间的关系
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果存在则加入事务。如果不存在,创建新事务 |
PROPAGATION_SUPPORTS | 如果存在则加入事务。如果不存在,以不可用事务执行 |
PROPAGATION_MANDATORY | 如果存在则加入事务。如果不存在,抛出异常 |
PROPAGATION_REQUIRES_NEW | 如果存在则挂起当前事务,并创建新事务。如果不存在则创建事务 |
PROPAGATION_NESTED | 如果存在则创建嵌套事务。如果不存在,创建新事务 |
PROPAGATION_NOT_SUPPORTED | 以不可用事务运行。如果存在事务,则先挂起当前事务 |
PROPAGATION_NEVER | 以不可用事务运行。如果存在事务,抛出异常 |
2 定义
- null:表示没有 @Transactional 注解的方法,且不在事务内
- NULL:表示没有 @Transactional 注解的方法,但在事务内(其外部有事务,所以加入外部事务)
- required:表示有 @Transactional(propagation = Propagation.REQUIRED) 的方法,但是外部没有事务
- REQUIRED:表示有 @Transactional(propagation = Propagation.REQUIRED) 的方法,但是外部有事务
- 同理其它 6 个传播行为
- 数据库错误:表示有 @Transactional 注解的方法,执行数据库操作时失败,但是异常最终被 catch 掉了
- 异常:方法抛出来的、没有被捕获的异常
- 方法被回滚:表示方法内部的数据库操作被回滚
- 事务切面方法:表示加入事务,且加入事务切面(被 @Transactional 注解注释的方法才会加入事务切面)
- 事务方法:表示加入事务的方法(包含事务切面方法)
- 外层事务:表示先加入同一事务的方法
- 真实事务:表示有 @Transactional 注解的方法
- 普通方法:没有 @Transactional 注解的方法
3 定理
- null 方法不受事务影响,只要执行成功就不会被回滚(例:插入了一行数据后,执行其它语句立马报错也不会被回滚,因为null 方法插入后会立马提交,其 auto-commit 为 true)
- 事务内发生 异常,加入这个事务的所有数据库操作都会被回滚
4 推理
-
NULL 方法受事务影响,外层事务被回滚,NULL 方法也会被回滚(…方法证明)
-
如果 NULL 方法发生 数据库错误,不影响事务(即是 3)
-
required 内部的 NULL 方法发生 数据库错误,required 方法不会被回滚(虽然在同一事务内,但是 NULL 方法的数据库异常不会传递到事务切面,因为 NULL 方法虽然加入了事务,但是没有加入事务切面)
-
required 内部的 REQUIRED 方法发生 数据库错误,required 方法被回滚(通过事务切面将回滚状态传递到外层)
-
required 内部的 SUPPORTS 方法发生 数据库错误,required 方法被回滚(同理)
-
required 内部的 MANDATORY 方法发生 数据库错误,required 方法被回滚(同理)
-
required 内部的 REQUIRES_NEW 方法发生 数据库错误,required 方法不会被回滚(不在同一个事务)
-
required 内部的 NESTED 方法发生 数据库错误,required 方法不会被回滚(NESTED 发生数据库错误会回滚到保存点,保证了外部事务的有效性)
-
required 内部的 NOT_SUPPORTED 方法发生 数据库错误,required 方法不会被回滚(NOT_SUPPORTED 方法 不在事务内)
-
requires_new、nested 情况和 required 相同,因为这两个都只是开启新事务,意思和 required 一模一样
-
supports、not_supported、never 都是以非事务运行和 null 效果一样(null之内的普通方法不能复用session)
-
mandatory 直接抛异常
-
NESTED:--------0------0---------,两个0 之间是嵌套事务,不管是内层嵌套事务还是外层事务,其实是一个事务,只是在这个事务线上创建了一个保存点,嵌套事务内部事情处理完了之后,整个事务状态会回到保存点时的状态,这样保证了即使嵌套内部发生数据库错误(这时仍然会设置事务为 rollback 状态),因为方法完了之后事务状态会回到保存点,所以 rollback 状态被覆盖了
-
NESTED 和 NULL 的区别
- NULL 内部的 事务方法 属于 NULL 所在事务,而 NESTED 内部的 事务方法 属于 NESTED 开启的嵌套事务和外层事务没关系
- NULL 内部的 事务切面方法 发生 数据库错误 会导致外部事务回滚,而 NESTED 内部的事务切面方法 发生 数据库错误 只会导致 NESTED 开启的嵌套事务回滚,而外层事务不会
-
NESTED 和 REQUIRES_NEW 的区别
- 外层事务发生 数据库错误 会导致 NESTED 方法回滚,而不会导致 REQUIRES_NEW 方法回滚
5 证明
5.1 事务传播行为证明
5.2 推理证明
6 事务切面设置的几个核心状态
6.1 newTransaction
-
是否是新开启的事务
-
此属性是直接赋值的,没有判断逻辑
-
所有的小写/REQUIRES_NEW 为 true
-
所有的大写(除了 REQUIRES_NEW) 为 false
-
事务关闭时如果 newTransaction= true 且 actualTransactionActive=true,才会做 connection 的 commit 操作
6.2 actualNewSynchronization
-
当前线程是否是新开启同步的
-
如果当前线程已经开启同步了,返回 false。否则,返回 true
AbstractPlatformTransactionManager#newTransactionStatus
actualNewSynchronization = !TransactionSynchronizationManager.isSynchronizationActive()
6.3 isSynchronizationActive/actualTransactionActive
-
当前线程是否同步/当前线程的事务是否可用
-
如果 actualNewSynchronization 为 false 的话
- 如果 status 中的 transaction 为 null,actualTransactionActive=false。否则为 true
- isSynchronizationActive = true
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
if (status.isNewSynchronization()) {
//关键 1:设置当前线程事务是否活跃
setActualTransactionActive(status.hasTransaction());
//关键 2: 设置事务是否已经同步
//synchronizations.set(new LinkedHashSet<>());
initSynchronization();
}
}
6.4 connectionHolder
- 此属性只有在创建新的、可用的事务时,才会设置
- transaction.newConnectionHolder = true
- conHolder.setSynchronizedWithTransaction(true)
- conHolder.setTransactionActive(true)
- connection.setAutoCommit(false)
org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
在 doBegin 方法中做了以下操作
1. 创建 connectionHolder
2. 将此 connectionHolder 设置到当前事务中,并设置 newConnectionHolder = true
3. 设置 ConnectionHolder().setSynchronizedWithTransaction(true) ----> 1
4. 将此 connectionHolder 中的 connection 设置 auto-commit = false -----> 2
5. 设置 事务的 read only 状态(如果指定了 read only 的话