Spring事务传播机制
对事务的学习总体来讲应该包含以下几个部分:
- 事务的概念:数据库层面的,简单了解什么是事务,以及隔离级别、事务的提交、回滚、保存点等基本概念。
- 动态代理以及Spring AOP,是Spring框架实现事务控制的底层技术基础。
- Spring框架事务的实现方式。
- Spring框架事务实现的底层原理。
可以循序渐进逐步学习,也可以单独学习其中某一部分,但是只有全部彻底掌握了,才能对事务有一个全局的了解。
一般来讲,程序员实现事务控制有两种选择:编程式事务、声明式事务。从代码编写的角度讲,编程式事务太麻烦,现在用的很少了,20年前的程序员绝大部分用的都是编程式事务,非常麻烦,需要自己获取连接、开启事务、提交或回滚事务、关闭连接等。
声明式事务是基于AOP实现的,现在JAVA世界的绝大部分项目都是基于Spring框架实现的,Spring框架、尤其是Springboot框架对事务管理的支持非常友好,使用非常简单。
Spring框架实现事务控制其实非常简单,主要的注解只有两个:
- @EnableTransactionManagement
- @Transactional
@EnableTransactionManagement注解是负责在配置类中打开Spring的事务管理功能,@Transactional是具体负责开启及实现事务控制的。从应用层面来讲,@Transactional是学习的重点。
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Transactional { @AliasFor("transactionManager") String value() default ""; @AliasFor("value") String transactionManager() default ""; String[] label() default {}; Propagation propagation() default Propagation.REQUIRED; Isolation isolation() default Isolation.DEFAULT; int timeout() default -1; String timeoutString() default ""; boolean readOnly() default false; Class<? extends Throwable>[] rollbackFor() default {}; String[] rollbackForClassName() default {}; Class<? extends Throwable>[] noRollbackFor() default {}; String[] noRollbackForClassName() default {}; }
@Transactional的重要属性包括:
- 事务传播机制Propagation
- 隔离级别isolation
- 等待超时时间timeout
- 回滚条件rollbackFor
- 不回滚条件noRollbackFor
其实Spring事务管理是重度依赖于数据库底层的支持的,尤其是类似隔离级别、等待超时时间等概念,都是直接依赖于数据库底层去实现的,Spring事务管理其实什么都不需要干,只需要把配置好的属性传递给数据库连接、交给数据库去完成即可。
而事务传播机制Propagation与其他特性不同,是spring框架事务管理功能的重头戏。事务传播机制负责控制不同事务发生的时候,上层事务与下层事务之间的关系。
Spring定义了以下7种事务传播机制:
REQUIRED
Support a current transaction, create a new one if none exists. Analogous to EJB transaction attribute of the same name. This is the default setting of a transaction annotation.
需要启用事务,当前不存在事务的话,就创建一个新事务,如果存在事务的话,则加入。
这是Spring的默认传播机制。
比如事务控制的经典案例银行转账交易:转账交易由转出和转入两部分功能组成,如果我们按照如下方式实现的话:
@Transactional public void transfer(Acct acct1,Acct acct2){ withdrawl(acct2); deposit(acct1); } @Transactional(propagation=Propagation.REQUIRED) public void deposit(){ acct1.deposit(); } @Transactional(propagation=Propagation.REQUIRED) public void withdrawl(){ acct2.wiwithdrawl(); }
转出和转入方法就必须要启用事务、事务传播机制可以设置为REQUIRED,那么在转出交易调用的时候,因为transfer方法已经开启了一个事务,所以,转出方法withdrawl不再开启新事物、而是使用已经存在的事务,同样,转入方法deposit也使用该事务。这样,转出交易和转入交易就被包含在了同一个事务中,能够实现要么全部成功、要么全部失败的交易目标。
SUPPORTS
Support a current transaction, execute non-transactionally if none exists. Analogous to EJB transaction attribute of the same name. Note: For transaction managers with transaction synchronization, SUPPORTS is slightly different from no transaction at all, as it defines a transaction scope that synchronization will apply for. As a consequence, the same resources (JDBC Connection, Hibernate Session, etc) will be shared for the entire specified scope. Note that this depends on the actual synchronization configuration of the transaction manager. See Also: org.springframework.transaction.support.AbstractPlatformTransactionManager.setTransactionSynchronization
当前如果已经开启了事务的话,则加入,如果没有已经开启的事务的话,也无所谓,当前方法不开启事务。
MANDATORY
Support a current transaction, throw an exception if none exists. Analogous to EJB transaction attribute of the same name.
当前方法被调用的时候必须有事务,如果没有已经开启的事务的话,就不干,抛异常。
上面银行转账交易,也可以采用MANDATORY。
REQUIRES_NEW
Create a new transaction, and suspend the current transaction if one exists. Analogous to the EJB transaction attribute of the same name. NOTE: Actual transaction suspension will not work out-of-the-box on all transaction managers. This in particular applies to org.springframework.transaction.jta.JtaTransactionManager, which requires the javax.transaction.TransactionManager to be made available to it (which is server-specific in standard Java EE). See Also: org.springframework.transaction.jta.JtaTransactionManager.setTransactionManager
启用新事务,如果当前已经存在一个事务的话,则挂起该事务。
被挂起的事务不会受到开启的新事务执行结果的影响,无论新事务被提交还是被回滚。
NOT_SUPPORTED
Execute non-transactionally, suspend the current transaction if one exists. Analogous to EJB transaction attribute of the same name. NOTE: Actual transaction suspension will not work out-of-the-box on all transaction managers. This in particular applies to org.springframework.transaction.jta.JtaTransactionManager, which requires the javax.transaction.TransactionManager to be made available to it (which is server-specific in standard Java EE). See Also: org.springframework.transaction.jta.JtaTransactionManager.setTransactionManager
不启用事务,如果当前已经存在一个事务的话则挂起当前事务。
NEVER
Execute non-transactionally, throw an exception if a transaction exists. Analogous to EJB transaction attribute of the same name.
不启用事务,如果当前已经存在一个事务的话就抛出异常。
NESTED
Execute within a nested transaction if a current transaction exists, behave like REQUIRED otherwise. There is no analogous feature in EJB. Note: Actual creation of a nested transaction will only work on specific transaction managers. Out of the box, this only applies to the JDBC DataSourceTransactionManager. Some JTA providers might support nested transactions as well. See Also: org.springframework.jdbc.datasource.DataSourceTransactionManager
嵌套事务,如果当前已经存在一个事务的话,则行为类似于REQUIRED。注意:嵌套事务仅针对特定的事务管理器,需要特定事务管理器的支持,是否生效取决于JDBC数据源的事务管理器。
嵌套事务的实际含义是:如果当前存在一个事务的话,则保存当前事务的savepoint(保存点),并加入事务,如果当前不存在事务的话,就启用一个新事务。
嵌套事务实际使用了数据库的savepoint,需要数据库支持savepoint,如果数据库不支持savepoint,那么这个NESTED也就不会生效。
savepoint的意思对当前事务已经执行的数据库操作记录一个保存点,新方法加入事务后,如果执行成功则一起提交,如果执行失败则只回滚新方法的操作,不回滚保存点以前的数据库操作。
以上Spring提供的7种事务传播机制,我们应该都有所了解。但是,实际应用场景中能用到的,或者用的比较多的,也就REQUIRED、以及REQUIRED_NEW。一般情况下REQUIRED足够满足需要了,有些应用框架需要记录操作日志,审计用,不论操作成功还是失败都需要记录。这种情况下,REQUIRED_NEW即可。