我的一个服务中有以下代码:
@Override
@Transactional
@RetryConcurrentOperation(exception = Exception.class, retries = 12)
public void test() {
Player player = this.playerRepository.findPlayerById(1L);
player.setFirstName("SomeName");
}
我使用的重试机制是这里描述的机制:http://josiahgore.blogspot.co.il/2011/02/using-spring-aop-to-retry-failed.html
问题是当我得到乐观重试(第二次重试)时,我得到一个异常:
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [xxx]
有趣的是,当我删除事务注释时,该机制是有效的,并且在非事务函数中,我调用了一个不同的事务方法:
// THIS WORKS:
@Override
@RetryConcurrentOperation(exception = Exception.class, retries = 12)
public void test() {
execute();
}
@Override
@Transactional
public void execute() {
Player player = this.playerRepository.findPlayerById(1L);
player.setFirstName("SomeName");
}
有什么想法吗?当从事务函数调用这个方面重试机制时,为什么它没有成功?
从非事务性test()
调用@Transactional
execute()
时,不会应用execute()
中的@Transactional
。这是因为它是从对象的一个方法直接到另一个方法的直接调用,从而绕过事务代理。
有关使用this
调用事务函数时代理如何工作以及@Transactional
为何不工作的更多详细信息,请参阅此答案。
还可以看看SpringRetryTemplate,它是解决这个问题的基于Spring的解决方案。
关于重试机制,它不适用于使用版本化实体(具有@Version
列)的情况,即抛出StaleObjectStateException
的情况。
原因是有另一个线程正在更新数据库上的实体,增加了版本列。
解决方案是refresh()
实体(加载最新版本),重新应用修改并重试。重复多次相同的修改只会在没有版本的实体的情况下起作用,而且这可能不是你想要的,因为一个线程所做的更改会被另一个线程静默地覆盖。