番石榴事件总线 post 在 transaction/commit 之后

guava eventbus post after transaction/commit

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

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

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

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

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

我能想到的唯一快速解决方法是异步处理它并等待一两秒钟。但是,在提交事务后,是否有另一种方法可以 post 使用 guava 处理事件?

我认为 guava 根本没有“意识到”spring,尤其是它的“@Transactional”东西。

所以你需要一个创造性的解决方案。我能想到的一种解决方案是将此代码移动到您确定交易已完成的地方。 实现这一目标的一种方法是使用 TransactionSyncrhonizationManager:

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

请注意,如果事务失败(回滚),则不会调用该方法,在这种情况下,您可能需要 afterCompletion 方法。参见 documentation

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

@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 的机制。

例如参见this tutorial