我是 JTA 的新手,目前正在研究它的规范。我还创建了一些示例项目来更快地深入研究这个主题。我使用 IBM WebSphere 9 作为运行时。
我创建了一个由 EJB 和 MDB 组成的简单项目。这个想法是我将一些 JMS 发送到队列,然后 MDB 获取此消息,处理它并使用本地接口(interface)调用 EJB(MDB 和 EJB 都位于同一个 EAR 中)。 EJB 反过来将处理传入的对象,并使用 JDBC 通过 XA 数据源将其写入 Oracle 数据库。
MDB onMessage()
方法定义了一个 TransactionAttributeType.NOT_SUPPORTED
,正如 JTA 所说,它应该在事务上下文之外运行。
从 MDB 调用的 EJB 的 process()
方法没有定义任何 TransactionAttributes
,因为它来自 JTA,它应该有一个默认值是 TransactionAttributeType.REQUIRES_NEW
。因此,如果我没记错的话,它会在被调用时启动一个新的全局 TX,或者我错了吗?
我还创建了一个简单的 DAO 类,它获取 JDBC 连接并运行语句来存储从 EJB 接收的数据。它位于 EJB 旁边的包中的普通 Java 类中。
问题发生在我尝试运行该项目时,更具体地说,它发生在我尝试从数据源获取连接时。由于我使用 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.
所以我的问题是:
用
TransactionAttributeType.REQUIRES_NEW
注释的 EJB 方法是否启动了 JTA 方面的全局 TX?我的假设是否正确,即从数据源检索新的 JDBC 连接会启动新的 JTA 本地事务?
如果以上都是正确的,那么在全局 TX 下的 EJB 中使用纯 JDBC 实际上是可能的吗?或者我应该只从非事务性 EJB 调用与 JDBC 相关的方法?
我是否应该将上述方法视为错误方法?
我应该使用更抽象的 JTA 接口(interface)来处理数据库,而不是使用“普通”JDBC 方法吗?如果是这样,那么哪种方式更可取?
最佳答案
回答您的问题:
1。用 TransactionAttributeType.REQUIRES_NEW 注释的 EJB 方法是否启动了 JTA 方面的全局 TX?
是的,REQUIRES_NEW 导致容器开始一个新的全局事务。
2。我的假设是否正确,即从数据源检索新的 JDBC 连接会启动新的 JTA 本地事务?
您在这一点上几乎是正确的。检索 JDBC 连接实际上并不启动事务。但是,在没有全局事务的情况下,在 JDBC 连接上进行有意义的工作(例如 createStatement、execute 等)会启动本地事务。
3。如果以上所有都是正确的,那么在全局 TX 下实际上可以在 EJB 中使用纯 JDBC 吗?或者我应该只从非事务性 EJB 调用与 JDBC 相关的方法?
普通 JDBC 从 EJB 完全有效,无论是否在全局事务下。
4。我是否应该将上述方法视为错误方法?
您描述的场景应该可以正常工作。很可能有一些额外的细节导致了问题,可能与订购或未提交有关。
5。我应该使用更抽象的 JTA 接口(interface)来处理数据库而不是使用“普通”JDBC 方法吗?如果是这样,那么哪种方式更可取?
我建议调试您所看到的问题的原因。
如果我理解您描述的情况,您会调用 getConnection 两次,第一次是在全局事务中,因为 EJB 标记为 REQUIRES_NEW,随后,在该方法返回且事务提交后,您第二次调用 getConnection 并使用它用于一个新的本地事务。您应该澄清哪个 getConnection 尝试失败,是否还有其他尝试,如果可能的话,从您的源代码中发布片段。您还应该发布错误的完整堆栈。缺少这些,这意味着您得到的答案更有可能是猜测而不是答案。例如,考虑到 Oracle 错误,我可以猜测您可能在尝试在全局事务中使用连接之前从未提交过的本地事务中额外使用过连接。还应注意,在应用程序服务器中,连接被合并,因此先前的使用可能来自未在本地事务中提交连接的不同线程。应用程序服务器尽最大努力检测这类事情并为您清理,但它不能总是这样做。因此,您还需要检查您对连接的其他使用情况,以了解在某些情况下可能不会提交/回滚的连接。
关于java - 在全局事务中运行的本地事务的正确含义是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51395824/