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