首页 > 解决方案 > 交易/提交后的番石榴事件总线发布

问题描述

我目前正在春季使用番石榴的事件总线,虽然到目前为止一般功能运行良好,但我遇到了以下问题:

当用户想要更改“线”实体上的数据时,这将在后端服务中照常处理。在此服务中,数据将首先通过 JPA 持久化,然后我创建一个“NotificationEvent”并引用已更改的实体。通过 EventBus,我将线路的引用发送给所有订阅者。

public void notifyUI(String lineId) {
    EventBus eventBus = getClientEventBus();
    eventBus.post(new LineNotificationEvent(lineId));
}

事件总线本身是在后台使用 new EventBus() 创建的。

现在在这种情况下,我的订阅者位于 @Transactional 领域之外的前端。因此,当我更改数据时,发布事件并让订阅者从数据库中获取所有必要的更新,实际事务尚未提交,这使得订阅者获取旧数据。

我能想到的唯一快速解决方法是异步处理它并等待一两秒钟。但是,在事务提交之后,还有其他方法可以使用 guava 发布事件吗?

标签: springguavaevent-bus

解决方案


我认为番石榴根本没有“意识到”春天,尤其是没有“@Transactional”的东西。

所以你需要一个创造性的解决方案。我可以考虑的一种解决方案是将此代码移动到您确定事务已完成的位置。实现这一目标的一种方法是使用TransactionSyncrhonizationManager

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){
           void afterCommit(){
                // do what you want to do after commit
                // in this case call the notifyUI method
           }
});

请注意,如果事务失败(回滚)该方法将不会被调用,在这种情况下您可能需要afterCompletion方法。查看文档

另一种可能的方法是将您的应用程序重构为如下所示:

@Service
public class NonTransactionalService {

   @Autowired 
   private ExistingService existing;

   public void entryPoint() {

      String lineId = existing.invokeInTransaction(...);
      // now you know for sure that the transaction has been committed
      notifyUI(lineId);
   }
}

@Service
public class ExistingService  {

   @Transactional
   public String invokeInTransaction(...) {
      // do your stuff that you've done before
   }
}

我想在这里提到的最后一件事是,Spring 本身提供了一种事件机制,您可以使用它来代替 guava 的机制。

例如,请参阅本教程


推荐阅读