我有一个案例,我在那里读取文件,将内容转换为String。然后将字符串拆分为多个有效负载,并将这些有效负载单独发送到队列。我想使用JmsTransactionManager以便所有消息都发送或根本不发送。
当TX成功时,我想将文件移动到Archive文件夹,否则将其移动到Failed文件夹。我已经读过,我可以使用transactionSynchronizationFactory来完成此任务。但是与JmsTransactionManager结合使用时,文件不会移动。如果我使用PseudoTransactionManager,那么文件就会被移动,但是我的JmsTransaction就失去了。
我制作了一个简化版本来重现这个问题。 (在这种情况下,文件的内容是一个简单的逗号分隔值列表。)
@Bean
public IntegrationFlow fileInboundAdaptor() {
return IntegrationFlows
.from(s -> s.file(new File(INBOUND_PATH))
.patternFilter("*.txt"),
e -> e.poller(Pollers.fixedDelay(5000)
.transactionSynchronizationFactory(transactionSynchronizationFactory())
.transactional(new JmsTransactionManager(connectionFactory))
)
)
.transform(Transformers.fileToString())
.split(s -> s.applySequence(false).get().getT2().setDelimiters(","))
.handle((GenericHandler<String>) (payload, headers) -> {
jmsTemplate.send("SOME_QUEUE", (Session session) -> session.createTextMessage(payload));
return payload;
})
.channel(MessageChannels.queue("fileReadingResultChannel"))
.get();
}
transactionSynchronizationFactory如下所示:
@Bean
public TransactionSynchronizationFactory transactionSynchronizationFactory() {
ExpressionParser parser = new SpelExpressionParser();
ExpressionEvaluatingTransactionSynchronizationProcessor syncProcessor
= new ExpressionEvaluatingTransactionSynchronizationProcessor();
syncProcessor.setBeanFactory(applicationContext.getAutowireCapableBeanFactory());
syncProcessor.setAfterCommitExpression(parser.parseExpression(
"payload.renameTo(new java.io.File('test/archive' " +
" + T(java.io.File).separator + 'ARCHIVE-' + payload.name))"));
syncProcessor.setAfterRollbackExpression(parser.parseExpression(
"payload.renameTo(new java.io.File('test/fail' " +
" + T(java.io.File).separator + 'FAILED-' + payload.name))"));
return new DefaultTransactionSynchronizationFactory(syncProcessor);
}
所以我的问题是:TransactionSynchronizationFactory只能与PseudoTransactionManager一起使用,还是应该与JmsTransactionManager一起使用?
解决方案
我需要在JmsTransaction上设置transactionSynchronization。像这样:
public JmsTransactionManager transactionManager() {
JmsTransactionManager jmsTransactionManager = new JmsTransactionManager(connectionFactory);
jmsTransactionManager.setTransactionSynchronization(AbstractPlatformTransactionManager.SYNCHRONIZATION_ON_ACTUAL_TRANSACTION);
return jmsTransactionManager;
}
答案 0 :(得分:0)
嗯,我认为你的问题在这里:
/**
* Create a new JmsTransactionManager for bean-style usage.
* <p>Note: The ConnectionFactory has to be set before using the instance.
* This constructor can be used to prepare a JmsTemplate via a BeanFactory,
* typically setting the ConnectionFactory via setConnectionFactory.
* <p>Turns off transaction synchronization by default, as this manager might
* be used alongside a datastore-based Spring transaction manager like
* DataSourceTransactionManager, which has stronger needs for synchronization.
* Only one manager is allowed to drive synchronization at any point of time.
* @see #setConnectionFactory
* @see #setTransactionSynchronization
*/
public JmsTransactionManager() {
setTransactionSynchronization(SYNCHRONIZATION_NEVER);
}
因此,您必须手动将其切换为setTransactionSynchronization(AbstractPlatformTransactionManager.SYNCHRONIZATION_ALWAYS);