原文地址:http://dev2dev.bea.com.cn/blog/davidturing/200803/transaction_04_923.html
目前,似乎很少有支持嵌套事务的中间件,但嵌套事务确实存在。
假定有Method A, Method B, Method C
A 调用 B,C
ServiceHost {
/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
void invoke() {
try{
ServiceA.invoke()
} catch (Bussiness.A.Exception) {
abort()
}
try {
ServiceB.invoke();
} catch (Bussiness.B.Exception) {
ServiceC.invoke();
} catch (Bussiness.C.Exception) {
ServiceD.invoke();
} catch (Bussiness.D.Exception) {
abort()
}
try {
ServiceE.invoke()
} catch (Bussiness.E.Exception) {
ServiceF.invoke()
}catch (Bussiness.F.Exception) {
abort()
}
}
}
/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
void invoke() {
try{
ServiceA.invoke()
} catch (Bussiness.A.Exception) {
abort()
}
try {
ServiceB.invoke();
} catch (Bussiness.B.Exception) {
ServiceC.invoke();
} catch (Bussiness.C.Exception) {
ServiceD.invoke();
} catch (Bussiness.D.Exception) {
abort()
}
try {
ServiceE.invoke()
} catch (Bussiness.E.Exception) {
ServiceF.invoke()
}catch (Bussiness.F.Exception) {
abort()
}
}
}
ServiceA {
/**
* 事务属性配置为 PROPAGATION_NESTED,
× 即该事务需要被嵌套
*/
void methodA() {
}
}
/**
* 事务属性配置为 PROPAGATION_NESTED,
× 即该事务需要被嵌套
*/
void methodA() {
}
}
ServiceA, ServiceB, ServiceC, ServiceD, ServiceE, ServiceF
都配置为
PROPAGATION_NESTED
通过这样的嵌套规约,我们可以满足业务完整性的需求,一个 ServiceParent 业务,只允许出现 A-B-E , A-B-F, A-C-E, A-C-F, A-D-E , A-D-F 的组合,其他组合,如 A-B-C, A-E, B-F 都是不允许的。
通过这样的嵌套规约,我们可以满足业务完整性的需求,一个 ServiceParent 业务,只允许出现 A-B-E , A-B-F, A-C-E, A-C-F, A-D-E , A-D-F 的组合,其他组合,如 A-B-C, A-E, B-F 都是不允许的。
术语上,
ServiceA, B, C, D, E, F
的事务均是
ServiceParent
事务的子事务,现在,是
ServiceParent
嵌套
ServiceA-F
,且嵌套事务的几个特性如下:
1,
假定子事务
(ServiceA-F)
处于活动状态
(active)
,则
parent
事务
(ServiceParent)
不能执行任何其他操作,父事务只能
commit
事务,
abort
事务以及创建更多其它子事务。
2,
若子事务
(ServiceA)
被提交了,此时父事务则能够看到子事务做出的任何修改,这些修改,对其他子事务来说是隐藏的,直到父事务提交为止。
3,
同样地,如果被嵌套的事务
ServiceA
被回滚了,则它也不会对父事务有任何影响,且父事务
servieParent
也不会看到
ServiceA
曾经修改过的数据。
4,
最终父事务提交、回滚才会决定到子事务的提交、回滚。表达的形象点,若
ServiceA
“提交”后,
ServiceParent
却因为
Service B/C/D
都无法成功执行,
ServiceA
会回滚。这种做法依赖于
JDBC 3.0 SavePoint
技术。
5,
嵌套子事务提交后,它对
DB
的锁其实并没有释放,这些锁的控制权被转交给父事务,直到父事务提交(或回滚)后,锁才会真正释放
6,
目前,嵌套的深度不收控制,也就是,我们可对
method
进行以深度嵌套。
现在,我们略略了解一下
JDBC SavePoint
技术,它给予了我们这样的能力,在一个事务里面,我们能够将已经提交的事务状态,恢复到一个事务
commit
以前的任意定点
(
这个点就是
SavePoint)
。
举个例子,假设我们面临这样一种情况,
methodA
是运算代价非常大的事务操作,
methodB, methodC
都是轻量级的事务,我们需要执行这样一个事务
Tx={methodA->methodB->methodC}
。
没有
SavePoint
之前,如果
methodA
执行成功,但是恰恰
methodB
失败了,那么
methodA
所有成果必须回滚,
methodC
也别想执行了,因为
methodA
、
methodB
、
methodC
都在同一个
Tx
事务中;
JDBC3.0
的
SavePoint
技术为我们提供了一个无需回滚
MethodA
的折衷办法,可以让我们在保留
methodA
的成果的同时,仅仅回滚
methodB
,然后继续执行
methodC
。
回滚到SavePoint的代码类似于:
Statement stmt
=
conn.createStatement();
int rows = stmt.executeUpdate( " INSERT INTO TAB1 (COL1) VALUES " +
" (’FIRST’) " );
// set savepoint
Savepoint svpt1 = conn.setSavepoint( " SAVEPOINT_1 " );
rows = stmt.executeUpdate( " INSERT INTO TAB1 (COL1) " +
" VALUES (’SECOND’) " );
conn.rollback(svpt1);
conn.commit();
int rows = stmt.executeUpdate( " INSERT INTO TAB1 (COL1) VALUES " +
" (’FIRST’) " );
// set savepoint
Savepoint svpt1 = conn.setSavepoint( " SAVEPOINT_1 " );
rows = stmt.executeUpdate( " INSERT INTO TAB1 (COL1) " +
" VALUES (’SECOND’) " );
conn.rollback(svpt1);
conn.commit();