Написание сообщений с помощью Spring JmsTemplate с использованием TransactionManager

Используя Spring-JMS, можно получать сообщения в контексте внешней транзакции через DefaultMessageListenerContainer.

Однако единственный задокументированный способ написать сообщение — через JmsTemplate.send(…), и я не понимаю, как это можно заставить использовать данный TransactionManager.

Может кто-то указать мне верное направление?


Дополнительная информация: Обеспечение доступности менеджера транзакций (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$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 не пытаться выполнить фиксацию. Буду обновлять, когда узнаю больше.


person Synesso    schedule 12.05.2011    source источник


Ответы (1)


Насколько я понимаю, производители JMS по своей сути взаимодействуют через JTA. При отправке сообщения через JMS MessageProducer используется локальная транзакция JTA потока (если она присутствует).

На это намекает руководство Spring (раздел 21.2). .5):

JmsTemplate также можно использовать с JtaTransactionManager и JMS с поддержкой XA ConnectionFactory для выполнения распределенных транзакций. Обратите внимание, что для этого требуется использование менеджера транзакций JTA, а также правильно сконфигурированного XA ConnectionFactory.

Это также предлагается JmsAccessor.setSessionTransacted (надкласс JmsTemplate) (javadoc):

Установите режим транзакции, который используется при создании сеанса JMS. По умолчанию "ложь". Обратите внимание, что в транзакции JTA параметры, переданные методу create(Queue/Topic)Session(boolean transacted, int acknowledgeMode), не учитываются. В зависимости от контекста транзакции J2EE контейнер принимает свои собственные решения относительно этих значений. Аналогично, эти параметры также не учитываются в рамках локально управляемой транзакции, поскольку в этом случае средство доступа работает с существующим сеансом JMS.

Установка этого флага в значение «true» будет использовать короткую локальную транзакцию JMS при выполнении вне управляемой транзакции и синхронизированную локальную транзакцию JMS в случае наличия управляемой транзакции (отличной от транзакции XA). Последнее приводит к тому, что локальная транзакция JMS управляется вместе с основной транзакцией (которая может быть собственной транзакцией JDBC), при этом транзакция JMS фиксируется сразу после основной транзакции.

Таким образом, запустив транзакцию JTA (т. е. используя API транзакций Spring с JtaTransactionManager) и вызвав JmsTemplate.send(...), вы отправите сообщение, связанное с этой транзакцией.

person skaffman    schedule 12.05.2011
comment
Спасибо. Интересно, сможет ли Spring найти транзакцию в процессе, когда она запущена другим процессом, отличным от Spring? завтра узнаю… - person Synesso; 12.05.2011
comment
@Synesso: Да, будет. Транзакциями JTA управляет контейнер, а не Spring. - person skaffman; 12.05.2011