TransactionManager를 사용하여 Spring JmsTemplate으로 메시지 작성
-
12-11-2019 - |
문제
Spring-JMS를 사용하면 외부 트랜잭션 컨텍스트 내에서 메시지를 수신할 수 있습니다. DefaultMessageListenerContainer.
그러나 문서화된 유일한 방법은 쓰다 메시지가 왔어요 JmsTemplate.send(…)
그리고 이것이 어떻게 주어진 것을 사용하도록 강제될 수 있는지 알 수 없습니다. TransactionManager
.
누구든지 올바른 방향으로 나를 가리킬 수 있습니까?
더 많은 정보:트랜잭션 관리자가 사용 가능한지 확인(WebSphereUowTransactionManager
), 사용 JmsTemplate.write
오라클을 상대로 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가 커밋을 시도하지 않도록 하는 방법을 알 수 없기 때문에 테스트할 수 없습니다.자세한 내용은 업데이트하겠습니다.
해결책
내가 이해한 바에 따르면 JMS 제작자는 본질적으로 JTA를 통해 거래되었습니다.JMS를 통해 메시지를 보내면 MessageProducer
, 스레드 로컬 JTA 트랜잭션이 사용됩니다(있는 경우).
이는 Spring 매뉴얼(섹션 21.2.5):
JmsTemplate
와 함께 사용할 수도 있습니다.JtaTransactionManager
XA 가능 JMSConnectionFactory
분산 트랜잭션을 수행하기 위한 것입니다.이를 위해서는 JTA 트랜잭션 관리자와 적절하게 구성된 XA 트랜잭션 관리자를 사용해야 합니다.ConnectionFactory
.
이는 또한 다음과 같이 제안됩니다. JmsAccessor.setSessionTransacted
(수퍼클래스 JmsTemplate
) (javadoc):
JMS 세션을 생성할 때 사용되는 트랜잭션 모드를 설정합니다.기본값은 "거짓"입니다. JTA 트랜잭션 내에서 전달된 매개변수는
create(Queue/Topic)Session(boolean transacted, int acknowledgeMode)
방법은 고려되지 않습니다.J2EE 트랜잭션 컨텍스트에 따라 컨테이너는 이러한 값에 대해 자체적으로 결정을 내립니다. 마찬가지로, 이 경우 접근자가 기존 JMS 세션에서 작동하기 때문에 이러한 매개변수는 로컬로 관리되는 트랜잭션 내에서도 고려되지 않습니다.이 플래그를 "true"로 설정하면 관리되는 트랜잭션 외부에서 실행될 때 짧은 로컬 JMS 트랜잭션을 사용하고, 관리되는 트랜잭션(XA 트랜잭션 제외)이 있는 경우 동기화된 로컬 JMS 트랜잭션을 사용합니다.후자는 기본 트랜잭션(네이티브 JDBC 트랜잭션일 수 있음)과 함께 관리되는 로컬 JMS 트랜잭션의 효과를 가지며, JMS 트랜잭션은 기본 트랜잭션 바로 다음에 커밋됩니다.
따라서 JTA 트랜잭션을 시작하여(예:Spring의 트랜잭션 API를 사용하여 JtaTransactionManager
) 그리고 전화 JmsTemplate.send(...)
, 해당 트랜잭션에 바인딩된 메시지를 보내게 됩니다.