本文是<实现 Spring 的事务控制>系列文章中一篇。本文假设读者已经阅读并理解《实现 Spring 的事务控制,之一(必要的概念)》文中所涉及的概念(当前连接、引用计数),以及数据库连接的(new状态)
RROPAGATION_REQUIRES_NEW(独立事务)
定义:
如果当前存在事务则挂起当前事务,并开启一个全新的事务。新事务与已存在的事务之间彼此没有关系。
解释:
REQUIRES_NEW 行为强调了独立性。它保证了每个事务状态管理范围内锁使用的数据库连接是彼此不一样的。例如独立事务会满足“A事务中存在B事务,当B事务递交的时候不影响A事务。A,B两个事务之间不存在相互关联关系。“
时间 | 事务1 | 事务2 |
T1 | 开始事务 | |
T2 | 操作1... | |
T3 | |
开始事务 |
T4 | 操作2... | |
T5 | 递交事务 | |
T6 | |
回滚事务 |
定义中提到”挂起事务“这句话怎么理解?
所谓“挂起”指的是将当前线程使用的数据库连接,暂时保存起来不在使用。取而代之的是一个新的数据库库连接。
与挂起相对应的还有一个“恢复事务”,它们的操作是成对出现的。恢复就是将当前数据库连接释放掉,然后将以前挂起的那个数据库连接重新设为当前数据库连接。
REQUIRES_NEW 行为由于出现了“挂起”这样的操作,因此它也带来了一个全新的事务状态叫“Suspent”。
具有 Suspent 特征的事务
首先,Suspent 特征只会出现在两种重播行为中
-
独立事务(REQUIRES_NEW)
-
非事务方式(NOT_SUPPORTED)
现在让我们看一看具有 Suspent 特征的事务究竟有什么特殊的。首先要想产生 Suspent 特征需要满足下面这样的条件。
-
当前事务状态中不具备“new”特征
在第一篇文章中介绍过,new状态是“标记当事务管理器创建新的事务状态时,当前连接的事务状态是如何的”。
我们一瞧便知道,凡是不满足new状态条件的那么就只有一种可能了。在事务管理器创建事务对象的时候,当前连接已经开启了事务。这也难怪,因为独立事务的定义中第一句话就明白的写着:“如果当前存在事务则挂起当前事务”。
我们再往下看“并开启一个全新的事务”这句话怎么解释?
这句话的前一句已经告诉我们,如果存在事务就会挂起,然后在执行开启新事务。这是由于挂起事务之后,事务管理器会重新为我们申请一个新的数据库连接。这个连接由于还尚未开启数据库事务,因此需要开启。
工作原理
开启事务
事务管理器在创建 REQUIRES_NEW 行为事务时,会取得当前连接这一过程会持有当前连接(引用计数+1)。注意:此时持有的数据库连接并不一定是最终在操作阶段使用的数据库连接。
然后通过判断当前连接是否存在事务状态,来决定是否通过 执行挂起事务操作。一旦执行了挂起事务的操作就相当于清空了当前的数据库连接。所以接下来需要重新申请一个数据库连接,新申请的数据库连接事务管理器也会持有它(引用计数+1)。
接下来随后会通过 doBegin 方法为这个新连接开启事务,而这个新的连接恰恰满足了“new”状态特征。因此 REQUIRES_NEW 行为事务中的事务状态是具备“new”状态的。但要注意,这个new状态指的是当前连接,也就是后申请的那个数据库连接事务状态。
事务中的数据库操作
无论在开启事务的时候Connection 此时此刻,可以直接使用 Connection 接口畅快的使用数据库操作。由于每次进行数据库操作都要反复的申请和释放数据库连接。这会反复的使引用计数 +1,-1。
递交/回滚事务
在 REQUIRED 行为中我们知道,凡是具有 new 状态的事务。在最后执行 commit & rollback 动作时都会真实的去执行它们。
但是在 REQUIRES_NEW 行为中,除了真实递交事务之外还会判断事务中是否存在“Suspent”特征。
在前面我们知道在 REQUIRES_NEW 行为下有可能会同时持有两个数据库连接的引用,因此在 cleanupAfter 过程中虽然释放了当前连接的引用计数,但是还有一个可能存在的挂起事务还没处理。
为此 REQUIRES_NEW 行为不同于 REQUIRES 行为的是增加了一个恢复挂起事务的过程,随后又将挂起的事务引用释放掉。
转载于:https://my.oschina.net/ta8210/blog/199947