假设有一个服务组件,使用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中。为简单起见,我启动了2个线程,每个线程都在一个提供对TraService
bean的引用的任务上。日志显示,确实创建了两个EntityManager
并绑定到相应的线程。但是,当第一个线程成功使用delete
时,另一个线程会抛出
org.springframework.orm.jpa.JpaOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1;
我不知道如何恢复(即我怀疑回滚未完成,并且调用事务方法后线程应该执行的代码将不会被执行)。工人的代码:
public void run() {
service.removeLongest(); //transactional
System.out.println("Code progressing really well " + Thread.currentThread()); //not executed on thread with exception
}
我们如何在Spring / JPA中正确处理此类事务delete
?
答案 0 :(得分:2)
简答:乐观锁异常的正确行为是抓住异常并重试。
长答案:乐观锁定是一种假设
的互斥策略来自:https://en.wikipedia.org/wiki/Optimistic_concurrency_control
多个交易可以经常完成而不会相互干扰
乐观锁定的存在主要是因为性能,并且通常使用一个字段来实现,该字段考虑了使用version
计数器的每个修改。如果在事务期间version
计数器发生更改,则表示正在进行并发修改并导致抛出异常。
悲观锁定会阻止任何可能的并发修改,更容易管理,但性能更差。