全局事务中的本地事务 运行 的正确含义是什么?

What is the correct meaning of Local transaction running inside a Global one?

我是 JTA 的新手,目前正在研究它的规范。我还创建了一些示例项目来更快地深入研究这个主题。我使用 IBM WebSphere 9 作为 运行时间。

我创建了一个由 EJB 和 MDB 组成的简单项目。这个想法是我将一些 JMS 发送到队列,然后 MDB 获取此消息,处理它并使用本地接口调用 EJB(MDB 和 EJB 都位于同一个 EAR 中)。 EJB 反过来将处理传入的对象,并使用 JDBC 通过 XA 数据源将其写入 Oracle 数据库。

MDB onMessage() 方法定义了 TransactionAttributeType.NOT_SUPPORTED,正如 JTA 所说,它应该 运行 在事务上下文之外。

从 MDB 调用的 EJB 的 process() 方法没有任何 TransactionAttributes 定义,因为它来自 JTA,它应该有一个默认值 TransactionAttributeType.REQUIRES_NEW .因此,如果我没记错的话,它会在被调用时启动一个新的全局 TX,还是我错了?

我还创建了一个简单的 DAO class,它获得一个 JDBC 连接和 运行s 语句来存储从 EJB 接收的数据。它位于 EJB 旁边的包中的普通 Java class 中。

问题发生在我尝试 运行 项目时,更具体地说,问题发生在我试图从数据源获取连接时。由于我使用XA数据源,出现XAER_PROTO异常:

[7/17/18 16:32:52:771 GMT+01:00] 000001b4 WSRdbXaResour E DSRA0304E: XAException occurred. XAException contents and details are:
The XA Error is: -6 The XA Error message is: Routine was invoked in an improper context. The Oracle > Error code is: 24776 The Oracle Error message is: Internal XA Error

经过一段时间调查这个问题后,我发现这个问题可能与 JTA 规范中的以下语句有关:

3.4.7 Local and Global Transactions

...

When using the same connection to perform both local and global transactions, the following rules apply:

• The local transaction must be committed (or rolled back) before starting a global transaction in the connection.

• The global transaction must be disassociated from the connection before any local transaction is started.

所以我的问题是:

  1. 是一个 EJB 方法,用 TransactionAttributeType.REQUIRES_NEW 注释根据 JTA 启动全局 TX?

  2. 我的假设是否正确,即从数据源检索新的 JDBC 连接会根据 JTA 启动新的本地事务?

  3. 如果以上都是正确的,那么在全局 TX 下的 EJB 中实际上可以使用纯 JDBC 吗?或者我应该仅从非事务性 EJB 调用 JDBC 相关方法?

  4. 我应该认为上述方法是错误的吗?

  5. 我应该使用更抽象的 JTA 接口来处理数据库而不是使用 "plain" JDBC 方法吗?如果是这样,那么哪种方式更可取?

回答您的问题:

1.是一个 EJB 方法,用 TransactionAttributeType.REQUIRES_NEW 注释根据 JTA 启动全局 TX?

是,REQUIRES_NEW 导致容器启动新的全局事务。

2。我的假设是否正确,即从数据源检索新的 JDBC 连接会根据 JTA 启动新的本地事务?

你几乎是对的。检索 JDBC 连接实际上并不启动事务。但是,在没有全局事务的情况下,在 JDBC 连接(例如 createStatement、execute 等)上进行有意义的工作会启动本地事务。

3。如果以上都是正确的,那么在全局 TX 下的 EJB 中实际上可以使用纯 JDBC 吗?或者我应该只从非事务性 EJB 调用 JDBC 相关方法?

普通 JDBC 从 EJB 中完全有效,无论是否在全局事务下。

4。我是否应该将上述方法视为错误方法?

您描述的场景应该可以正常工作。很可能有一些额外的细节导致了问题,可能与订购或未提交有关。

5.我应该使用更抽象的 JTA 接口来处理数据库而不是使用 "plain" JDBC 方法吗?如果是这样,那么哪种方式更可取?

我建议调试您所看到的问题的原因。

如果我理解您描述的场景,您调用 getConnection 两次,第一次是在全局事务中由于标记为 REQUIRES_NEW 的 EJB,随后,在该方法 returns 和事务提交之后,第二次调用 getConnection 并将其用于新的本地事务。您应该澄清是哪个 getConnection 尝试失败了,是否还有其他失败,如果可能的话 post 从您的源代码中提取片段。您还应该 post 错误的完整堆栈。缺少这些,这意味着您得到的答案更有可能是猜测而不是答案。例如,考虑到 Oracle 错误,我可以猜测您可能在尝试在全局事务中使用连接之前从未提交过的本地事务中额外使用过连接。还应注意,在应用程序服务器中,连接被合并,因此先前的使用可能来自未在本地事务中提交连接的不同线程。应用程序服务器尽最大努力检测这类事情并为您清理,但它不能总是这样做。因此,您还需要检查在某些情况下可能不会 committing/rolling 返回的任何连接的其他使用情况。