I am trying to design an application using spring + atomikos that reads a batch of N messages and persists them to a database within a single transaction. It has to be a batch of messages because the data is only consistent when it's in batches, i.e., a single message is not enough data for a consistent transaction. Moreover having one transaction per message would absolutely kill my performance. This is not the typical JMS + DB application so I'm having a hard time finding examples online (I tried with a MessageListener as suggested on the atomikos website but that creates one transaction per message). What's the best way to achieve this using Spring? Thanks
Giovanni
Figured out what I need to do. In my spring configuration file, I need to configure the following:
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 classes com.atomikos.icatch.jta.UserTransactionManager
and com.atomikos.icatch.jta.UserTransactionImp
;<tx:annotation-driven transaction-manager="txManager">
tag to use annotation-driven transactions in the client code;com.atomikos.jms.AtomikosConnectionFactoryBean
;com.tibco.tibjms.TibjmsQueue
or similar;com.atomikos.jdbc.AtomikosDataSourceBean
(or com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean
for a last resource gambit scenario);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.