Figured out what I need to do. In my spring configuration file, I need to configure the following:
- an instance of
org.springframework.transaction.jta.JtaTransactionManager
class (configured for example to use atomikos) with the "transactionManager" and "userTransaction" dependencies injected, e.g., with instances of the classescom.atomikos.icatch.jta.UserTransactionManager
andcom.atomikos.icatch.jta.UserTransactionImp
; - the
<tx:annotation-driven transaction-manager="txManager">
tag to use annotation-driven transactions in the client code; - register the JMS connection factory with the transaction manager, e.g., by creating an instance of
com.atomikos.jms.AtomikosConnectionFactoryBean
; - a queue object, e.g., an instance of
com.tibco.tibjms.TibjmsQueue
or similar; - a JmsTemplate with the dependency "defaultDestination" injected with the previously created queue;
- register the database(s) with the transaction manager, e.g., by creating an instance of
com.atomikos.jdbc.AtomikosDataSourceBean
(orcom.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean
for a last resource gambit scenario); - one or more SqlUpdate (or BatchSqlUpdate or JdbcTemplate) instances (one per data source) each with the "dataSource" property injected with the previously defined data source(s);
an instance of a custom DAO class with injected the previously defined JmsTemplate and SqlUpdate objects: this class will have one or more methods annotated
@Transactional
that will perform transactions using the JmsTemplate to receive messages and the SqlUpdate(s) to persist them on the databases, e.g., it could be a class that looks like this:public class MyDao{ JmsTemplate jmsTemplate; // getter/setter omitted for clarity BatchSqlUpdate sqlUpdate; // getter/setter omitted for clarity @Transactional public void persistMessages(int n){ // map used for the sqlUpdate object Map<String, Object> params = new HashMap<>(); // need to reset this since it's being reused sqlUpdate.reset(); for(int i=0;i<n;i++){ // retrieve a message synchronously Message msg = jmsTemplate.receive(); // transform the message doSomeMagic(msg,params); // set parameters in the SQL sqlUpdate.updateByNamedParam(params); } // now the batch can be flushed sqlUpdate.flush(); } private void doSomeMagic(Message msg, Map<String,Object> params){ // implementation is application-dependent! // the only assumption is that somehow the // message can be used to set the named // parameters in the sqlUpdate object } }
The only thing worth noting is that the DAO must be spring-managed because the use of annotations requires spring to create a proxy.