我的 SQL 表中有一个字段需要更新 1 并返回一个唯一的 ID。但看起来它没有更新最新数据,特别是当我发出很多请求时。
@Transactional
public interface CompanyRepository extends CrudRepository<Company, Integer> {
@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query(value = "update Company c set c.accessId = :accessId WHERE c.id = :companyId AND c.accessId = :oldAccessId", nativeQuery = true)
int updateAccessId(@Param("companyId") Integer companyId, @Param("accessId") Integer accessId, @Param("oldAccessId") Integer oldAccessId);
}
即使将clearAutomatically 和flushAutomatically 设置为true,它也无法处理最新数据。
我可以看到两个更新查询都成功,并且 oldAccessId 相同。
表格设计是否应该改变?
PS:我也尝试过不使用nativeQuery = true。
最佳答案
这里是一个经典的竞争条件。
两个线程读取具有相同 accessId
的同一实体,将其加一,然后使用您在问题中显示的方法写入结果。结果实际上只有一次更新。
有多种方法可以解决此问题。
使用 JPA 和乐观锁定。
假设您有一个带有
@Version
注释的属性,您可以执行以下操作 以下为单一交易方法:- 加载实体。
- 增加
accessId
。 - 保留实体。
如果另一个事务尝试在同一实体上执行相同的操作,则两者之一将出现异常。在这种情况下,请重试,直到更新完成。
使用数据库。
使数据库中的读取和更新成为原子操作。不要将新值作为参数传递,而是使用如下查询:
update Company c set c.accessId = c.accessId + 1 WHERE c.id = :companyId
将其设为版本属性。
如上所述,JPA 已经具有
@Version
属性,这些属性会在每次更改时更新。根据具体要求,您可以将accessId
设置为该属性并自动更新。检查是否有任何行已更新。
根据您的评论,您的意图是基本上重新实现 JPA 对版本属性的操作。如果您这样做,您就会错过一个关键部分:通过将返回值与 1 进行比较来检查更新是否实际更新了任何内容。
关于java - Spring Data 未更新最新数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53274785/