使用 TransactionManager 通过 Spring JmsTemplate 编写消息

发布于 2024-11-07 05:29:25 字数 2272 浏览 5 评论 0原文

使用 Spring-JMS 可以通过 DefaultMessageListenerContainer

然而,编写消息的唯一记录方法是通过JmsTemplate.send(…),我看不出如何强制使用给定的TransactionManager

有人能指出我正确的方向吗?


更多信息:确保事务管理器可用 (WebSphereUowTransactionManager),对 Oracle AQjmsFactory.getQueueConnectionFactory(dataSource) 使用 JmsTemplate.write 会导致:

org.springframework.jms.UncategorizedJmsException: Uncategorized exception occured during JMS processing; nested exception is oracle.jms.AQjmsException: could not use local transaction commit in a global transaction
  at org.springframework.jms.support.JmsUtils.convertJmsAccessException(JmsUtils.java:316)
  at org.springframework.jms.support.JmsAccessor.convertJmsAccessException(JmsAccessor.java:168)
  at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:469)
  at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:534)
Caused by: oracle.jms.AQjmsException: could not use local transaction commit in a global transaction
  at oracle.jms.AQjmsSession.commitNoCheck(AQjmsSession.java:1053)
  at oracle.jms.AQjmsSession.commit(AQjmsSession.java:1021)
  at org.springframework.jms.support.JmsUtils.commitIfNecessary(JmsUtils.java:217)
  at org.springframework.jms.core.JmsTemplate.doSend(JmsTemplate.java:573)
  at org.springframework.jms.core.JmsTemplate$3.doInJms(JmsTemplate.java:536)
  at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:466)
  ... 24 more
Caused by: java.sql.SQLException: could not use local transaction commit in a global transaction
  at oracle.jdbc.driver.PhysicalConnection.disallowGlobalTxnMode(PhysicalConnection.java:6647)
  at oracle.jdbc.driver.PhysicalConnection.commit(PhysicalConnection.java:3635)
  at oracle.jdbc.driver.PhysicalConnection.commit(PhysicalConnection.java:3680)
  at oracle.jdbc.OracleConnectionWrapper.commit(OracleConnectionWrapper.java:133)
  at oracle.jms.AQjmsSession.commitNoCheck(AQjmsSession.java:1049)
  ... 29 more

因此,虽然我没有理由怀疑下面的建议,但我无法对其进行测试,因为我无法弄清楚如何让 AQ JMS 不尝试提交。当我了解更多时会更新。

Using Spring-JMS it is possible to receive messages within an external transaction context via the DefaultMessageListenerContainer.

However the only documented way to write a message is via JmsTemplate.send(…) and I can't see how this can be coerced to use a given TransactionManager.

Can anyone point me in the right direction?


More info: Ensuring a transaction manager is available (WebSphereUowTransactionManager), using JmsTemplate.write against an Oracle AQjmsFactory.getQueueConnectionFactory(dataSource) results in:

org.springframework.jms.UncategorizedJmsException: Uncategorized exception occured during JMS processing; nested exception is oracle.jms.AQjmsException: could not use local transaction commit in a global transaction
  at org.springframework.jms.support.JmsUtils.convertJmsAccessException(JmsUtils.java:316)
  at org.springframework.jms.support.JmsAccessor.convertJmsAccessException(JmsAccessor.java:168)
  at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:469)
  at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:534)
Caused by: oracle.jms.AQjmsException: could not use local transaction commit in a global transaction
  at oracle.jms.AQjmsSession.commitNoCheck(AQjmsSession.java:1053)
  at oracle.jms.AQjmsSession.commit(AQjmsSession.java:1021)
  at org.springframework.jms.support.JmsUtils.commitIfNecessary(JmsUtils.java:217)
  at org.springframework.jms.core.JmsTemplate.doSend(JmsTemplate.java:573)
  at org.springframework.jms.core.JmsTemplate$3.doInJms(JmsTemplate.java:536)
  at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:466)
  ... 24 more
Caused by: java.sql.SQLException: could not use local transaction commit in a global transaction
  at oracle.jdbc.driver.PhysicalConnection.disallowGlobalTxnMode(PhysicalConnection.java:6647)
  at oracle.jdbc.driver.PhysicalConnection.commit(PhysicalConnection.java:3635)
  at oracle.jdbc.driver.PhysicalConnection.commit(PhysicalConnection.java:3680)
  at oracle.jdbc.OracleConnectionWrapper.commit(OracleConnectionWrapper.java:133)
  at oracle.jms.AQjmsSession.commitNoCheck(AQjmsSession.java:1049)
  ... 29 more

So whilst I have no reason to doubt the advise below, I am not able to test it as I can't figure out how to get AQ JMS to not attempt a commit. Will update as I learn more.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

路还长,别太狂 2024-11-14 05:29:25

我的理解是,JMS 生产者本质上是通过 JTA 进行交易的。通过通过 JMS MessageProducer 发送消息,将使用线程本地 JTA 事务(如果存在)。

Spring手册暗示了这一点(第21.2节.5):

JmsTemplate 还可以与 JtaTransactionManager 和支持 XA 的 JMS ConnectionFactory 一起使用来执行分布式事务。请注意,这需要使用 JTA 事务管理器以及正确配置 XA 的 ConnectionFactory

JmsAccessor.setSessionTransactedJmsTemplate 的超类)也建议这样做(javadoc):

设置创建 JMS 会话时使用的事务模式。默认为“假”。
请注意,在 JTA 事务中,不会考虑传递给 create(Queue/Topic)Session(boolean transacted, int recognizeMode) 方法的参数。根据 J2EE 事务上下文,容器对这些值做出自己的决定。类似地,本地管理的事务中也不考虑这些参数,因为在这种情况下访问器在现有的 JMS 会话上进行操作。

将此标志设置为“true”将在托管事务外部运行时使用短本地 JMS 事务,并在存在托管事务(XA 事务除外)的情况下使用同步本地 JMS 事务。后者具有与主事务(可能是本机 JDBC 事务)一起管理本地 JMS 事务的效果,并且 JMS 事务在主事务之后立即提交。

因此,通过启动 JTA 事务(即通过使用带有 JtaTransactionManager 的 Spring 事务 API)并调用 JmsTemplate.send(...),您将发送绑定到该事务的消息交易。

My understanding is that JMS producers are inherently transacted via JTA. By sending a message via JMS MessageProducer, the thread-local JTA transaction is used (if one is present).

This is hinting at by the Spring manual (section 21.2.5):

JmsTemplate can also be used with the JtaTransactionManager and an XA-capable JMS ConnectionFactory for performing distributed transactions. Note that this requires the use of a JTA transaction manager as well as a properly XA-configured ConnectionFactory.

This is also suggested by JmsAccessor.setSessionTransacted (the superclass of JmsTemplate) (javadoc):

Set the transaction mode that is used when creating a JMS Session. Default is "false".
Note that within a JTA transaction, the parameters passed to create(Queue/Topic)Session(boolean transacted, int acknowledgeMode) method are not taken into account. Depending on the J2EE transaction context, the container makes its own decisions on these values. Analogously, these parameters are not taken into account within a locally managed transaction either, since the accessor operates on an existing JMS Session in this case.

Setting this flag to "true" will use a short local JMS transaction when running outside of a managed transaction, and a synchronized local JMS transaction in case of a managed transaction (other than an XA transaction) being present. The latter has the effect of a local JMS transaction being managed alongside the main transaction (which might be a native JDBC transaction), with the JMS transaction committing right after the main transaction.

So by start a JTA transaction (i.e. by using Spring's transaction API with JtaTransactionManager) and calling JmsTemplate.send(...), you will be sending the message bound to that transaction.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文