【问题标题】:How to ensure @Entity exists in @Async methods?如何确保@Entity 存在于@Async 方法中?
【发布时间】:2015-11-28 21:34:16
【问题描述】:

我想创建一个实体,并在事务中触发@Async 方法来对同一实体执行一些更改。更改也应该保持异步。

问题:由于我必须在事务中触发异步方法,我可以使用从实体自动生成的@Id。但是 async 方法必须首先通过该 Id 获取实体,而且大多数情况下这还不存在。

只有当我在异步方法中放入一些Thread.sleep()作为第一条语句时,才能确保实体已被外部事务持久化。

但是那个解决方案不是很好。问题:如何确保在异步方法内部它应该等待实体存在于数据库中?

@Service
public class OuterService {
    @Service
    private SyncService service;

    @Transactional
    public void process() {
        service.mySyncMethod();
        //etc
    }
}

@Service
public class SyncService {
    @Transactional
    public void mySnycMethod() {
        Entity entity = new MyEntity();
        //fill entity
        dao.save(entity);
        asyncService.performLongRunningTask(entity.getId());
    }
}

@Service
public class AsycnService {
    @Async
    @Transactional
    public voi performLongRunningTask(Long id) {
        //problem: is mostly == null because this is executed before the outer transaction completes
        //only works if I put like Thread.sleep(5000) in between. but how can I really ensure the entity exists before executing this async lookup?
        MyEntity entity = dao.findOne(id);

        //perform long running task
        //change some fields in entity accordingly
        dao.save(entity);
    }
}

【问题讨论】:

  • 使您的OuterService 不是事务性的,并将对AsyncService.performLongRunningTask 的调用移至该位置。这确保了事务已经完成并且实体在数据库中。
  • 那么传递分离的实体而不是 id 是否有意义,以将 dao 查找保存在异步方法中?
  • 您仍然需要重新附加它,因为会话不再有效。我会简单地传递 ID。
  • 好的,谢谢。无论如何,我可能无法使OuterService 不是事务性的,因为在导致问题的特定调用之前和之后有很多逻辑(还有必须参与 tx 的数据库/dao 调用)......
  • 然后将所有这些调用包装在事务中,而不是对异步内容的调用。您可以将其移动到可以注释事务的不同类。您只需要在调用异步方法之前确保事务已提交。想办法做到这一点。

标签: java spring hibernate asynchronous


【解决方案1】:

您可以使用TransactionSynchronizationManager.registerSynchronization() 注册事务提交挂钩并实现afterCommit() 方法。

@Transactional
public void mySnycMethod() {
    Entity entity = new MyEntity();
    // fill entity
    dao.save(entity);
    // performLongRunningTask will start after the transaction has been
    // commited
    TransactionSynchronizationManager
            .registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() {
                    asyncService.performLongRunningTask(entity.getId());
                }
            });

}

但请注意 Javadocs 关于在您的应用程序中使用 TransactionSynchronizationManager 的说法:

供资源管理代码使用,但不被典型应用程序使用 代码

【讨论】:

  • 嗯,这是一篇很老的帖子,但我也面临同样的问题。主方法在 DB 中注册一个需要长时间处理的操作并将处理委托给 @Async 方法。在 2020 年,除了您的“黑客”@Ruben 之外,没有其他解决方案了吗? (我的意思是 JMS 非常适合这种情况,但是 @Async 的解决方案?)
  • 您可以一个接一个地调用一个方法,但在调用异步方法之前必须确保第一个方法已完成事务。例如经过。用 @Transactional(propagation=REQUIRES_NEW) 包装同步方法
猜你喜欢
  • 2020-04-07
  • 2013-05-10
  • 2021-02-13
  • 1970-01-01
  • 2017-08-25
  • 1970-01-01
  • 2021-09-07
  • 2018-11-09
  • 1970-01-01
相关资源
最近更新 更多