我是JTA的新手,目前正深入了解其规范。我还创建了一些示例项目,以更快地深入该主题。我将IBM WebSphere 9用作运行时。
我创建了一个由EJB和MDB组成的简单项目。我的想法是,我将一些JMS发送到队列,MDB然后获取此消息,对其进行处理,并使用本地接口调用EJB(MDB和EJB都位于同一EAR)。 EJB依次处理传入的对象,并通过 XA 数据源使用JDBC将其写入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本地和全局事务
...
使用同一连接执行本地和全局连接时 交易,以下规则适用:
•必须先提交(或回滚)本地事务 在连接中启动全局事务。
•必须取消全局事务与连接的关联 在任何本地交易开始之前。
所以我的问题是:
用TransactionAttributeType.REQUIRES_NEW
注释的EJB方法是否就JTA启动了全局TX?
我的假设是正确的吗?从数据源中检索新的JDBC连接会根据JTA启动新的本地事务吗?
如果上述所有方法都正确,那么实际上是否可以在全局TX下在EJB中使用纯JDBC?还是只应该从非事务性EJB调用与JDBC相关的方法?
我是否应该将上述方法视为错误的方法?
我应该使用更多抽象的JTA接口来处理数据库,而不是使用“普通” JDBC方法吗?如果是这样,那哪种方法更可取?
答案 0 :(得分:4)
要回答您的问题:
1。是用TransactionAttributeType注释的EJB方法。REQUIRES_NEW根据JTA启动全局TX吗?
是的,REQUIRES_NEW使容器开始新的全局事务。
2。我的假设是正确的,即从数据源检索新的JDBC连接会根据JTA启动新的本地事务?
您几乎是正确的。检索JDBC连接实际上并不启动事务。但是,在没有全局事务的情况下,对JDBC连接进行有意义的工作(例如createStatement,execute等)会启动本地事务。
3。如果上述所有方法都正确,那么实际上可以在全局TX下在EJB中使用纯JDBC吗?还是只应该从非事务性EJB调用与JDBC相关的方法?
无论是否在全局事务下,纯JDBC对EJB都是完全有效的。
4。我是否应该将上述方法视为错误的方法?
您所描述的方案应该可以正常工作。很有可能还有其他一些导致问题的细节,可能与订购或未提交有关。
5。我是否应该使用更多抽象的JTA接口来处理数据库,而不要使用“普通” JDBC方法?如果是这样,那哪种方法更可取?
我建议您调试导致问题的原因。
如果我理解您所描述的场景,您将调用getConnection两次,第一次是由于标记为REQUIRES_NEW的EJB在全局事务中,然后在该方法返回并提交事务之后,第二次调用getConnection并将其用于新的本地交易。您应该弄清楚哪个getConnection尝试失败,是否还有其他尝试,如果可能的话,请从源代码中发布摘要。您还应该发布完整的错误堆栈。缺少这些,意味着您获得的答案比答案更可能是猜测。例如,考虑到Oracle错误,我可能会猜想您可能在本地事务中有其他先前使用过的连接,而在尝试在全局事务中使用该连接之前,它从未提交。还应注意,在应用程序服务器中,连接被池化,因此先前的用法可能来自未在本地事务中提交连接的其他线程。应用程序服务器会尽力检测到这种情况并为您清理,但并非总是如此。因此,您还需要检查在某些情况下可能未提交/回退的任何其他连接用法。