我的Spring(4.1.1)应用程序部署在JBoss-6.10-最终实例上,因此它使用基于容器的事务管理器和数据源。对于消息传递,我使用TIBCOEMS8.1,并设置了XA队列连接工厂。Java版本是1.8。0_20。所有这些都在我的Ubuntu 14.04笔记本电脑上运行。
我需要通过JMS发送请求,然后等待回复。我调用的bean的事务传播设置为传播。需要,所以我需要在一个新的事务中发送请求,然后等待回复。这意味着请求是在一个单独的bean中发送的,事务传播设置为传播。REQUIRES_NEW。它有效,但是我从JBoss那里得到了一个令人担忧的警告:
14-10-02 12:06:12,902 WARN[org.jboss.tm.usertx.UserTransaction注册表](超文本传输协议-0.0.0.0-8080-1)错误通知监听器org.jboss.resource.connectionmanager.CachedConnectionManager@1917b4deuserTransactionStarted:java.lang.IllegalStateException:试图更改事务TransactionImple
…在EMS方面,我看到一些XA错误:
查看堆栈跟踪后,我打开了Spring的AbstractPlatformTransactionManager的源代码,发现了以下处理REQUIRES_NEW的代码(从第415行开始):
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
catch (Error beginErr) {
resumeAfterBeginException(transaction, suspendedResources, beginErr);
throw beginErr;
}
}
我的问题是:新交易在哪里开始?
从表面上看,它看起来像是正在使用现有事务而不是正在启动的新事务——看看“事务”是如何传递给doBegin(…)的。我也在doBegin中查看了,没有指示正在请求或创建新事务。这个视图似乎得到了堆栈跟踪和我从JBoss那里得到的警告的支持…
很高兴看到我不是一个人被困在这个没有韧带的洞里…
就我所知,这里描述了这个警告的深层原因(线程结束)
当外部事务挂起并启动新的内部事务时,Jboss连接池为内部事务检索的托管连接与外部事务的托管连接相同,这导致IllegalStateException被抛出!
并且是由于jboss JCA合约实现的特定行为(Lazy JCA enlistment
)。
Spring端打开了一个缺陷,标记为“无法修复”,但它们提供了解决方法配置:
典型的解决方案是使用Spring的TransactionAware DataSourceProxy并在那里将“reobtainTransactionalConnections”标志切换为“true”
玩得开心!