【问题标题】: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
有没有办法解决这个问题?
【问题讨论】:
标签:
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
从批处理中调用时,可能有一个已经在运行的事务,因此在事务结束时检测到并发删除,更新将失败。