JPA/Hibernate как правильно увеличить счетчик в базе данных?

Возможно, для некоторых это будет звучать как простой вопрос, но как правильно увеличить счетчик в базе данных?

Например, если у меня есть таблица, которая содержит столбец "like_count", который обновляется каждый раз, когда пользователю нравится фотография.

(Предположим, у меня есть моя фотография @Entity)

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

Например, является ли приведенный выше код правильным? Произойдет ли какое-либо состояние гонки?

Спасибо

1 ответ

Я думаю, что код не является правильным. Параллельно работающий поток, завершающий обновление позже, но предварительно прочитавший тот же счетчик, перезапишет счетчик, и поэтому один счет теряется.

Это также зависит от уровня изоляции транзакции, если вы используете SERIALIZATION или "REPEATABLE_READ" вы в безопасности, но обычно READ COMMITTED используется, что бы показать эту проблему обычно с базой данных, как Oracle или PostgreSQL.

Также следует отметить, что по крайней мере Hibernate по умолчанию сохраняет всю сущность, а не только измененные столбцы. Таким образом, изменение другого столбца не работает. Вы можете изменить это с помощью @DynamicUpdate но, может быть, это сложное изменение поведения имеет некоторые побочные эффекты, по крайней мере, в отношении производительности для грязной проверки поля для сброса в БД.

Решения:

Правильные решения:

  1. Пессимистическая блокировка: заблокируйте ряд SELECT ... FOR UPDATE - плохо, это может быть плохо для производительности, потому что все писатели должны ждать, а также все читатели, если на писателе активен
  2. Атомные Обновления: Лучше, потому что это не использует пессимистическую блокировку:UPDATE photo SET likecount = likecount + 1 WHERE id = :id
  3. Оптимистичные подходы к блокировке. Но тогда вы должны справиться OptimisticLockingExceptions и повторить транзакцию
Другие вопросы по тегам