一. 有哪几种传播机制?
在org.springframework.transaction.annotation.Propagation
类中,定义类七种传播机制,结合官网和源码来看看一共有哪些。
1. 理解 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.
*/
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
翻译一下意思就是,支持当前事务,如果当前不存在事务,就创建一个新的事务来执行。是Spring事务传播机制的默认实现。
再来看看官网是怎么解释的,先上个图:
重要的点
首先会强制以事务方式执行,如果在当前作用域还不存在事务,则开启,若在外部存在一个作用范围更大的事务,则加入到外部事务中去。当然前提是这些事务都处于一个线程中。
注意,如果内部事务范围设置只回滚的标记,而没有异常抛出(内部处理了Exception异常而没有抛出),外部事务是无感知的。外部事务仍旧会commit。
在Spring中我们可以这么用,伪代码表示,具体用法这里不展开:
// PROPAGATION_REQUIRED 级别
methodA(){
// do something with database.
// ...
methodB();
}
// PROPAGATION_REQUIRED 级别
methodB(){
// do something
// ...
}
用传统的JDBC代码来表示,就是这样的:
methodA(){
try{
con = getConnection();
con.setAutoCommit(false);
//方法调用
methodB();
//提交事务
con.commit();
} Catch(RuntimeException ex) {
//回滚事务
con.rollback();
} finally {
//释放资源
}
}
这种传播机制的应用,应该是比较常见的,更多的是对于上面methodB()
方法的一个代码公用。
2. 理解 REQUIRES_NEW
/**
* Create a new transaction, and suspend the current transaction if one exists.
* Analogous to the EJB transaction attribute of the same name.
*/
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
意思是,如果当前存在事务,则挂起当前事务,创建一个新的事务去执行。
看下官网的解释,继续上图:
翻译一下官网的原文,个人理解,有疑问烦请指正。
和PROPAGATION_REQUIRED的传播属性相比,它总是需要开启一个新的独立物理(数据库)事务来执行每个受影响的事务范围,永远不会加入到另一个已存在的外部事务里面。在这种机制下,基础事务资源都是不同的,并且每个都可以独立的提交和回滚,外部事务不受内部事务回滚的影响。这样一个独立的内部事务还可以声明自己的隔离级别,事务超时时间和事务的只读设置,而不受外部事物的影响。
Spring里面可以这么用:
// PROPAGATION_REQUIRED 级别
methodA(){
// do something with database.
// ...
methodB();
}
// PROPAGATION_REQUIRES_NEW 级别
methodB(){
// do something
// ...
}
这时候,methodB
会以独立事务的形式去执行。
methodA (){
TransactionManager tm = null;
try{
//获得一个JTA事务管理器
tm = getTransactionManager();
tm.begin();//开启一个新的事务
Transaction ts1 = tm.getTransaction();
doSomeThing();
tm.suspend();//挂起当前事务
try{
tm.begin();//重新开启第二个事务
Transaction ts2 = tm.getTransaction();
methodB();
ts2.commit();//提交第二个事务
} Catch(RunTimeException ex) {
ts2.rollback();//回滚第二个事务
} finally {
//释放资源
}
//methodB执行完后,恢复第一个事务
tm.resume(ts1);
doSomeThingB();
ts1.commit();//提交第一个事务
} catch(RunTimeException ex) {
ts1.rollback();//回滚第一个事务
} finally {
//释放资源
}
}
应用场景:
3. 理解 PROPAGATION_NESTED
/**
* Execute within a nested transaction if a current transaction exists,
* behave like {@code 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 org.springframework.jdbc.datasource.DataSourceTransactionManager
*/
NESTED(TransactionDefinition.PROPAGATION_NESTED);
如果当前存在事务,则创建一个嵌套事务,否则以REQUIRED的形式来执行。
来看看官网的解释,翻译一下:
PROPAGATION_NESTED uses a single physical transaction with multiple savepoints that it can roll back to. Such partial rollbacks let an inner transaction scope trigger a rollback for its scope, with the outer transaction being able to continue the physical transaction despite some operations having been rolled back. This setting is typically mapped onto JDBC savepoints, so it works only with JDBC resource transactions. See Spring’s DataSourceTransactionManager.
PROPAGATION_NESTED 使用一个具有多个保存点的物理事务,可以回滚到该事务。这种部分回滚允许内部事务作用域触发其作用域的回滚,尽管某些操作已回滚,但外部事务仍能够继续物理事务。此设置通常映射到JDBC保存点,因此它仅适用于JDBC资源事务。可以看Spring的DataSourceTransactionManager类
那按它的意思,再来看看DataSourceTransactionManager这个类。先看下这个类的结构:
主要关注继承的类和自身的一些代码实现,AbstractPlatformTransactionManager和可以看Spring的DataSourceTransactionManager类。
NESTED 与 REQUIRES_NEW 的区别:
- 它们非常类似,都像一个嵌套事务,如果不存在一个活动的事务,都会开启一个新的事务。使用 REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它需要JTA事务管理器的支持。
- 使用NESTED时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。DataSourceTransactionManager使用savepoint支持NESTED时,需要JDBC 3.0以上驱动及1.4以上的JDK版本支持。其它的JTA TrasactionManager实现可能有不同的支持方式。
- REQUIRES_NEW 启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行。
- 另一方面, NESTED 开始一个 “嵌套” 事务, 它是已经存在事务的一个真正的子事务. 嵌套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 嵌套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。
- 由此可见, REQUIRES_NEW 和 NESTED 的最大区别在于, REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 嵌套事务也会被 commit, 这个规则同样适用于 roll back.