Spring事务的传播属性

一、事务特性

事务具有四个特点(ACID),如下:

1)原子性:要不全部成功,要不全部撤销

2)隔离性:事务之间相互独立,互不干扰

3)一致性:数据库正确的改变状态后,数据库的一致性约束没有被破坏

4)持久性:事务的提交结果,将持久保存在数据库中

二、Spring事务传播源码

Spring事务的传播特性定义在 TransactionDefinition  类中,源码如下:

package org.springframework.transaction;

import org.springframework.lang.Nullable;

/**
 * Interface that defines Spring-compliant transaction properties.
 * Based on the propagation behavior definitions analogous to EJB CMT attributes.
 *
 * <p>Note that isolation level and timeout settings will not get applied unless
 * an actual new transaction gets started. As only {@link #PROPAGATION_REQUIRED},
 * {@link #PROPAGATION_REQUIRES_NEW} and {@link #PROPAGATION_NESTED} can cause
 * that, it usually doesn't make sense to specify those settings in other cases.
 * Furthermore, be aware that not all transaction managers will support those
 * advanced features and thus might throw corresponding exceptions when given
 * non-default values.
 *
 * <p>The {@link #isReadOnly() read-only flag} applies to any transaction context,
 * whether backed by an actual resource transaction or operating non-transactionally
 * at the resource level. In the latter case, the flag will only apply to managed
 * resources within the application, such as a Hibernate {@code Session}.
 *
 * @author Juergen Hoeller
 * @see PlatformTransactionManager#getTransaction(TransactionDefinition)
 * @see org.springframework.transaction.support.DefaultTransactionDefinition
 * @see org.springframework.transaction.interceptor.TransactionAttribute
 * @since 08.05.2003
 */
public interface TransactionDefinition {

	/**
	 * Support a current transaction; create a new one if none exists.
	 * Analogous to the EJB transaction attribute of the same name.
	 * <p>This is typically the default setting of a transaction definition,
	 * and typically defines a transaction synchronization scope.
	 */
	// Spring默认的事务传播特性,支持当前事务,如果没有当前事务,则创建新事务运行
	int PROPAGATION_REQUIRED = 0;

	/**
	 * Support a current transaction; execute non-transactionally if none exists.
	 * Analogous to the EJB transaction attribute of the same name.
	 * <p><b>NOTE:</b> For transaction managers with transaction synchronization,
	 * {@code PROPAGATION_SUPPORTS} is slightly different from no transaction
	 * at all, as it defines a transaction scope that synchronization might apply to.
	 * As a consequence, the same resources (a JDBC {@code Connection}, a
	 * Hibernate {@code Session}, etc) will be shared for the entire specified
	 * scope. Note that the exact behavior depends on the actual synchronization
	 * configuration of the transaction manager!
	 * <p>In general, use {@code PROPAGATION_SUPPORTS} with care! In particular, do
	 * not rely on {@code PROPAGATION_REQUIRED} or {@code PROPAGATION_REQUIRES_NEW}
	 * <i>within</i> a {@code PROPAGATION_SUPPORTS} scope (which may lead to
	 * synchronization conflicts at runtime). If such nesting is unavoidable, make sure
	 * to configure your transaction manager appropriately (typically switching to
	 * "synchronization on actual transaction").
	 *
	 * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
	 * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#SYNCHRONIZATION_ON_ACTUAL_TRANSACTION
	 */
	// 支持当前事务,如果没有当前事务,则以非事务方式运行
	int PROPAGATION_SUPPORTS = 1;

	/**
	 * Support a current transaction; throw an exception if no current transaction
	 * exists. Analogous to the EJB transaction attribute of the same name.
	 * <p>Note that transaction synchronization within a {@code PROPAGATION_MANDATORY}
	 * scope will always be driven by the surrounding transaction.
	 */
	// 支持当前事务,如果当前没有事务则抛出异常
	int PROPAGATION_MANDATORY = 2;

	/**
	 * Create a new transaction, suspending the current transaction if one exists.
	 * Analogous to the EJB transaction attribute of the same name.
	 * <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
	 * on all transaction managers. This in particular applies to
	 * {@link org.springframework.transaction.jta.JtaTransactionManager},
	 * which requires the {@code javax.transaction.TransactionManager} to be
	 * made available it to it (which is server-specific in standard Java EE).
	 * <p>A {@code PROPAGATION_REQUIRES_NEW} scope always defines its own
	 * transaction synchronizations. Existing synchronizations will be suspended
	 * and resumed appropriately.
	 *
	 * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
	 */
	// 新建事务,如果当前存在事务,则把当前事务挂起
	int PROPAGATION_REQUIRES_NEW = 3;

	/**
	 * Do not support a current transaction; rather always execute non-transactionally.
	 * Analogous to the EJB transaction attribute of the same name.
	 * <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
	 * on all transaction managers. This in particular applies to
	 * {@link org.springframework.transaction.jta.JtaTransactionManager},
	 * which requires the {@code javax.transaction.TransactionManager} to be
	 * made available it to it (which is server-specific in standard Java EE).
	 * <p>Note that transaction synchronization is <i>not</i> available within a
	 * {@code PROPAGATION_NOT_SUPPORTED} scope. Existing synchronizations
	 * will be suspended and resumed appropriately.
	 *
	 * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
	 */
	// 不支持当前事务,总是在非事务中运行,如果存在当前事务,就把当前事务挂起
	int PROPAGATION_NOT_SUPPORTED = 4;

	/**
	 * Do not support a current transaction; throw an exception if a current transaction
	 * exists. Analogous to the EJB transaction attribute of the same name.
	 * <p>Note that transaction synchronization is <i>not</i> available within a
	 * {@code PROPAGATION_NEVER} scope.
	 */
	// 不支持当前事务,如果存在当前事务,则抛出异常
	int PROPAGATION_NEVER = 5;

	/**
	 * Execute within a nested transaction if a current transaction exists,
	 * behave like {@link #PROPAGATION_REQUIRED} otherwise. There is no
	 * analogous feature in EJB.
	 * <p><b>NOTE:</b> Actual creation of a nested transaction will only work on
	 * specific transaction managers. Out of the box, this only applies to the JDBC
	 * {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}
	 * when working on a JDBC 3.0 driver. Some JTA providers might support
	 * nested transactions as well.
	 *
	 * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
	 */
	// 如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则进行和 PROPAGATION_REQUIRED 类似的操作
	int PROPAGATION_NESTED = 6;
}

从代码中可以看出Spring中共有 7 种事务传播属性:

1、PROPAGATION_REQUIREDSpring默认的事务传播特性,方法被调用时自动开启事务,在事务范围内使用则使用同一个事务,否则开启新事务

2、PROPAGATION_SUPPORTS:自身不会开启事务,在事务范围内则使用相同事务,否则不使用事务(支持当前事务,如果当前没有事务,就以非事务方式执行)

3、PROPAGATION_MANDATORY:自身不开启事务,必须在事务环境使用否则抛出异常,在事务环境中使用同一个事务(支持当前事务,如果当前没有事务就会抛出异常)

4、PROPAGATION_REQUIRES_NEW:无论何时自身都会开启事务(新建事务,如果当前存在事务,把当前事务挂起)

5、PROPAGATION_NOT_SUPPORTED:自身不会开启事务,在事务范围内使用挂起事务,运行完毕事务恢复(不支持当前事务,以非事务方式执行,如果存在当前事务,就把当前事务挂起)

6、PROPAGATION_NEVER:自身不会开启事务,在事务范围使用抛出异常(以非事务方式执行操作,如果当前存在事务,则抛出异常)

7、PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则进行和 PROPAGATION_REQUIRED 类似的操作

注意:前六个事务传播属性类似于 EJB CMT,而第七个则是 JDBC 的(Spring 提供的一个特殊变量)

三、demo示例

下面以具体例子分析:

注意:首先应该清楚地是如果异常被捕获,则事务是不会回滚的,事务回滚默认的条件是遇到 RuntimeException,当然每个事务上可以自定义事务回滚的 Exception

3.1、PROPAGATION_REQUIRED(insertUser 传播属性是 REQUIRED)

3.1.1 insertGroup 传播属性是 REQUIRED 或者 SUPPORTS 或者 MANDATORY 或者 NESTED

或者

或者

或者

测试结果:无论 UserService 还是 GroupService 抛出异常,事务都将会回滚,说明二者使用的是同一个事务。

3.1.2 如果 GroupService 中 insertGroup 的事务传播属性是 NOT_SUPPORTED,具体代码如下:

3.1.3  insertGroup 的事务传播属性是 REQUIRES_NEW,具体代码如下:

1)insertUser 运行正常,insertGroup 抛出异常:

2)insertUser 抛出异常 ,insertGroup  运行正常  :

 

3.2、PROPAGATION_REQUIRES_NEW(insertUser 的传播属性是  REQUIRES_NEW  )

3.2.1、insertGroup 传播属性是 REQUIRED 或者 MANDATORY 或者 SUPPORTS 或者 NESTED(无论二者谁抛出异常,事务都会回滚,由于二者使用的是同一个事务

或者

或者

或者

3.2.2、insertGroup 传播属性是 REQUIRES_NEW

1)、insertUser 抛出异常,insertGroup 运行正常,则 insertUser 回滚,insertGroup 正常提交

2)、insertUser 运行正常,insertGroup 抛出异常,则 insertUser 回滚,insertGroup 回滚

3)、insertUser 运行正常,insertGroup 运行正常,则 insertUser 正常提交,insertGroup 正常提交

4)、insertUser 抛出异常,insertGroup 抛出异常,则 insertUser 回滚,insertGroup 回滚

3.2.3、insertGroup 传播属性是 REQUIRES_NOT_SUPPORTED

1)、insertUser 抛出异常,insertGroup 运行正常 或者 抛出异常,则 insertUser 回滚,insertGroup 正常提交

2)、insertUser 运行正常,insertGroup 运行正常 或者 抛出异常,则 insertUser 正常提交,insertGroup 正常提交

总结:insertUser 方法使用事务的传播属性 REQUIRED 或者 REQUIRES_NEW 时,无论 insertGroup 的事务传播是什么,只要在二者任一方法中出现抛出异常,insertUser 就会回滚

其实让我们最容易混淆的就是 REQUIRES_NEW  和 NESTED,那么这两种方式有什么区别呢?下面我们解析一下:

REQUIRES_NEW:启动一个新的,不依赖于环境的“内部”事务,这个事务将被完全的 commited 或者 rollback 而不依赖于外部事务,它拥有自己的隔离范围,自己的锁等等。当内部事务开始执行时,外部事务被挂起,内部事务结束时,外部事务将继续执行

NESTED:开始一个“嵌套的”事务,它是已经存在事务的一个真正的子事务,嵌套事务开始执行时,它将获取一个 savepoint。如果这个嵌套事务执行失败,我们将回滚到此 savepoint。嵌套事务是外部事务的一部分,只有外部事务结束后它才会提交。

由此可见,REQUIRES_NEW 和 NESTED 最大的区别在于

REQUIRES_NEW 完全是一个新的事务,而 NESTED 则是外部事务的子事务,如果外部事务 commit ,嵌套事务也会被 commit,这个规则同样适用于 rollback

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值