My jdbcSourceMessage execute a select for update with batch of 100 rows at a time. While the integrationFlow is been executed in a Transaction to hold a lock to the database for the fetched batch. I would like to start new Transaction for my JdbcSourceUpdate (within the message flow) to excute an update and commit my change for each row sent throught the channel.
@Bean
public IntegrationFlow integrationFlow() {
IntegrationFlowBuilder flowBuilder = IntegrationFlows.from(jdbcSourceMessage());
flowBuilder
.split()
.log(LoggingHandler.Level.INFO, message ->
message.getHeaders().get("sequenceNumber")
+ " événements publiés sur le bus de message sur "
+ message.getHeaders().get("sequenceSize")
+ " événements lus (lot)")
.transform(Transformers.toJson())
.log()
.enrichHeaders(h -> h.headerExpression("type", "payload.typ_evenement"))
.publishSubscribeChannel(publishSubscribeSpec -> publishSubscribeSpec
.subscribe(flow -> flow
.bridge()
.transform(Transformers.toJson())
.transform(kafkaGuyTransformer())
.channel(this.rabbitMQchannel.demandeInscriptionOutput()))
.subscribe(flow -> flow
.handle(jdbcMessageHandler()))
);
return flowBuilder.get();
}
@Bean(name = PollerMetadata.DEFAULT_POLLER)
public PollerMetadata defaultPoller() {
PeriodicTrigger trigger = new PeriodicTrigger(this.proprietesSourceJdbc.getTriggerDelay(), TimeUnit.SECONDS);
PollerMetadata pollerMetadata = Pollers.trigger(trigger)
.advice(transactionInterceptor())
.get();
pollerMetadata.setMaxMessagesPerPoll(proprietesSourceJdbc.getMaxRowsPerPoll());
return pollerMetadata;
}
@Bean
public JdbcSourceUpdate jdbcSourceUpdate() {
return new JdbcSourceUpdate();
}
public TransactionInterceptor transactionInterceptor() {
return new TransactionInterceptorBuilder()
.transactionManager(transactionManager())
.build();
}
public PlatformTransactionManager transactionManager(){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(sourceDeDonnees);
transactionManager.setRollbackOnCommitFailure(false);
return transactionManager;
}
public class KafkaGuyTransformer implements GenericTransformer<Message, Message> {
@Override
public Message transform(Message message) {
Message<String> msg = null;
try {
DemandeRecueDTO dto = objectMapper.readValue(message.getPayload().toString(), DemandeRecueDTO.class);
msg = MessageBuilder.withPayload(dto.getTxtDonnee())
.copyHeaders(message.getHeaders())
.build();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
return msg;
}
}
public class JdbcSourceUpdate implements MessageHandler {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
try {
Thread.sleep(100);
DemandeRecueDTO dto = objectMapper.readValue(message.getPayload().toString(), DemandeRecueDTO.class);
jdbcTemplate.update(proprietesSourceJdbc.getUpdate(), dto.getIdEvenementDemandeCrcd());
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}
Since you have that JdbcSourceUpdate
implementation, there is just enough to do like this:
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void handleMessage(Message<?> message) throws MessagingException {
See its JavaDocs for more info:
/**
* Create a new transaction, and suspend the current transaction if one exists.
* Analogous to the EJB transaction attribute of the same name.
* <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
* on all transaction managers. This in particular applies to
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* which requires the {@code javax.transaction.TransactionManager} to be
* made available to it (which is server-specific in standard Java EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
UPDATE
Pay attention to the NOTE though:
* Actual transaction suspension will not work out-of-the-box
* on all transaction managers. This in particular applies to
* {@link org.springframework.transaction.jta.JtaTransactionManager}`.
So, sounds like DataSourceTransactionManager
doesn't work with suspension. I can suggest you to consider to use a .gateway()
for that JdbcSourceUpdate
, but using an ExecutorChannel
. This way your handle(jdbcSourceUpdate()
will be performed on a new thread and, therefore, with a new transaction. The main flow will wait for the reply from that gateway holding its transaction opened.
Something like this:
.subscribe(f -> f
.gateway(subFlow ->
subFlow.channel(c -> c.executor())
.handle(jdbcMessageHandler()))
.channel("nullChannel")
));
Buy your JdbcSourceUpdate
must return something for the gateway reply. Consider do not implement MessageHandler
there, but make just as a plain POJO with a single non- void
method.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.