【问题标题】:How to handle lock on a db entity in a multithreaded application?如何在多线程应用程序中处理数据库实体的锁定?
【发布时间】:2022-01-17 07:37:18
【问题描述】:

我有以下两种方法:

public void retrieveAndInsert(String number){
     Entity e = repository.findByNumber(number).orElseGet(() -> new Entity());
     //...update some part of the entity... 
     repository.save(e);
}
public void deleteEntity(String number){
  repository.deleteByNumber(number);
}

这两个方法可以批量调用,怕是下面的顺序:

  • Retrieve 由线程调用并检索实体,开始更新
  • 同时调用 delete 并删除底层行
  • 在第一个方法上调用 save() 会发生什么?从理论上讲,实体将与数据库分离,同时仍保留 ID

有没有办法解决这个问题?

【问题讨论】:

  • 您使用的是什么数据库?这可能取决于您的隔离级别。
  • 微软 sql 服务器

标签: hibernate spring-data-jpa


【解决方案1】:

save 上会发生什么取决于输入retrieveAndInsert 时是否有事务在运行。


如果没有事务正在运行:

  • 实体将在为存储库调用 (findByNumber) 自动创建的第一个事务中读取,并在返回实体时关闭(已分离)
  • 在分离实体的save 上,打开第二个事务,并且休眠检查:
    • 实体是否需要为updated(以防数据库中存在具有相同ID的行)
    • inserted(如果找不到实体,“合并瞬态实例”(在这种情况下,如果它是@GeneratedValue,也会生成一个新ID,替换旧的ID值!) -> 因此,如果实体在 save 之前被删除,则会插入一个新条目。

(请注意,在hibernate 5.6版本中,DefaultMergeEventListener中有一个TODO,它建议抛出异常而不是“Merging transient instance”,这样这种行为可能会改变。)


如果事务已经在运行:

  • 实体将在当前事务中读取并以“持久”状态返回
  • save 上,什么都不会发生,因为实体不是新的/分离的
  • 在提交事务时,将发出一个update,它不会影响任何行(即使预计会有一个受影响的行),因此会导致StaleStateException

从批处理中调用时,可能有一个已经在运行的事务,因此在事务结束时检测到并发删除,更新将失败。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多