“本地事务已经有1个非XA资源:无法添加更多资源”错误

在阅读了有关此错误的先前问题后,似乎所有这些问题都表明您需要在所有数据源上启用XA。 但:

  1. 如果我不想要分布式事务怎么办? 如果我想同时在两个不同的数据库上启动事务,但是在一个数据库上提交事务并在另一个数据库上回滚事务,我该怎么办?
  2. 我想知道我的代码是如何实际启动分布式事务的。 在我看来,我在每个数据库上开始完全独立的事务。

有关应用程序的信息:

该应用程序是在Sun Java Application Server 9.1上运行的EJB

我使用类似下面的spring上下文来设置hibernate会话工厂:

      hibernate.dialect=org.hibernate.dialect.Oracle9Dialect hibernate.default_schema=schemaA   [mapping resources...]         hibernate.dialect=org.hibernate.dialect.Oracle9Dialect hibernate.default_schema=schemaB   [mapping resources...]   

两个JNDI资源都是javax.sql.ConnectionPoolDatasoure。 它们实际上都指向同一个连接池,但是我们有两个不同的JNDI资源,因为这两个完全独立的表组可能会在将来移动到不同的数据库。

然后在代码中,我做:

 sessionA = dbASessionFactory.openSession(); sessionB = dbBSessionFactory.openSession(); sessionA.beginTransaction(); sessionB.beginTransaction(); 

sessionB.beginTransaction()行在这篇文章的标题中产生错误 – 有时候。 我在两个不同的sun应用服务器上运行了应用程序。 一个运行正常,另一个抛出错误。 虽然它们连接到不同但等效的数据库,但我没有看到两个服务器的配置方式有何不同。

所以问题是

  1. 为什么上面的代码没有启动完全独立的事务?
  2. 如何强制它启动独立事务而不是分布式事务?
  3. 什么配置可能导致两个应用程序服务器之间的行为差​​异?

谢谢。

PS堆栈跟踪是:

 Local transaction already has 1 non-XA Resource: cannot add more resources. at com.sun.enterprise.distributedtx.J2EETransactionManagerOpt.enlistResource(J2EETransactionManagerOpt.java:124) at com.sun.enterprise.resource.ResourceManagerImpl.registerResource(ResourceManagerImpl.java:144) at com.sun.enterprise.resource.ResourceManagerImpl.enlistResource(ResourceManagerImpl.java:102) at com.sun.enterprise.resource.PoolManagerImpl.getResource(PoolManagerImpl.java:216) at com.sun.enterprise.connectors.ConnectionManagerImpl.internalGetConnection(ConnectionManagerImpl.java:327) at com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:189) at com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:165) at com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:158) at com.sun.gjc.spi.base.DataSource.getConnection(DataSource.java:108) at org.springframework.orm.hibernate3.LocalDataSourceConnectionProvider.getConnection(LocalDataSourceConnectionProvider.java:82) at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:446) at org.hibernate.jdbc.ConnectionManager.getConnection(ConnectionManager.java:167) at org.hibernate.jdbc.JDBCContext.connection(JDBCContext.java:142) at org.hibernate.transaction.JDBCTransaction.begin(JDBCTransaction.java:85) at org.hibernate.impl.SessionImpl.beginTransaction(SessionImpl.java:1354) at [application code ...] 

1为什么上面的代码没有启动完全独立的事务?

该应用程序。 服务器为您管理事务,如果需要,可以是分布式事务。 它会自动招募所有参与者。 当只有一个参与者时,您没有注意到与纯JDBC事务有任何区别,但如果有多个,则确实需要分布式事务,因此出现错误。

2如何强制它启动独立事务而不是分布式事务?

您可以将数据源配置为XA或Local 。 Spring / Hibernate的事务行为也可以配置为使用常规JDBC事务或将事务管理委派给JTA分布式事务管理器。

我建议你将数据源切换到非XA并尝试配置Spring / Hibernate以使用JDBC事务。 您应该在文档中找到相关信息,这里我怀疑是要更改的行:

  

这应该基本上意味着您没有使用该应用程序。 服务器分布式事务管理

3哪种配置可能导致两个应用程序服务器之间的行为差​​异?

如果你有完全相同的应用程序和配置,这意味着在一种情况下,只有一个参与者被列入dist。 交易,而第二种情况有两种。 一个参与者通常对应于与数据库的一个物理连接。 可能是在一种情况下,您在两个不同的数据库上使用两个模式,而在第二种情况下,您在同一个物理数据库上使用两个模式 ? 更可能的解释是数据源在两个应用程序上的配置不同。 服务器。

PS:如果您使用JTA分布式事务,则应使用UserTransaction.{begin,commit,rollback}而不是Session上的等效项。

在阅读了有关此错误的先前问题后,似乎所有这些问题都表明您需要在所有数据源上启用XA。

不,不是全部,除了一个(除了一个例外),如果您的应用服务器支持记录上一个资源(LLR)优化(允许在全局事务中登记一个非XA资源)。

为什么上面的代码没有启动完全独立的事务?

因为你不是。 在EJB Session Beans后面使用beginTransaction() ,Hibernate将加入JTA事务(有关完整详细信息,请参阅文档 )。 所以第一个调用正常工作,但第二个调用意味着在当前事务中征募另一个事务资源。 由于您的资源都不是XA,因此您会遇到exception。

如何强制它启动独立事务而不是分布式事务?

见@ewernli回答。

什么配置可能导致两个应用程序服务器之间的行为差​​异?

不知道。 也许其中一个人正在使用至少一个XA数据源。