我在我的SpringBoot应用程序中使用JPA存储库我有一个后台任务,定期将我的数据库中的某些元素设置为“准备好”。 我还有一个用户可以调用的端点,它可以修改同一个表中的一行。
有没有办法避免彼此取消彼此写?采取这种情况
Table:
Key
id name is_ready
0)初始数据有一个键(1 no_name false)
1)后台任务启动并即将通过设置
修改表Keyis_ready to true
Key key = repo.findKeyByIsReady(false)
key.setIsReady(true)
repo.save(key) <--- does NOT yet execute this
2)用户调用api端点将密钥名称更改为&#34; new_name&#34;并完成
3)现在后台服务执行repo.save(key),最终数据为
1 no_name true
而不是
1 new_name true
基本上后台任务已覆盖用户设置的密钥名称
有没有办法避免这种情况?交易如何在这里有所帮助?
答案 0 :(得分:2)
这通常通过额外的锁来解决:
您在第二个事务中检测到该行已被其他人更改。然后,您要么告诉用户并要求手动修复或尝试自动合并更改。
要实现它,您必须向表中添加其他列 - 版本。然后在更新行时,查询将如下所示:
UPDATE ..., version=old_version+1 WHERE id=old_id and version=old_version
如果WHERE
关闭没有找到该行(因为其他人增加了版本),更改的行数将为0(JDBC从DB获取此信息)并且JPA会在这种情况下抛出错误。
在JPA中,必须将附加字段映射为@Version
。
每次更新实体时都使用构造:
select ... for update
当第二个事务发出这样的语句时,DB会锁定该请求,直到第一个事务完成。如果在特定时间段内没有发生这种情况,您将从JPA获得例外。有关详细信息,请参阅EntityManager#lock()
方法。
答案 1 :(得分:0)
有几种方法可以解决这个问题 -
仅更新is_ready字段
使用具有更严格的数据库隔离级别的事务锁定行 -
@Transactional(isolation=Isolation.SERIALIZABLE)