我遇到一个问题,因为实体的某个删除是在 2 个线程中执行的,所以出现了 StaleStateException
。这是因为由 REST 调用启动的线程正在删除对象并调用另一个服务来清理其中的一些内容,但该另一个服务最终将一些数据放在 Kafka 流上。这个 Kafka 流不断地从另一个线程读取,也会导致对象被删除。但取决于谁先到达那里我可以得到这个异常(exception):
由以下原因引起:org.hibernate.StaleStateException:批量更新从更新 [0] 返回意外的行计数;实际行数:0;预期:1;执行的语句:HikariProxyPreparedStatement@821401258 包装从 notification_setting 中删除,其中 id='f47ef4b1-eb54-4d3a-a6f0-fc4518704288'::uuid 和 db_version=0
现在,在这种特殊情况下,删除是由系统操作完成的,我真的不关心 StateStateException
和产生的 OptimisticLockingException
。这两个操作都只需删除该对象,我不介意谁先到达它。但我不想从该实体中完全删除 @Version 属性。我可以暂时禁用特定删除的乐观锁定吗?
代码不太相关,但就在这里。我已经尝试过按 id 删除,但 deleteAll
也有同样的问题。
eventLevelSubscriptions.forEach(
eventLevelSubscription -> notificationEventLevelSubscriptionRepo.deleteById(eventLevelSubscription.getId()));
notificationSettings.forEach(notificationSetting -> notificationSettingRepo.deleteById(notificationSetting.getId()));
em.flush();
for (NotificationSettingKey notificationSettingKey : notificationSettingKeys) {
kafkaWritingService.removeNotificationSetting(producer, notificationSettingKey);
}
注意刷新,因为我们需要确保这部分在更新 Kafka 上的另一个流之前成功。
我尝试简单地尝试/捕获整个代码,因为我什至不需要在第二个线程中执行删除,因为它们已经在第一个线程中完成,但是 Hibernate 似乎进入了损坏状态,其中最后还是抛出异常。
Exception in thread "Thread-6" org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:753)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:631)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:385)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:99)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
...
at java.base/java.lang.Thread.run(Unknown Source) (note: this is my kafka consumer thread)
最佳答案
A) 您无法禁用乐观锁定。你为什么要使用它?此异常可能表明您的逻辑不一致。检查一下你的逻辑是否真的正确。
B) 如果在检查后您仍然想保持逻辑不变,则意味着您应该在这种情况下将此异常视为正确行为(否则返回 A 并检查/更改逻辑),这意味着您应该捕获异常。
C) 如果您当前的代码在单个事务中运行:请勿在单个步骤中删除所有实体,因为其中一些实体可能已被删除,而其他实体则尚未删除。解决方案如下:
- 将对
deleteById()
的调用放入新方法 - 将此方法的事务传播设置为REQUIRES_NEW
- 为了使事务传播生效,请将此方法放入单独的类中
- 在
forEach()
中调用此方法而不是调用存储库
D) 还考虑一种非面向对象的方法:使用 JPQL 或 SQL 删除条目。优点:如果之前已经删除了一些条目,也不会有异常(exception)。
关于java - 即使实体具有 @Version 属性,我也可以在 Hibernate 中暂时禁用乐观锁定吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59467843/