Scrivere messaggi con Spring jmstemplate utilizzando un manager di transazione
-
12-11-2019 - |
Domanda
Usando Spring-JMS è possibile ricevere messaggi in un contesto di transazione esterna tramite il DefaultMessagelistenerContainer.
Tuttavia l'unico modo documentato per scrivere Un messaggio è via JmsTemplate.send(…)
E non riesco a vedere come questo possa essere costretto a usare un dato TransactionManager
.
Qualcuno può indicarmi nella giusta direzione?
Maggiori informazioni: garantire un gestore delle transazioni (WebSphereUowTransactionManager
), usando JmsTemplate.write
contro un oracolo AQjmsFactory.getQueueConnectionFactory(dataSource)
Risultati 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
Quindi, mentre non ho motivo di dubitare del consiglio di seguito, non sono in grado di testarlo in quanto non riesco a capire come convincere AQ JMS a non tentare un commit. Aggiornerò mentre imparo di più.
Soluzione
La mia comprensione è che i produttori di JMS lo sono intrinsecamente Transatto tramite JTA. Inviando un messaggio tramite JMS MessageProducer
, viene utilizzata la transazione JTA-thread-locali (se è presente).
Questo è accennare dal manuale di primavera (Sezione 21.2.5):
JmsTemplate
può anche essere usato con ilJtaTransactionManager
e un JMS con capacità XAConnectionFactory
Per eseguire transazioni distribuite. Si noti che ciò richiede l'uso di un gestore delle transazioni JTA e un correttamente configurato con XAConnectionFactory
.
Questo è anche suggerito da JmsAccessor.setSessionTransacted
(la superclasse di JmsTemplate
) (Javadoc):
Impostare la modalità di transazione utilizzata durante la creazione di una sessione JMS. L'impostazione predefinita è "falso". Si noti che all'interno di una transazione JTA, i parametri sono passati a
create(Queue/Topic)Session(boolean transacted, int acknowledgeMode)
Il metodo non è preso in considerazione. A seconda del contesto della transazione J2EE, il contenitore prende le proprie decisioni su questi valori. Analogamente, questi parametri non vengono presi in considerazione in una transazione gestita localmente, poiché l'accessore opera in una sessione JMS esistente in questo caso.L'impostazione di questo flag su "TRUE" utilizzerà una breve transazione JMS locale quando si esegue fuori da una transazione gestita e una transazione JMS locale sincronizzata in caso di transazione gestita (diversa da una transazione XA). Quest'ultimo ha l'effetto di una transazione JMS locale gestita accanto alla transazione principale (che potrebbe essere una transazione JDBC nativa), con la transazione JMS che si impegna subito dopo la transazione principale.
Quindi, per iniziare una transazione JTA (cioè usando l'API di transazione di Spring con JtaTransactionManager
) e chiamata JmsTemplate.send(...)
, invierai il messaggio associato a quella transazione.