Poo*_*a B 0 mysql spring asynchronous transactional redis
我有一个Spring应用程序,该@Transactional方法使用一种方法更新MySQL DB中的特定实体详细信息,并且在同一方法中,我试图调用另一个端点,使用@Async该端点是另一个Spring应用程序,该应用程序从MySql DB中读取相同的实体并更新其中的值Redis存储。
现在的问题是,每次我更新实体的某些值时,有时会在redis中更新它,有时却没有。
当我尝试调试时,我发现有时第二个应用程序从MySql读取实体时会选择旧值而不是更新值。
谁能建议我可以做些什么来避免这种情况,并确保第二个应用程序始终从Mysql中选择该实体的更新值?
M. Deinum的答案很好,但是还有另一种方法可以实现此目标,具体情况取决于您当前应用程序的状态。
您可以简单地将对异步方法的调用包装在一个事件中,该事件将在当前事务提交后处理,因此您每次都将正确地从db中读取更新的实体。
做到这一点很简单,让我向您展示:
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
@Transactional
public void doSomething() {
// application code here
// this code will still execute async - but only after the
// outer transaction that surrounds this lambda is completed.
executeAfterTransactionCommits(() -> theOtherServiceWithAsyncMethod.doIt());
// more business logic here in the same transaction
}
private void executeAfterTransactionCommits(Runnable task) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
public void afterCommit() {
task.run();
}
});
}
Run Code Online (Sandbox Code Playgroud)
基本上,这里发生的是我们为当前事务回调提供了一个实现,并且只覆盖了afterCommit方法-那里还有其他可能有用的方法,请检查一下。为了避免在其他部分使用相同的样板代码,或者只是为了使方法更具可读性,请避免输入相同的样板代码,我在辅助方法中提取了该样板代码。
解决方案并不难,显然您希望在数据写入数据库后触发并更新。@Transactional方法执行完毕后才提交。如果@Async在方法末尾调用另一个方法,则事务可能已提交或未提交,具体取决于提交(或实际 REST 调用)的持续时间。
由于事务之外的某些内容只能看到已提交的数据,因此它可能会看到更新的数据(如果已经提交)或仍然是旧的数据。这也取决于事务的序列化级别,但出于性能原因,您通常不希望在数据库上使用排他锁。
要解决此问题,@Async不应从内部调用该方法@Transactional,而应在其之后调用。这样,数据始终会提交,其他服务将看到更新的数据。
@Service
public class WrapperService {
private final TransactionalEntityService service1;
private final AsyncService service2;
public WrapperService(TransactionalEntityService service1, AsyncService service2) {
this.service1=service1;
this.service2=service2;
}
public updateAndSyncEntity(Entity entity) {
service1.update(entity); // Update in DB first
service2.sync(entity); // After commit trigger a sync with remote system
}
}
Run Code Online (Sandbox Code Playgroud)
该服务是非事务性的,因此推测service1.update它将@Transactional更新数据库。完成后,您可以触发外部同步。
| 归档时间: |
|
| 查看次数: |
1277 次 |
| 最近记录: |