【问题标题】:What is the correct meaning of Local transaction running inside a Global one?在全局事务中运行的本地事务的正确含义是什么?
【发布时间】:2018-07-18 07:17:47
【问题描述】:

我是 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 类,它获取 JDBC 连接并运行语句来存储从 EJB 接收的数据。它位于 EJB 旁边的包中的普通 Java 类中。

当我尝试运行项目时会出现问题,更具体地说,当我尝试从数据源获取连接时会出现问题。由于我使用的是 XA 数据源,所以会出现 XAER_PROTO 异常:

[7/17/18 16:32:52:771 GMT+01:00] 000001b4 WSRdbXaResour E DSRA0304E: 发生 XAException。 XAException 的内容和细节是:
XA 错误是:-6 XA 错误消息是:在不正确的上下文中调用了例程。 Oracle > 错误代码是:24776 Oracle 错误消息是:内部 XA 错误

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

3.4.7 本地和全局事务

...

当使用同一个连接来执行本地和全局 交易,以下规则适用:

• 本地事务必须在之前提交(或回滚) 在连接中启动一个全局事务。

• 全局事务必须与连接解除关联 在任何本地事务开始之前。

所以我的问题是:

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

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

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

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

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

【问题讨论】:

    标签: java jdbc ejb websphere jta


    【解决方案1】:

    回答您的问题:

    1.是一个 EJB 方法,用 TransactionAttributeType 注释。REQUIRES_NEW 启动一个全局 TX 就 JTA 而言?

    是的,REQUIRES_NEW 导致容器开始一个新的全局事务。

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

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

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

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

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

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

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

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

    如果我理解您描述的场景,您会调用 getConnection 两次,首先是在全局事务中,因为 EJB 标记为 REQUIRES_NEW,随后,在该方法返回并且事务提交后,您第二次调用 getConnection 并使用它是为了一个新的本地事务。您应该澄清哪个 getConnection 尝试失败,是否有其他尝试,如果可能的话,从您的源代码中发布 sn-ps。您还应该发布错误的完整堆栈。缺乏这些,这意味着你得到的答案更有可能是猜测而不是答案。例如,考虑到 Oracle 错误,我可以猜测您可能在本地事务中使用了额外的连接,而在尝试在全局事务中使用连接之前从未提交过该连接。还应注意,在应用程序服务器中,连接是池化的,因此先前的使用可能来自未在本地事务中提交连接的不同线程。应用程序服务器会尽力检测此类事情并为您清理,但并非总是如此。因此,您还需要查看您对连接的其他使用情况,以了解在某些情况下可能不会提交/回滚的任何连接。

    【讨论】:

    • 感谢您的评论,这对我来说很清楚。问题是我在开始全球交易之前从未提交过本地交易,遗憾的是我忘记了这一点并且在我的问题中没有提到这一点。无论如何,非常感谢您的详细说明。
    猜你喜欢
    • 2014-04-08
    • 2013-03-08
    • 1970-01-01
    • 1970-01-01
    • 2016-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-06
    相关资源
    最近更新 更多