【问题标题】:"Row was updated or deleted by another transaction" - but it's deleted in the same transaction“行已被另一个事务更新或删除” - 但它在同一个事务中被删除
【发布时间】:2022-02-28 22:09:24
【问题描述】:

在我的应用程序中,我使用 Hibernate 和 Spring Data 将测量数据导入数据库。源可以为具有现有数据库数据的时间戳提供数据,以便进行修改。因此,在重新写入条目之前,该时间段内的所有条目(由源数据给出)都将被删除。

对于现有数据,我在saveAll() 上收到此异常:

“行已被另一个事务更新或删除”(或未保存值映射不正确)

但是,应该在 same 事务中删除该行,我希望 Hibernate 明白它必须重新INSERT 条目而不是执行UPDATE

存储库方法由 Spring Data 提供。这是我的代码的简化版本:

@Transactional(rollbackFor = Exception.class)
private void import(List<Entry> entries) {
    // ...
    this.measurementRepo.removeAllByTimeIsBetween(firstDt, lastDt);
    this.measurementRepo.saveAll(measurements);
}

这里有什么问题?这是不允许的吗?还是认为这两个操作都属于一个事务是错误的假设?但是,它应该可以工作,即使有两个事务。有时间问题吗?是否保证操作按给定顺序执行?是不是 Spring Data 的问题?

我不能在两个单独的事务中执行这些操作(如果这可能是一个解决方案),因为我只想在确定也插入新行的情况下删除行。

我解决此问题的想法是检测现有行并对具有匹配时间戳的条目执行更新而不是删除。我不确定这是否可行。

【问题讨论】:

  • 你是如何进行删除和保存的?如果您纯粹使用托管实体,它应该可以工作,您可以从 persistentremoved 并返回,但如果您混合其他操作,它可能会混淆。另外,数据库获取 DELETE 和 INSERT 是硬性要求,还是在休眠级别进行优化就可以了?
  • 我只调用 Spring Data 存储库方法。甚至 remove... 方法也是由 Spring Data 自动实现的。删除/插入不是硬性要求。实际上是这样写的,以使其尽可能简单。检查条目是否已存在比将它们全部删除更复杂且容易出错。

标签: spring hibernate spring-data-jpa spring-data spring-transactions


【解决方案1】:

在使用Hibernate时有一个“问题”。 Hibernate 优化语句执行顺序,删除是最后完成的(我不知道确切的细节),因此您遇到了问题。

要解决这个问题,请使用deleteInBatch。这样会先删除,问题就解决了。我假设你有一个方法getAllByTimeIsBetween。但请根据您的需要调整以下内容:

@Transactional(rollbackFor = Exception.class)
private void import(List<Entry> entries) {
    // ...
    Object elementsToRemove = this.measurementRepo.getAllByTimeIsBetween(firstDt, lastDt);
    measurementRepo.deleteInBatch(elementsToRemove);

    this.measurementRepo.saveAll(measurements);
}

以下question 可以为您提供有关 deleteInBatch 的更多详细信息。这不是同一个问题,但一些答案对 Spring JPA 和 Hibernate 有一些细节。

【讨论】:

  • 即使这通常是正确的 - 这里似乎是一个不同的问题。看我的回答。我仍然想知道为什么它不适用于两个单独的交易。您在回答中所说的问题应该只是单个事务中的问题,因为我怀疑 Hibernate 会尝试优化多个事务之间的查询(?)我想知道这在技术上是否可行。
【解决方案2】:

@Transactional 不适用于 bean 内部调用。 Spring 为这些对象创建了一个代理,但它无法拦截类本身内部的方法调用(例如,this.import() 的调用)。

即使将import() 移动到单独的类之后,它也不起作用。那是因为意外的是 protected 而不是 public。我以为是public,因为我不记得在Java 中允许外部类访问其他类的受保护成员(如果它们在同一个包中)。这就是为什么我错过了documentation 的重点:

使用代理的另一个注意事项是,只有公共方法才能使用@Transactional 进行注释。任何其他可见性的方法将简单地忽略注释,因为它们没有被代理。

它现在可以工作,但我仍然不知道为什么它不适用于两个单独的事务。一个删除数据的事务,然后另一个创建新条目的事务应该不是问题。

【讨论】:

    猜你喜欢
    • 2013-09-07
    • 1970-01-01
    • 2021-10-03
    • 1970-01-01
    • 2017-02-08
    • 1970-01-01
    • 2021-08-12
    • 1970-01-01
    • 2020-04-02
    相关资源
    最近更新 更多