Питання 14 · Розділ 16

У чому різниця між persist() і merge()

persist() та merge() — два ключові методи EntityManager для управління життєвим циклом сутностей. Розуміння різниці між ними критично важливе для правильного використання Hibern...

Мовні версії: English Russian Ukrainian

Огляд

persist() та merge() — два ключові методи EntityManager для управління життєвим циклом сутностей. Розуміння різниці між ними критично важливе для правильного використання Hibernate.


🟢 Junior Level

Основна відмінність

persist() — для нових сутностей (виконує INSERT).

merge() — для оновлення detached сутностей (виконує UPDATE або INSERT).

// persist — для нових сутностей
User newUser = new User();
newUser.setName("John");
entityManager.persist(newUser);  // INSERT
// newUser тепер Persistent

// merge — для detached сутностей
User detached = getDetachedUser();
User managed = entityManager.merge(detached);  // UPDATE
// managed — нова managed копія
// detached залишається detached!

Поведінка

  persist() merge()
Для Transient Detached
SQL INSERT UPDATE або INSERT
Повертає void Managed entity
Вихідний об’єкт Стає managed Залишається detached

🟡 Middle Level

persist() детально

User user = new User();
entityManager.persist(user);

// Що відбувається:
// 1. Перевіряє що entity не managed
// 2. Генерує ID (якщо auto)
// 3. Додає в persistence context
// 4. Планує INSERT при flush
// 5. user стає Persistent

merge() детально

User detached = getDetachedUser();
User managed = entityManager.merge(detached);

// Що відбувається всередині:
// 1. Перевіряє чи є entity з таким ID в persistence context
// 2. Якщо немає — завантажує з БД (SELECT)
// 3. Копіює стан з detached в managed
// 4. Планує UPDATE при flush (якщо є зміни)
// 5. Повертає managed entity

// ВАЖЛИВО:
// detached залишається detached!
// Всі подальші операції — з managed!

Типові помилки

// ❌ merge() для нової сутності — зайвий SELECT
User newUser = new User();
entityManager.merge(newUser);

// ✅ persist() для нових
entityManager.persist(newUser);  // Одразу INSERT

// ❌ persist() для detached сутності — EntityExistsException
User detached = getDetachedUser();
entityManager.persist(detached);  // ❌

// ✅ merge() для detached
entityManager.merge(detached);

// ❌ Ігнорування return value merge()
entityManager.merge(detached);  // результат втрачено!
detached.setName("Changed");    // НЕ збережеться!

// ✅ Використовуйте return value
User managed = entityManager.merge(detached);
managed.setName("Changed");  // збережеться

Generic save метод

public <T extends BaseEntity> T save(T entity) {
    if (entity.getId() == null) {
        entityManager.persist(entity);
        return entity;
    } else {
        return entityManager.merge(entity);
    }
}

🔴 Senior Level

Внутрішня реалізація

persist():
1. check if entity already managed → error if yes
2. schedule INSERT (не виконує одразу!)
3. add to persistence context
4. entity becomes managed

merge():
1. check if entity with same ID in persistence context
2. if not in DB: create new managed instance
3. copy state from detached to managed
4. schedule UPDATE if state changed
5. return managed entity

Продуктивність

persist():
- 0 SELECT запитів
- 1 INSERT при flush

merge():
- 1 SELECT (перевірка існування)
- 1 UPDATE або INSERT при flush

Для нових entities persist() швидший на 1 SELECT!

Best Practices

✅ persist() для нових сутностей
✅ merge() для detached сутностей
✅ Перевіряти ID перед вибором методу
✅ Використовувати return value merge()
✅ Generic save() з перевіркою ID

❌ merge() для нових (зайвий SELECT)
❌ persist() для detached (помилка)
❌ Ігнорування return value merge()

🎯 Шпаргалка для співбесіди

Обов’язково знати:

  • persist() — для нових (Transient → Persistent), виконує INSERT
  • merge() — для detached, повертає НОВИЙ managed об’єкт, виконує UPDATE/INSERT
  • persist() швидший на 1 SELECT (не перевіряє існування)
  • Оригінал після merge() залишається detached — всі операції з returned object
  • Generic save(): перевірка ID == null → persist, інакше merge

Пов’язані теми:

  • [[7. Опишіть життєвий цикл Entity в Hibernate]]
  • [[8. Що таке стани transient, persistent, detached, removed]]
  • [[12. Що таке dirty checking в Hibernate]]
  • [[13. Як працює механізм flush в Hibernate]]
  • [[15. Що робить метод refresh()]]