Spring Boot 2.7升级至3.3后,新事务内查询获取实体时乐观锁失效问题求助
最近我把项目从Spring Boot 2.7升级到3.3后,遇到了一个乐观锁的异常问题——当在新事务中从数据库查询获取实体时,乐观锁完全不生效了,具体场景如下:
@Entity class MyEntity{ @Id long id; @Version Long version; } class A { private B bService; @Transactional(readOnly = true) public void execute() { try { bService.begin(10); } catch (OptimisticLockingFailureException e) { log.... // 在Spring 3.3中这段代码从未触发过 } } class B { @Transactional(propagation = Propagation.REQUIRES_NEW) public MyEntity begin(Long id) { MyEntity freshMyEntity = repository.findAwaiting(id) .orElseThrow(EntityNotFoundException::new); // 此时另一个事务提交了同ID的实体 // 或者我在这里手动暂停,然后在数据库中手动提交修改 // 两种情况都会导致数据库里的version字段被递增 return repository.save(freshMyEntity); } }
奇怪的是,如果我移除repository.findAwaiting查询操作,让实体作为参数直接传入begin方法,乐观锁就可以正常工作。我是不是漏掉了什么配置?这看起来像是个bug,但我不确定是不是自己理解错了?
EDIT 1:我的预期行为
我原本以为begin方法里的save操作应该抛出异常,因为此时另一个事务已经修改了同ID实体的version字段,但实际情况是当前事务居然成功提交了...
return repository.save(freshMyEntity);
EDIT 2:关于repository.save()的返回结果repository.save()返回的是完全相同的对象——相同的引用、相同的version值。我原本以为它应该返回一个更新后的新对象。
EDIT 3:调试后的发现
经过长时间调试,我找到了问题的根源:当UpdateCoordinatorStandart.doStaticUpdate执行UPDATE操作时,代码中final MutationExecutor mutationExecutor = executor( session, staticUpdateGroup, false );这一行选择了MutationExecutorSingleBatched作为执行器,而这个执行器并没有实现AbstractMutationExecutor中的OperationResultChecker resultChecker接口。
在COMMIT执行UPDATE的阶段,ModelMutationHelper.checkResults( resultChecker, statementDetails, affectedRowCount, -1 );这行代码并没有被执行,因此不会抛出异常,乐观锁也就失效了。
而在旧事务模式下执行时,走的是完全不同的代码路径:实体版本检查不是在COMMIT阶段执行,而是在EntityManager的MERGE操作中进行,并且会从数据库刷新目标对象,之后还会执行update结果的version校验。
EDIT 4:问题上报状态
目前这个问题已经上报至Hibernate官方Issue平台,现在我需要找到临时的解决方案...
备注:内容来源于stack exchange,提问作者viking

