在@Transactional方法中处理Spring事务和JMSTemplate

3
在我们的Spring Boot应用程序中,我们对数据库进行了分页循环,每一页都使用JMSTemplate发送一个JMS消息。包含该循环的方法是@Transactional标记的。创建JMSTemplate时,已将事务标志设置为true。
我一直在浏览JMSTemplate的源代码,据我所见,如果已经有外部事务正在进行,它不会提交事务,而是将其放入该事务中。
现在让我们考虑以下代码:
@Transactional
public void loopThroughPages(String destination, String from, String to) {
    Pageable pageRequest = PageRequest.of(0, maxPageSize);
    Page<Event> eventPage;
    do {
       eventPage = eventRepo.getEventsInTimeRangeForDestination(from, to, destination, pageRequest);
       if(eventPage.hasContent()) {
          Message<String> eventMessage = buildEventMessage(eventPage.getContent());
          JmsTemplate template = queueService.createTemplate(destination);
          template.send(eventMessage);
          pageRequest = eventPage.nextPageable();
       }
    } while(pageRequest != null && eventPage.hasNext());
}
createTemplate 方法创建了一个 DynamicJmsTemplate,使用了一个 CachingConnectionFactory 并将 setSessionTransacted 设置为 true
我现在不太确定这与事务有何关系。我的理解是所有的 N 页消息都会在由 loopThroughPages 创建的事务中发送,一旦 loopThroughPages 方法完成,它将提交所有的 N 条消息,而不是在每个消息被发送后进行提交。这也意味着 MQ 上的事务将保持打开状态,直到处理完最后一页。这种理解正确吗?

我已经删除了关于JmsTemplatecommitclose的评论,因为它不一定会关闭获取到的Session,只有在无法从ConnectionFactoryUtils.doGetTransactionalSession获取事务性会话时才会关闭。 - Yanick Salzmann
1个回答

3
这里的关键点是事务管理。
如果您使用XA数据源并在Spring Boot应用程序上进行配置,则会有分布式事务,如果您有一个带有@Transactional注释的方法,则Spring将管理您的事务的提交/回滚,否则您将具有本地事务管理,您的数据库和消息系统的事务将不会同步。
对于发送消息,您可以通过属性配置消息是否持久化,这意味着您的消息将由消息系统持久化,而对于监听器方面,您可以配置确认模式。顺便说一句,我的建议是让Spring管理事务,一切都会很好,但真正需要注意的是,如果您想要在数据库和JMS系统之间进行分布式事务,您只需配置Atomikos即可作为一个可用选项,否则足以不手动管理事务并让Spring为您管理。
希望这能帮到您。

很有趣。我们的应用程序没有使用XA事务(因此是本地事务),但我注意到在当前问题中,循环从T + 00s开始,在T + 42s时,MQ系统会中止事务,因为它运行时间过长,需要释放日志空间。当前分页实现的性能较差,这表明MQ“Session”的事务仅在循环结束时提交(或在此情况下被MQ强制回滚)。这是矛盾的吗?我不太确定。 - Yanick Salzmann
1
这不是矛盾的,因为应用程序创建的事务管理器会使用默认的JpaTransactionManager和每个QueueConnectionFactoryJmsTransactionManager创建一个ChainedTransactionManager,在这些事务中使用 SYNCHRONIZATION_ALWAYS。因此根据我的理解,这些事务将被绑定在一起,提交只会在循环结束时发生。 - Yanick Salzmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接