假设有一个服务组件,使用 CrudRepository
的 Book
实体 . 假设服务的一个方法应该是事务性的,并且应该(除其他外)用事务语义从数据库中删除一个实体(即,如果删除不可能执行,则应该回滚所有效果) .
大致,
@Component
public class TraService {
@Autowired
BookRepo repo;
@Transactional
public void removeLongest() {
//some repo.find's and business logic --> Book toDel
repo.delete(toDel);
}
}
现在这应该在多线程环境中工作,例如在Spring MVC中 . 为简单起见,我启动了两个线程,每个线程都在一个提供了对 TraService
bean的引用的任务上 . 日志显示,确实创建了两个 EntityManager
并绑定到相应的线程 . 但是,当第一个线程成功使用 delete
时,另一个线程抛出
org.springframework.orm.jpa.JpaOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1;
从中我不知道如何恢复(即我怀疑回滚未完成,并且调用事务方法后线程应该执行的代码将不会被执行) . Worker 代码:
public void run() {
service.removeLongest(); //transactional
System.out.println("Code progressing really well " + Thread.currentThread()); //not executed on thread with exception
}
我们如何在Spring / JPA中正确处理这样的事务 delete
?
1 回答
Short answer: 乐观锁定异常的正确行为是 Catch the exception and Retry .
Long answer: 乐观锁定是一种假设的互斥策略
来自:https://en.wikipedia.org/wiki/Optimistic_concurrency_control
乐观锁定主要是因为性能而存在,并且通常使用一个字段来实现,该字段考虑了使用
version
计数器的每个修改 . 如果在事务期间version
计数器发生更改,则表示正在进行并发修改并导致引发异常 .悲观锁定将阻止任何可能的并发修改,更容易管理,但性能更差 .