2014-12-04 67 views
3

也許這聽起來像是一個簡單的問題,但是在數據庫中增加計數器的正確方法是什麼?JPA/Hibernate如何正確增加數據庫中的計數器?

例如,如果我有一個包含「like_count」列的表格,每次用戶喜歡照片時都會更新。

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

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

例如,是上面的代碼是否正確?會出現任何種族情況嗎?

謝謝

+0

我認爲你是對的。我只給出兩個建議:第一個是你不需要使用photoRespository.save(),因爲照片實體已經與會話關聯。如果你修改它的態度,修改將在交易結束時保持不變。第二個是,當用戶喜歡這張照片時,你應該記錄用戶的ID,因爲通常一個用戶可以只喜歡一張照片。 – MageXellos 2014-12-04 01:17:19

+0

謝謝你的回覆!很高興知道我不需要擔心這樣的競賽條件或其他問題。我試過不使用.save,但它不會保留在我的數據庫中? (我的數據庫是PostgreSQL,我正在使用entityManager(不是會話)是這個原因嗎? – Johny19 2014-12-04 08:53:22

+0

我現在的工作仍在開發一個使用PostgreSQL的軟件,但是我們還沒有使用entityManager,所以我不能給你一個令人驚訝的答案。知道,如果你使用會話,你真的不需要在同一會話中使用save()。也許這就是爲什麼你必須使用save()來保存實體,但我認爲它沒問題。 – MageXellos 2014-12-05 00:55:11

回答

1

我認爲代碼是不正確的。一個並行運行線程稍後完成更新,但之前讀取了相同的計數器,將覆蓋計數器,因此一個計數丟失。

它還取決於事務隔離級別,如果您使用的是SERIALIZATION或'REPEATABLE_READ',則安全,但通常使用READ COMMITTED,這通常會在Oracle或PostgreSQL等數據庫中顯示此問題。

還值得注意的是,至少Hibernate是保存完整的實體不僅默認修改列。所以改變不同的列不是工作。你可以用@DynamicUpdate來改變這個,但是這個硬性的行爲改變至少在性能方面可能會有一些副作用,那就是髒的檢查字段到數據庫。

解決方案:

正確的解決方案是:

  1. 悲觀鎖:與SELECT ... FOR UPDATE鎖定該行 - 差這可不好的性能,因爲所有的作家必須等待,也是所有讀者如果對作家
  2. 原子更新:更好,因爲它沒有使用悲觀鎖定:但那麼你必須處理OptimisticLockingExceptions並重復交易