Що таке @Version і навіщо вона потрібна
@Version — анотація JPA для реалізації оптимістичного блокування. Вона автоматично відстежує версію сутності та запобігає конфліктуючим оновленням від кількох транзакцій.
Огляд
@Version — анотація JPA для реалізації оптимістичного блокування. Вона автоматично відстежує версію сутності та запобігає конфліктуючим оновленням від кількох транзакцій.
🟢 Junior Level
Що таке @Version
@Version — анотація для оптимістичного блокування. Автоматично відстежує версію сутності та запобігає конфліктуючим оновленням.
@Entity
public class Order {
@Id
private Long id;
@Version
private Integer version; // збільшується автоматично
private String status;
}
Навіщо потрібна
Коли дві транзакції оновлюють один об’єкт, @Version гарантує що одна з них отримає помилку:
Initial: Order(id=1, version=1, status="new")
Транзакція 1: читає (version=1)
Транзакція 2: читає (version=1)
Транзакція 2: оновлює → UPDATE WHERE version=1 → version=2 ✅
Транзакція 1: оновлює → UPDATE WHERE version=1 → 0 rows → ❌ OptimisticLockException
Як це працює — просто
1. При INSERT: version = 0. При кожному UPDATE: version = version + 1.
2. При кожному UPDATE: WHERE clause: WHERE id = ? AND version = ?
3. Якщо version не співпав → 0 rows updated → OptimisticLockException
🟡 Middle Level
Типи полів version
@Version
private Integer version; // int — збільшується на 1
@Version
private Long version; // long — збільшується на 1
@Version
private Timestamp version; // timestamp — оновлюється поточним часом
Типові помилки
// ❌ Ручна зміна version
order.setVersion(0); // ❌ зламає механізм блокування
// ❌ Немає @Version для важливих даних
@Entity
public class Account {
// немає @Version → два потоки можуть одночасно змінити баланс
}
// ❌ Ігнорування OptimisticLockException
try {
em.flush();
} catch (OptimisticLockException e) {
// мовчки проігнорували → дані втрачено
}
🔴 Senior Level
Внутрішня реалізація
Hibernate:
- При INSERT: version = 0 (або 1 для деяких типів)
- При UPDATE: version = version + 1
- WHERE clause: WHERE id = ? AND version = ?
- Якщо 0 rows → OptimisticLockException
Для Timestamp:
- version = CURRENT_TIMESTAMP
- Timestamp-версія небезпечна: якщо дві транзакції оновлять сутність в одну мілісекунду, conflict НЕ виявиться. В production використовуйте Integer/Long, а не Timestamp.
OPTIMISTIC_FORCE_INCREMENT
// Збільшити version без зміни entity
em.lock(order, LockModeType.OPTIMISTIC_FORCE_INCREMENT);
// Коли потрібно:
// - При зміні дочірньої колекції
// - Для примусової інвалідації кешу
Performance implications
@Version overhead:
- +1 column в таблиці
- +1 condition в WHERE при UPDATE
- Практично negligible
Benefits:
- Запобігання lost updates
- Немає блокувань (на відміну від pessimistic)
- Автоматичне управління
Best Practices
✅ @Version на всіх mutable сутностях
✅ Retry при OptimisticLockException
✅ Не змінювати version вручну
✅ Моніторинг частоти конфліктів
❌ Без @Version для критичних даних
❌ Ручне управління version
❌ Ігнорування OptimisticLockException
🎯 Шпаргалка для співбесіди
Обов’язково знати:
- @Version — анотація для оптимістичного блокування, автоматично збільшує version при UPDATE
- WHERE clause: WHERE id = ? AND version = ? — якщо 0 rows → OptimisticLockException
- Типи: Integer/Long (збільшуються на 1), Timestamp (оновлюється поточним часом, небезпечний)
- OPTIMISTIC_FORCE_INCREMENT — збільшує version без зміни entity
- @Version на всіх mutable сутностях — запобігає lost updates
Пов’язані теми:
- [[17. Як реалізувати оптимістичне блокування в JPA]]
- [[18. Як реалізувати песимістичне блокування в JPA]]
- [[15. Що робить метод refresh()]]
- [[20. Як працюють каскадні операції (Cascade)]]