Що таке dirty checking в Hibernate
Dirty checking — механізм автоматичного відстеження змін сутностей в persistence context та їх збереження в базу даних. Це одна з ключових можливостей Hibernate, яка позбавляє р...
Огляд
Dirty checking — механізм автоматичного відстеження змін сутностей в persistence context та їх збереження в базу даних. Це одна з ключових можливостей Hibernate, яка позбавляє розробника від ручного написання UPDATE-запитів.
🟢 Junior Level
Що таке dirty checking
Dirty checking — Hibernate автоматично відстежує зміни у сутностей в persistence context і при збереженні автоматично генерує UPDATE-запит.
@Transactional
public void updateUser(Long id, String name) {
// 1. Завантаження з БД — сутність стає Persistent
User user = entityManager.find(User.class, id);
// 2. Зміна поля — Hibernate "помічає" зміну
user.setName(name);
// 3. НЕ ПОТРІБЕН em.merge() чи em.save()!
// При commit — Hibernate автоматично зробить UPDATE
}
Як це працює — просто
1. Завантаження: Hibernate завантажує entity і зберігає його "знімок" (snapshot)
2. Зміна: ви змінюєте поле в об'єкті
3. Flush: Hibernate порівнює snapshot з поточним станом
4. Якщо різні → генерує UPDATE SQL
5. Якщо однакові → нічого не робить
🟡 Middle Level
Детальний механізм dirty checking
Крок 1: Завантаження entity
- entityManager.find(User.class, 1L)
- Hibernate створює EntityEntry з snapshot
- snapshot = копія всіх полів на момент завантаження
Крок 2: Зміна
- user.setName("New Name")
- Hibernate НЕ робить нічого одразу
Крок 3: Flush (при commit або явно)
- Hibernate обходить всі entities в persistence context
- Для кожного: порівнює snapshot з поточним станом
- Якщо є зміни → schedule UPDATE
- Виконує всі UPDATE в БД
- Оновлює snapshot
Коли відбувається flush
// 1. При commit транзакції (автоматично)
@Transactional
public void update() {
user.setName("New");
} // commit → flush → UPDATE
// 2. При entityManager.flush() (явно)
entityManager.flush(); // UPDATE виконано
// 3. Перед виконанням запиту (щоб повернути актуальні дані)
user.setName("New");
List<User> users = entityManager.createQuery("FROM User", User.class)
.getResultList(); // перед цим — flush!
Оптимізація — read-only сутності
// Якщо сутність не буде змінюватися — позначити як read-only
User user = entityManager.createQuery("FROM User u WHERE u.id = :id", User.class)
.setParameter("id", id)
.setHint("org.hibernate.readOnly", true)
.getSingleResult();
// Переваги:
// 1. Не створюється snapshot (економія пам'яті)
// 2. Dirty checking не виконується
// 3. Менше overhead на flush
🔴 Senior Level
Внутрішня реалізація
PersistenceContext:
StatefulPersistenceContext {
entityEntries: Map<Object, EntityEntry>
}
EntityEntry {
loadedState: Object[] // snapshot при завантаженні
state: Object[] // поточний стан
status: Status // MANAGED, DELETED, READ_ONLY
id: Serializable
version: Object
}
Алгоритм flush:
1. Для кожного entity в entityEntries:
if entity.status == MANAGED:
if !Arrays.equals(entry.loadedState, entry.state):
scheduleUpdate(entity)
2. Виконати всі scheduled UPDATE
3. Оновити loadedState = state
Performance характеристики
Dirty checking overhead:
- O(N) де N = число entities в persistence context
- Для кожного entity: array comparison всіх полів
- Для 10k+ entities — може бути помітно повільно
Оптимізації:
- read-only hint → O(0) (пропускає entity)
- clear() періодично → зменшує N
- StatelessSession → без dirty checking
Best Practices
✅ Dirty checking для простих оновлень
✅ Read-only hint для запитів без змін
✅ entityManager.clear() для великих операцій
✅ Розуміння коли відбувається flush
✅ Періодичний flush + clear в batch операціях
❌ Dirty checking для великих persistence context
❌ Без read-only hint коли не потрібне оновлення
❌ Ігнорування performance impact
🎯 Шпаргалка для співбесіди
Обов’язково знати:
- Dirty checking — Hibernate автоматично відстежує зміни managed-сутностей
- При завантаженні зберігається snapshot, при flush порівнюється з поточним станом
- Не потрібен explicit merge() чи save() для оновлень managed-сутностей
- O(N) overhead де N = число entities в persistence context
- Flush відбувається при: commit, entityManager.flush(), перед запитом
- Read-only hint економить пам’ять — не створюється snapshot, dirty checking пропускається
Пов’язані теми:
- [[7. Опишіть життєвий цикл Entity в Hibernate]]
- [[13. Як працює механізм flush в Hibernate]]
- [[9. Що таке кеш першого рівня в Hibernate]]
- [[14. У чому різниця між persist() і merge()]]