java 使用 TransactionManager 使用 Spring JmsTemplate 编写消息

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5974601/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 13:47:44  来源:igfitidea点击:

Writing messages with Spring JmsTemplate using a TransactionManager

javaspringoracle-aqspring-jms

提问by Synesso

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

使用 Spring-JMS 可以通过DefaultMessageListenerContainer在外部事务上下文中接收消息。

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

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

Can anyone point me in the right direction?

任何人都可以指出我正确的方向吗?



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

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

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.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.

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

回答by skaffman

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

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

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

Spring 手册(第 21.2.5 节)暗示了这一点:

JmsTemplatecan also be used with the JtaTransactionManagerand an XA-capable JMS ConnectionFactoryfor performing distributed transactions. Note that this requires the use of a JTA transaction manager as well as a properly XA-configured ConnectionFactory.

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

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

这也由JmsAccessor.setSessionTransacted(的超类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.

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

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

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.

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