Spring的TransactionDefinition中定义的事务的传播特性和隔离级别

本文详细解析了Spring框架中的事务管理机制,包括传播特性和隔离级别的概念,以及它们如何影响事务的执行。通过具体示例,阐述了不同事务属性的作用,如PROPAGATION_REQUIRED和ISOLATION_REPEATABLE_READ,帮助开发者理解并正确应用Spring事务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

spring的传播特性: (PROPAGATION是传播的意思)

PROPAGATION_REQUIRED,

PROPAGATION_SUPPORTS, 

PROPAGATION_MANDATORY, 

PROPAGATION_REQUIRES_NEW, 

PROPAGATION_NOT_SUPPORTED, 

PROPAGATION_NEVER, 

PROPAGATION_NESTED

class A{
        methodA(){
            //逻辑处理
            b.methodB();
            //逻辑处理
        }
}
class B{
        methodB();
}

1.PROPAGATION_REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。

/**
* 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.
*/
int PROPAGATION_REQUIRED = 0;

翻译:

支持一个当前的事务,如果没有事务则创建一个新的事务;这通常是一个事务定义的默认设置,并且通常限定了事务同步范围

解释:

当A.methodA()和B.methodB()都打上REQUIRED的事务标志,执行A.methodA()方法的时候,看到上下文没有事务,会新建一个事务,当执行到b.methodB()的时候,发现上下文已经有事务了,则不会新建事务,用A.methodA()新建的那个事务。

如果b.methodB()执行成功,a.methodA()执行失败,那么b.methodB()和a.methodA()都会回滚(用的都是a.methodA()的事务)

2.PROPAGATION_SUPPORTS:如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行

/**
	 * 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;

翻译

支持当前事务; 如果不存在,则以非事务方式执行。 类似于同名的EJB事务属性。
注意:对于具有事务同步的事务管理器,PROPAGATION_SUPPORTS与完全没有事务稍有不同,因为它定义了同步可能适用的事务范围。 因此,相同的资源(JDBC连接,Hibernate会话等)将被共享给整个指定的范围。 请注意,确切的行为取决于事务管理器的实际同步配置!
一般来说,请小心使用PROPAGATION_SUPPORTS! 特别是,不要依赖PROPAGATION_SUPPORTS范围内的PROPAGATION_REQUIRED或PROPAGATION_REQUIRES_NEW(这可能会导致运行时出现同步冲突)。 如果这种嵌套是不可避免的,请确保正确配置事务管理器(通常切换到“实际事务同步”)。  

 解释:

 当B.methodB()打上PROPAGATION_SUPPORTS的事务标志,执行A.methodA()方法,当执行到b.methodB()的时候,会检查上下文有没有事务,如果A.methodA()有事务,则b.methodB()沿用该事务,反之b.methodB()就以非事物的方式执行。 

3.PROPAGATION_MANDATORY:如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常

MANDATORY是强制的,命令的意思,放在这里就是强制需要一个事务。

/**
	 * 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;

翻译

支持当前事务; 如果没有当前事务存在,则抛出异常。 类似于同名的EJB事务属性。
请注意,PROPAGATION_MANDATORY范围内的事务同步将始终由周围事务驱动。

解释:

当B.methodB()打上PROPAGATION_MANDATORY的事务标志,执行A.methodA()方法,当执行到b.methodB()的时候,会检查上下文有没有事务,如果A.methodA()有事务,则b.methodB()沿用该事务,如果没有,则会抛出异常

4.PROPAGATION_REQUIRES_NEW:总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起

/**
	 * 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 J2EE).
	 * <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;

翻译

创建一个新的事务,如果存在,暂停当前事务。 类似于同名的EJB事务属性。
注意:实际的事务暂停将无法在所有事务管理器上直接使用。 实际的事务暂停将无法在所有事务管理器上开箱即用。

这特别适用于org.springframework.transaction.jta.JtaTransactionManager,它要求使javax.transaction.TransactionManager可用(它是标准Java EE中的服务器特定的)。
PROPAGATION_REQUIRES_NEW范围始终定义自己的事务同步。 现有的同步将被暂停并恢复正常

解释:

当B.methodB()打上PROPAGATION_REQUIRES_NEW的事务标志,执行A.methodA()方法,当执行到b.methodB()的时候,会检查上下文有没有事务,如果A.methodA()有事务,则会挂起A.methodA()的事务,新建一个属于b.methodB()的事务,当b.methodB()的事务执行结束的时候,则会唤醒A.methodA()的事务。

和PROPAGATION_REQUIRED的差别在于回滚,当b.methodB()的事务提交后,A.methodA()执行失败,只会回滚A.methodA不会回滚b.methodB(),当b.methodB()执行失败,异常被A.methodA()方法catch到的话,A.methodA()事务不会回滚。

5.PROPAGATION_NOT_SUPPORTED:总是非事务地执行,并挂起任何存在的事务

/**
	 * 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 J2EE).
	 * <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;

翻译 

不支持当前事务; 而总是执行非事务性的。 类似于同名的EJB事务属性。
注意:实际的事务暂停将无法在所有事务管理器上直接使用。

这特别适用于org.springframework.transaction.jta.JtaTransactionManager,它要求使javax.transaction.TransactionManager可用(它是标准Java EE中的服务器特定的)。
请注意,事务同步在PROPAGATION_NOT_SUPPORTED范围内不可用。 现有的同步将被暂停并恢复正常。 

解释:

当B.methodB()打上PROPAGATION_NOT_SUPPORTED的事务标志,执行A.methodA()方法,当执行到b.methodB()的时候,会检查上下文有没有事务,如果A.methodA()有事务,则会挂起A.methodA()的事务,当执行完b.methodB()方法的时候,A.methodA()方法继续以事务的方式执行

6.PROPAGATION_NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常

/**
	 * 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;

翻译

不支持当前事务; 如果当前事务存在,则抛出异常。 类似于同名的EJB事务属性。
请注意,事务同步在PROPAGATION_NEVER范围内不可用。

解释:

当B.methodB()打上PROPAGATION_NEVER的事务标志,执行A.methodA()方法,当执行到b.methodB()的时候,会检查上下文有没有事务,如果有事务,则抛出异常,如果没有则以非事务执行

7.PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, PROPAGATION_REQUIRED 属性执行

/**
	 * Execute within a nested transaction if a current transaction exists,
	 * behave like {@link #PROPAGATION_REQUIRED} else. 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
	 */
	int PROPAGATION_NESTED = 6;

翻译

如果当前事务存在,则在嵌套事务内执行,其行为类似于PROPAGATION_REQUIRED 。 EJB中没有类似的功能。
注意:实际创建嵌套事务只适用于特定事务管理器。

开箱即用,这只适用于在处理JDBC 3.0驱动程序时使用JDBC org.springframework.jdbc.datasource.DataSourceTransactionManager。 一些JTA提供者也可能支持嵌套事务。

解释:

当B.methodB()打上 PROPAGATION_REQUIRED的事务标志,执行A.methodA()方法,当执行到b.methodB()的时候,如果A.methodA()方法有事务,则会创建一个依赖于当前事务的嵌套事务,如果 b.methodB()执行失败,只会回滚 b.methodB(),不会回滚A.methodA(),只有当A.methodA()执行完成后才会提交b.methodB()的事务,因为嵌套事务不能单独提交;如果A.methodA()回滚了,则会导致内部嵌套事务的回滚。如果A.methodA()方法没有事务,就会新建一个事务;

事务隔离级别:

ISOLATION_DEFAULT,

ISOLATION_READ_UNCOMMITTED,

ISOLATION_READ_COMMITTED,

ISOLATION_REPEATABLE_READ,

ISOLATION_SERIALIZABLE 

虚幻读 :一个事务读到了另一个事务已经提交的 insert 的数据导致多次查询结果不一致.

不可重复读 :一个事务读到了另一个事务已经提交的 update 的数据导致多次查询结果不一致. 

什么是脏读?

一个事务读到了另一个事务的未提交的数据

例如: 
  张三的工资为5000,事务A中把他的工资改为8000,但事务A尚未提交。 
  与此同时, 
  事务B正在读取张三的工资,读取到张三的工资为8000。 
  随后, 
  事务A发生异常,而回滚了事务。张三的工资又回滚为5000。 
  最后, 
  事务B读取到的张三工资为8000的数据即为脏数据,事务B做了一次脏读。

什么是幻读?

一个事务读到了另一个事务已经提交的 insert 的数据导致多次查询结果不一致.

例如: 
  目前工资为5000的员工有10人,事务A读取所有工资为5000的人数为10人。 
  此时, 
  事务B插入一条工资也为5000的记录。 
  这是,事务A再次读取工资为5000的员工,记录为11人。此时产生了幻读。

什么是不可重复读?

一个事务读到了另一个事务已经提交的 update 的数据导致多次查询结果不一致.

例如: 
  在事务A中,读取到张三的工资为5000,操作没有完成,事务还没提交。 
  与此同时, 
  事务B把张三的工资改为8000,并提交了事务。 
  随后, 
  在事务A中,再次读取张三的工资,此时工资变为8000。在一个事务中前后两次读取的结果并不致,导致了不可重复读。

提醒: 
不可重复读的重点是修改,同样的条件,你读取过的数据,再次读取出来发现值不一样了 
幻读的重点在于新增或者删除,同样的条件,第 1 次和第 2 次读出来的记录数不一样

不可重复读的和幻读很容易混淆,不可重复读侧重于修改update(行级别-通过行锁可以来控制),幻读侧重于新增或删除insert/delete(表级别-通过表锁来控制)。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

【幻读和不可重复读的区别如下:】参考博客:spring事务的隔离级别。如何避免脏读或者幻读_spring 事务的隔离级别有什么不足,怎么解决-优快云博客

数据库默认事务隔离级别

MYSQL:

InnoDB引擎的锁机制:InnoDB支持事务,支持行锁和表锁用的比较多,Myisam不支持事务,只支持表锁。

Mysql 在InnoDB引擎下默认:可重复读 ISOLATION_REPEATABLE_READ;通过加行锁,可以解决掉脏读、不可重复读


Oracle 默认:读已提交ISOLATION_READ_COMMITTED

1.ISOLATION_DEFAULT:这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别   

/**
	 * Use the default isolation level of the underlying datastore.
	 * All other levels correspond to the JDBC isolation levels.
	 * @see java.sql.Connection
	 */
	int ISOLATION_DEFAULT = -1;

翻译

使用底层数据存储的默认隔离级别。 所有其他级别对应于JDBC隔离级别。

2. ISOLATION_READ_UNCOMMITTED :这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻读。 

/**
	 * Indicates that dirty reads, non-repeatable reads and phantom reads
	 * can occur.
	 * <p>This level allows a row changed by one transaction to be read by another
	 * transaction before any changes in that row have been committed (a "dirty read").
	 * If any of the changes are rolled back, the second transaction will have
	 * retrieved an invalid row.
	 * @see java.sql.Connection#TRANSACTION_READ_UNCOMMITTED
	 */
	int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;

翻译

表示可能发生脏读,不可重复读和幻读。
这个级别允许一个事务改变的行被另一个事务读取,然后该行的任何改变被提交(“脏读”)。 如果任何更改回滚,则第二个事务将检索到无效行。

3. ISOLATION_READ_COMMITTED  :保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。

/**
	 * Indicates that dirty reads are prevented; non-repeatable reads and
	 * phantom reads can occur.
	 * <p>This level only prohibits a transaction from reading a row
	 * with uncommitted changes in it.
	 * @see java.sql.Connection#TRANSACTION_READ_COMMITTED
	 */
	int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;

翻译

指示防止脏读; 不可重复读取和幻像读取可能发生。
这个级别只禁止一个事务读取一行,并在其中有未提交的变化

4. ISOLATION_REPEATABLE_READ  :这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。 

/**
	 * Indicates that dirty reads and non-repeatable reads are prevented;
	 * phantom reads can occur.
	 * <p>This level prohibits a transaction from reading a row with uncommitted changes
	 * in it, and it also prohibits the situation where one transaction reads a row,
	 * a second transaction alters the row, and the first transaction re-reads the row,
	 * getting different values the second time (a "non-repeatable read").
	 * @see java.sql.Connection#TRANSACTION_REPEATABLE_READ
	 */
	int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;

翻译

指示防止脏读和不可重复读取; 幻读取可能会发生。
这个级别禁止一个事务读取一个没有提交变化的行,它也禁止如下情况:一个事务读取一行,
第二个事务改变行,第一个事务重新读取行,第二次获取不同的值(“不可重复读取”)。 

5. ISOLATION_SERIALIZABLE :这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻读。

/**
	 * Indicates that dirty reads, non-repeatable reads and phantom reads
	 * are prevented.
	 * <p>This level includes the prohibitions in {@link #ISOLATION_REPEATABLE_READ}
	 * and further prohibits the situation where one transaction reads all rows that
	 * satisfy a {@code WHERE} condition, a second transaction inserts a row
	 * that satisfies that {@code WHERE} condition, and the first transaction
	 * re-reads for the same condition, retrieving the additional "phantom" row
	 * in the second read.
	 * @see java.sql.Connection#TRANSACTION_SERIALIZABLE
	 */
	int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;

翻译

表示禁止脏读,不可重复读取和幻像读取。
该级别包括ISOLATION_REPEATABLE_READ中的禁止条件,并进一步禁止如下的情况:一个事务读取满足WHERE条件的所有行,第二个事务插入一条满足该WHERE条件的行,
并且第一个事务以这些相同的条件重新读取,在第二次读取中检索另外的“幻像”行。

源码解析

TransactionDefinition:事务的定义接口

DefaultTransactionDefinition:TransactionDefinition 的一个实现类

就是对上述属性设置一些默认值,默认的传播特性为PROPAGATION_REQUIRED ,隔离级别为ISOLATION_DEFAULT 

TransactionAttribute接口:定义对什么类型的异常进行回滚

DefaultTransactionAttribute:TransactionAttribute的实现类

 指明了默认对RuntimeException 和Error都进行回滚

事务模板类TransactionTemplate(这个不常用,通常用Spring的AOP管理事务)

 他的核心是里面有PlatformTransactionManager 这个事务管理类,用它来对事务提交和回滚。我们的业务逻辑只要写在TransactionCallback.doInTransaction()方法里面既可以,每次执行这个方法前,先会transactionManager.getTransaction(this)开启一   个事务,执行TransactionCallback.doInTransaction()异常的话会调用transactionManager.rollback(status)来回滚事务,正确的话就会调用transactionManager.commit(status)提交事务; 

public class TransactionTemplate extends DefaultTransactionDefinition implements TransactionOperations, InitializingBean {
    protected final Log logger = LogFactory.getLog(this.getClass());
    private PlatformTransactionManager transactionManager;

    public TransactionTemplate() {
    }

    public TransactionTemplate(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    public TransactionTemplate(PlatformTransactionManager transactionManager, TransactionDefinition transactionDefinition) {
        super(transactionDefinition);
        this.transactionManager = transactionManager;
    }

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    public PlatformTransactionManager getTransactionManager() {
        return this.transactionManager;
    }

    public void afterPropertiesSet() {
        if(this.transactionManager == null) {
            throw new IllegalArgumentException("Property 'transactionManager' is required");
        }
    }

    public <T> T execute(TransactionCallback<T> action) throws TransactionException {
        if(this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
            return ((CallbackPreferringPlatformTransactionManager)this.transactionManager).execute(this, action);
        } else {
            TransactionStatus status = this.transactionManager.getTransaction(this);

            Object result;
            try {
                result = action.doInTransaction(status);//业务逻辑代码写在这里
            } catch (RuntimeException var5) {
                this.rollbackOnException(status, var5);
                throw var5;
            } catch (Error var6) {
                this.rollbackOnException(status, var6);
                throw var6;
            } catch (Exception var7) {
                this.rollbackOnException(status, var7);
                throw new UndeclaredThrowableException(var7, "TransactionCallback threw undeclared checked exception");
            }

            this.transactionManager.commit(status);
            return result;
        }
    }

    private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException {
        this.logger.debug("Initiating transaction rollback on application exception", ex);

        try {
            this.transactionManager.rollback(status);
        } catch (TransactionSystemException var4) {
            this.logger.error("Application exception overridden by rollback exception", ex);
            var4.initApplicationException(ex);
            throw var4;
        } catch (RuntimeException var5) {
            this.logger.error("Application exception overridden by rollback exception", ex);
            throw var5;
        } catch (Error var6) {
            this.logger.error("Application exception overridden by rollback error", ex);
            throw var6;
        }
    }
}

基于上面的模板类,我们可以这样来实现数据库的事务,把每个逻辑都写在TransactionCallback.doInTransaction()方法里面,他会自动帮我们提交事务

TransactionTemplate transactionTemplate=new TransactionTemplate();
transactionTemplate.setTransactionManager(platformTransactionManager);
        
transactionTemplate.execute(new TransactionCallback<String>() {

            @Override
            public String doInTransaction(TransactionStatus status) {
                //数据库操作
                return "success";
            }
});

但是这样会使每个数据库方法都要要写到TransactionCallback.doInTransaction(),其实耦合度还是很高。Spring于是推出了他的AOP管理事务!!!

参考spring源码解析--事务篇(前篇) - 等待九月 - 博客园

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值