大鸟的学习乐园
路漫漫其修远兮,吾将上下而求索
posts - 26,comments - 27,trackbacks - 0
在WebLogic 7/8/9中,对TransactionManager的引用可以通过在Weblogic 7的TxHelper中定义的静态方法getTransactionManager()而获得。该类在WebLogic 8中被否决了,而用TransactionHelper取而代之。

public TransactionManager getFromWebLogicFactory()
throws Exception {
try {
// WebLogic 8/9
return
weblogic.transaction.TransactionHelper
.getTransactionManager();
}
catch (ClassNotFoundException ex) {}

try {
// WebLogic 7
return
weblogic.transaction.TxHelper
.getTransactionManager();
}
catch (ClassNotFoundException ex) {}

return null;
}


使用TransactionManager

  一旦成功地获得TransactionManager引用,就可以用它来挂起和恢复事务,正如以下的示例代码所示。

// obtain UserTransaction object and start transaction
InitialContext ctx = new InitialContext();
UserTransaction userTransaction = (UserTransaction)
ctx.lookup("java:comp/UserTransaction");

// start first transaction
userTransaction.begin();

// obtain TransactionManager
// using one of the methods described above
TransactionManager tm = getTransactionManager();

// suspend transaction
// suspend() returns reference to suspended
// Transaction object which later should be passed
// to resume()
Transaction transaction = tm.suspend();

// here you can do something outside of transaction
// or start new transaction,
// do something and then commit or rollback
userTransaction.begin();

// commit subtransaction
userTransaction.commit();

// resume suspended transaction
tm.resume(transaction);

// commit first transaction
userTransaction.commit();
...



正如您所看到的,在TransactionManager接口的帮助下,可以对UserTransaction所提供的标准功能进行扩展,在BMT代码中实现与CMT bean相同的灵活性水平。

  需要知道的是,当事务被挂起时,并不意味着事务的计时器停止了。换言之,如果把事务的超时设定为30秒,而事务已挂起了20秒并恢复了,那么该事务只剩10秒就要到达超时了。事务的挂起会解除事务与正在运行的线程之间的关联,然后resume()调用会再次将其关连,而不影响事务超时计时器。

  已知问题

  因为J2EE规范并不要求TransactionManager在J2EE容器中的可用性以及功能(虽然从底层的JTA基础架构中我们知道它应该是存在的),所以有些应用服务器中存在一些问题。例如,在WebLogic 7、8以及9(beta版)中有种特别奇怪的现象:假如一个事务被标记为回滚(通过调用UserTransaction.setRollbackOnly()),然后被挂起,当被挂起的事务尝试恢复时,它将会出现如下的提示:
javax.transaction.InvalidTransactionException: Attempt to resume an inactive transaction

以下代码说明了该行为:

// obtain UserTransaction object and start transaction
InitialContext ctx = new InitialContext();
UserTransaction userTransaction = (UserTransaction)
ctx.lookup("java:comp/UserTransaction");
userTransaction.begin();

// mark for rollback
userTransaction.setRollbackOnly();

TransactionManager tm = getTransactionManager();

// suspend transaction
Transaction transaction = tm.suspend();

// resume suspended transaction
// this call will fail with InvalidTransactionException
// in WebLogic
tm.resume(transaction);
...


幸运的是,对于该问题,有一个应急方案。WebLogic的TransactionManager实现连同标准的resume(Transaction transaction)方法,有一个可用于替代的forceResume方法。以下的代码展示了一种在WebLogic中运行代码时需要用到的模式。注意,在这种情况下,应该把对TransactionManager的引用转换到WebLogic的定制实现接口(WebLogic 7中的weblogic.transaction.TransactionManager或WebLogic 8以及更高版本中的weblogic.transaction.ClientTransactionManager)。

...
// obtain UserTransaction object and start transaction
InitialContext ctx = new InitialContext();
UserTransaction userTransaction = (UserTransaction)
ctx.lookup("java:comp/UserTransaction");
userTransaction.begin();

// mark for rollback
userTransaction.setRollbackOnly();

TransactionManager tm = getTransactionManager();

// suspend transaction
Transaction transaction = tm.suspend();

// resume suspended transaction
try ...{
// first try standard JTA call
tm.resume(transaction);
}
catch (InvalidTransactionException e) ...{
// standard method failed, try forceResume()
if (tm instanceof
weblogic.transaction.ClientTransactionManager) ...{
// WebLogic 8 and above
((weblogic.transaction.ClientTransactionManager)tm)
.forceResume(transaction);
}
else if (tm instanceof
weblogic.transaction.TransactionManager) ...{
// WebLogic 7
((weblogic.transaction.TransactionManager)tm)
.forceResume(transaction)
}
else ...{
// cannot resume
throw e;
}
}
...


TransactionManager在Spring Framework中的用法

  在流行的Spring framework中,以上所描述的技术都被广泛地用于事务代理。应该注意的是,每次使用PROPAGATION_REQUIRES_NEW或PROPAGATION_NOT_SUPPORTED、事务属性以及JtaTransactionManager来配置Spring的TransactionProxyFactoryBean时,Spring就会用JTA的TransactionManager来挂起和恢复事务。Spring太智能了,但有时太智能了反而使我无法接受——甚至没有指定,它就会发现容器中的TransactionManager。例如,当在一个Spring应用程序上下文中定义JtaTransactionManager时,就可以为UserTransaction提供一个JNDI名,而如果UserTransaction也实现了它,它就将“自动检测”TransactionManager。如上所示,这对WebLogic、Orion和Oracle OC4J都适用。

  有时,这可能并不是我们所想要的,尤其是当您想要严格遵循J2EE/EJB规范,并确保跨所有J2EE容器的完全可移植性的时候。正如我们所看到的,有时候编程式的事务挂起和恢复可能存在一些问题。虽然Spring知道如何绕过这些问题,至少是对于上面所描述的问题,即,在Welogic中,在挂起之前将事务标记为回滚。在这种情况下,可以在配置JtaTransactionManager时把autodetectTransactionManager属性设定为false。如果这么做了,那么任何使用PROPAGATION_REQUIRES_NEW或PROPAGATION_NOT_SUPPORTED事务属性的尝试都会抛出TransactionSuspensionNotSupportedException而失败。但PROPAGATIO-REQUIRED、PROPAGATION-SUPPORTS、PROPAGATION-MANDATORY以及PROPAGATION-NEVER应该会正常运行。这对应于JTA Usertransaction所提供的功能,而且在任一个兼容J2EE的容器中都起作用。

  以下是Spring应用程序上下文中的事务管理器定义,其中禁用了TransactionManager自动检测:

<bean id="transactionManager" class=
"org.springframework.transaction.jta.JtaTransactionManager">
<property name="userTransactionName">
<value>javax.transaction.UserTransaction</value>
</property>
<property name="autodetectTransactionManager">
<value>false</value>
</property>
</bean>

结束语

  J2EE规范不要求对JIA TransactionManager接口的支持。但由于J2EE使用JTA作为它的底层事务基础架构,所以几乎所有的J2EE服务器都把它公开为J2EE的扩展。存在一些已知的兼容性问题,而在某些情况下,可以用特定于容器的代码来绕过这些问题。但是,如果需要实现编程式事务挂起,则J2EE中的TransactionManager是一个强大的特性。只需首先在所选择的J2EE服务器中检测一下它是否可用就可以了。

posted on 2008-11-26 19:28 大鸟 阅读(435) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航: