本文是<实现 Spring 的事务控制>系列文章中一篇。本文假设读者已经阅读并理解《实现 Spring 的事务控制,之一(必要的概念)》文中所涉及的概念(当前链接、引用计数),以及数据库链接的(new状态) java
解释 REQUIRED 行为并不困难。它是指若是当前链接中若是已经开启了数据库事务,而且有程序在控制这个事务。那么在处理 REQUIRED 行为时将会忽略 commit/rollback 操做,这是加入已有事务的特征。 数据库
时间 | 事务1 | 事务2 |
T1 | 开始事务 | |
T2 | 操做1... | |
T3 | |
加入事务1 |
T4 | 操做2... | |
T5 | 操做3... | |
T6 | 递交事务 | |
定义中有一句“若是没有则开启一个新的事务”这句话怎么理解? 测试
所谓“没有”指的是当前链接中不存在事务(事务状态为false)。“则开启新的事务”这里的能够理解为,若是没有事务那么才开启新的事务。并且新开启的事务是须要事务管理器进行维护的。 spa
开启事务 .net
事务管理器在建立 REQUIRED 类型事务时,会取得当前链接这一过程会持有当前链接(引用计数+1)。 code
而后经过判断当前链接是否存在事务状态,来决定是否经过 doBegin 方法开启事务。这个规则为:若是存在事务状态则忽略,不然开启。 对象
通常状况下在调用 doBegin 方法以前事务都是还没有开启的。咱们知道没有开启事务的链接特征是 autoCommit 属性为 true。在这种状况下每一次SQL操做都是独立成为一个事务,所以多条SQL语句之间是不存在事务关系的。换句话说只要进入 doBegin 方法的数据库链接都是没有开启事务的链接,它们都知足 new 状态的特征。 blog
事务中的数据库操做 接口
不管在开启事务的时候Connection 此时此刻,能够直接使用 Connection 接口畅快的使用数据库操做。因为每次进行数据库操做都要反复的申请和释放数据库链接。这会反复的使引用计数 +1,-1。 事务
递交/回滚事务
REQUIRED 行为告诉咱们,若是环境中有事务控制,那么该行为下的事务管理器将不做为。全部事务控制操做交给环境中的事务控制来处理。
可是若是环境中不存在事务,那么事务管理器是须要负责 commit & rollback 的。
这时候在前面肯定的 new 状态就能够发挥做用了。由于根据 new 状态的特征描述咱们知道凡是持有 new 状态的数据库链接在开启事务以前是不存在先前事务的。做为后来者的事务管理器而言,能够放心大胆的去递交事务。
若是不具有 new 特征则正好说明外部在事务管理器以外有其它程序代码代为控制了事务。
例以下面这个业务逻辑(伪代码):
public static void main(){ DataSource ds= ......; Connection conn = DataSourceUtil.getConnection(ds);//取得数据库链接,会致使引用计数+1 conn.setAutoCommit(false);//开启事务 conn.execute("update ...");//预先执行的 update 语句 insertData();//执行数据库插入 if (test< 20){ conn.rollback();//测试状态不知足的条件下,将插入的测试数据连通以前的 update 一同回滚。 } DataSourceUtil.releaseConnection(conn,ds);//释放链接,引用计数-1 } public static void insertData(){ TransactionStatus status = tm.getTransaction(PROPAGATION_REQUIRED);//引用计数+1 jdbc.execute("insert into ...");//执行插入语句,在执行过程当中引用计数会 +1,而后在-1 tm.commit(status);//引用计数-1 }
在上面这个例子中,第四行中开启了事务。所以在执行 insertData 的时候事务管理器会认为当前链接中已经存在事务,新的事务只须要加入到当前的事务中便可。并不须要独立控制事务。所以在最后 insertData 方法结束的时,其插入的数据还未真正的插入到数据库。随后外部事务控制代码又将全部数据都回滚了。
上面的代码修改一下以下:
public static void main(){ DataSource ds= ......; Connection conn = DataSourceUtil.getConnection(ds);//取得数据库链接,会致使引用计数+1 conn.execute("update ...");//预先执行的 update 语句 insertData();//执行数据库插入,数据会进入数据库。 jdbc.execute("insert into ...");//这条插入的数据会被插入 DataSourceUtil.releaseConnection(conn,ds);//释放链接,引用计数-1 } public static void insertData(){ TransactionStatus status = tm.getTransaction(PROPAGATION_REQUIRED);//引用计数+1 jdbc.execute("insert into ...");//执行插入语句,在执行过程当中引用计数会 +1,而后在-1 tm.commit(status);//引用计数-1 }
上面的代码中咱们能够看出,insertData 方法以外的程序并无控制事务。 insertData 中的事务管理器在建立事务对象的时候,因为当前链接知足 new 状态特征。因此在最后 tm.commit(status) 时会执行事务递交操做。
而上面第8行,所使用的链接因为也没有开启事务。因此它会以自动递交事务的方式去运行。
关于 afterClear
afterClear 阶段的做用是将当前链接的状态恢复到 getTransaction 方法以前。而且因为 getTransaction 方法会持有当前链接(引用计数++),所以 afterClear 方法也会释放掉这个链接的持有(引用计数--)。