← Назад к вопросам

Что случается с версией, если она не совпала в оптимистической блокировке

2.2 Middle🔥 161 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Оптимистическая блокировка и конфликты версий

Оптимистическая блокировка — это механизм в JPA/Hibernate, который проверяет совпадение версии перед обновлением записи. Если версия не совпала, возникает исключение OptimisticLockException.

Как это работает

Когда версия не совпадает, это означает, что данные были изменены другим процессом между моментом загрузки сущности и её сохранением:

// 1. Загружаем сущность (version = 1)
Product product = repository.findById(1L);
// Между этим и следующей строкой другой процесс изменил записанный

// 2. Изменяем сущность
product.setPrice(100.00);

// 3. Пытаемся сохранить
repository.save(product); // javax.persistence.OptimisticLockException!
// Потому что version стала 2 в БД, а мы пытаемся обновить с version = 1

Сущность с версией

@Entity
@Table(name = "products")
public class Product {
    @Id
    private Long id;
    
    @Version
    private Long version;
    
    private String name;
    private BigDecimal price;
    
    // getters/setters
}

Поле @Version автоматически управляется Hibernate:

  • При каждом обновлении версия увеличивается на 1
  • Перед UPDATE запросом проверяется: WHERE id = ? AND version = ?

Что случается при несовпадении

  1. Выбрасывается исключениеOptimisticLockException
  2. Транзакция откатывается — изменения не применяются
  3. Приложение должно обработать ошибку

Стратегии обработки

Вариант 1: Повтор операции

public void updateProductWithRetry(Long productId, BigDecimal newPrice) {
    int maxAttempts = 3;
    for (int i = 0; i < maxAttempts; i++) {
        try {
            Product product = repository.findById(productId)
                .orElseThrow();
            product.setPrice(newPrice);
            repository.save(product);
            return;
        } catch (OptimisticLockException e) {
            if (i == maxAttempts - 1) {
                throw new RuntimeException("Failed after retries", e);
            }
            // Повторяем попытку
        }
    }
}

Вариант 2: Уведомление пользователя

@Transactional
public void updateProduct(Long id, ProductDTO dto) {
    try {
        Product product = repository.findById(id).orElseThrow();
        product.setPrice(dto.getPrice());
        repository.save(product);
    } catch (OptimisticLockException e) {
        throw new ProductWasModifiedException(
            "Товар был изменён другим пользователем. Пожалуйста, перезагрузите страницу"
        );
    }
}

Когда это полезно

Оптимистическая блокировка эффективна при:

  • Высокой вероятности отсутствия конфликтов
  • Большом количестве конкурентных операций
  • Читаемых операциях значительно больше чем писаемых

Когда конфликты редки, оптимистическая блокировка даёт лучшую производительность, чем пессимистическая (SELECT FOR UPDATE).

Важные замечания

  • Версия сравнивается в условии WHERE UPDATE запроса
  • Если версия не совпала — ноль строк обновлено, выбросится исключение
  • Это правильное и надёжное средство против race conditions
  • Требует аккуратной обработки исключения в бизнес-логике
Что случается с версией, если она не совпала в оптимистической блокировке | PrepBro