JPA / Hibernate如何正确增加数据库中的计数器?

时间:2014-12-04 00:58:31

标签: hibernate jpa counter increment race-condition

对于某些人来说,这听起来似乎是一个简单的问题但是在数据库中增加计数器的正确方法是什么?

例如,如果我有一个包含" like_count"每次用户喜欢照片时都会更新的列。

(假设我有我的照片@Entity)

Photo photo = photoRepository.findByPhotoId(id)
photo.setLikeCount(photo.getLikeCount()+1);
photoRepository.save(photo)

例如,上面的代码是否正确?是否会出现任何种族情况?

谢谢

1 个答案:

答案 0 :(得分:1)

我认为代码不正确。并行运行的线程稍后完成更新,但之前读过相同的计数器,将覆盖计数器,因此丢失一个计数。

它还取决于事务隔离级别,如果您使用SERIALIZATION或'REPEATABLE_READ'是安全的,但通常使用READ COMMITTED,这通常会显示此问题通常与Oracle等数据库或PostgreSQL的。

另外值得注意的是,至少Hibernate默认保​​存整个实体,而不仅仅是修改了列。因此,更改不同的列会工作。您可以使用@DynamicUpdate更改此设置,但这种硬性行为更改可能会产生一些副作用,至少在脏字检查字段以刷新到DB时会产生一些副作用。

解决方案:

正确的解决方案是:

  1. 悲观锁定:使用SELECT ... FOR UPDATE锁定行 - 这可能对性能不利,因为所有作者都必须等待,并且所有读者如果在作者上都是活跃的
  2. 原子更新:更好,因为它没有使用悲观锁定: UPDATE photo SET likecount = likecount + 1 WHERE id = :id
  3. 乐观锁定方法。但是你必须处理OptimisticLockingExceptions并重复交易