What is the Difference Between persist() and merge()
persist() and merge() are two key EntityManager methods for managing entity lifecycles. Understanding the difference between them is critical for proper Hibernate usage.
Overview
persist() and merge() are two key EntityManager methods for managing entity lifecycles. Understanding the difference between them is critical for proper Hibernate usage.
Junior Level
Main Difference
persist() - for new entities (performs INSERT).
merge() - for updating detached entities (performs UPDATE or INSERT).
// persist - for new entities
User newUser = new User();
newUser.setName("John");
entityManager.persist(newUser); // INSERT
// newUser is now Persistent
// merge - for detached entities
User detached = getDetachedUser(); // obtained detached somewhere
User managed = entityManager.merge(detached); // UPDATE
// managed - new managed copy
// detached remains detached!
Behavior
| persist() | merge() | |
|---|---|---|
| For | Transient | Detached |
| SQL | INSERT | UPDATE or INSERT |
| Returns | void | Managed entity |
| Source object | Becomes managed | Remains detached |
persist() Example
User user = new User();
user.setName("John");
entityManager.persist(user);
// user became Persistent
// Can continue working with user
user.setName("Jane"); // dirty checking will trigger
merge() Example
User detached = getDetachedUser();
detached.setName("Changed");
User managed = entityManager.merge(detached);
// detached - still detached!
// managed - new managed copy
detached.setName("NotSaved"); // will NOT affect DB
managed.setName("WillBeSaved"); // will affect (dirty checking)
Middle Level
When merge() is Actually Needed
merge() is typically needed in web applications: an object came from the frontend (JSON -> DTO -> entity), it’s detached - merge copies its state into the managed context.
persist() in Detail
User user = new User();
entityManager.persist(user);
// What happens:
// 1. Checks that entity is not managed
// 2. Generates ID (if auto)
// 3. Adds to persistence context
// 4. Schedules INSERT on flush
// 5. user becomes Persistent
// After persist, can work with it
user.setName("Updated"); // dirty checking
merge() in Detail
User detached = getDetachedUser();
User managed = entityManager.merge(detached);
// What happens inside:
// 1. Checks if entity with same ID exists in persistence context
// 2. If not - loads from DB (SELECT)
// 3. Copies state from detached to managed
// 4. Schedules UPDATE on flush (if changes exist)
// 5. Returns managed entity
// IMPORTANT:
// detached remains detached!
// All further operations - with managed!
persist vs merge - When to Use What
persist() - Transient -> Persistent, INSERT
merge() - Detached -> Persistent, UPDATE or INSERT
Common Mistakes
// merge() for new entity - unnecessary SELECT
User newUser = new User();
entityManager.merge(newUser);
// First SELECT (existence check), then INSERT
// persist() for new
entityManager.persist(newUser); // Immediate INSERT
// persist() for detached entity with set ID - EntityExistsException. If ID is generated at DB level, behavior depends on generation strategy.
User detached = getDetachedUser();
entityManager.persist(detached); // EntityExistsException
// merge() for detached
entityManager.merge(detached);
// Ignoring merge() return value
entityManager.merge(detached); // result lost!
detached.setName("Changed"); // NOT saved!
// Use return value
User managed = entityManager.merge(detached);
managed.setName("Changed"); // will be saved
Generic save Method
public <T extends BaseEntity> T save(T entity) {
if (entity.getId() == null) {
entityManager.persist(entity);
return entity;
} else {
return entityManager.merge(entity);
}
}
Senior Level
Internal Implementation
persist():
1. check if entity already managed -> error if yes
2. check entity state (not detached)
3. schedule INSERT (doesn't execute immediately!)
4. add to persistence context
5. entity becomes managed
merge():
1. check if entity with same ID in persistence context
-> yes: use existing managed entity
-> no: SELECT from DB
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
Key difference:
- persist: original entity becomes managed
- merge: returns NEW managed instance, original stays detached
Performance
persist():
- 0 SELECT queries
- 1 INSERT on flush
merge():
- 1 SELECT (existence check)
- 1 UPDATE or INSERT on flush
- + state copying
For new entities, persist() is faster by 1 SELECT!
Advanced Scenarios
// Scenario: update with version check
@Version
private Integer version;
User detached = getDetachedUser();
User managed = entityManager.merge(detached);
// On flush: UPDATE WHERE id = ? AND version = ?
// If version doesn't match -> OptimisticLockException
Best Practices
persist() for new entities
merge() for detached entities
Check ID before choosing method
Use merge() return value
Understand that merge returns a copy
Generic save() with ID check
merge() for new (unnecessary SELECT)
persist() for detached (error)
Ignoring merge() return value
merge() for persistent (unnecessary SELECT)
Interview Cheat Sheet
Must know:
- persist() - for new entities (Transient -> Persistent), performs INSERT
- merge() - for detached entities, returns a NEW managed object, performs UPDATE/INSERT
- persist() is faster by 1 SELECT (doesn’t check existence)
- merge() does SELECT before UPDATE/INSERT - checks if entity exists in DB
- Original after merge() remains detached - all operations with returned object
- Generic save(): check ID == null -> persist, otherwise merge
Frequent follow-up questions:
- Why does merge() do SELECT? To check if entity exists and load current state for dirty checking
- What if persist() is called for detached? EntityExistsException - Hibernate thinks entity is already managed
- Why does merge() return a new object? Original detached may be from another session, Hibernate creates managed copy in current session
- When does merge() do INSERT? When entity with that ID doesn’t exist in DB
Red flags (DO NOT say):
- “merge() for new entities” - unnecessary SELECT
- “I ignore merge() return value” - changes won’t be saved
- “persist() for detached” - EntityExistsException
- “merge() for persistent” - unnecessary SELECT, pointless
Related topics:
- [[7. Describe the Entity Lifecycle in Hibernate]]
- [[8. What Are the Transient, Persistent, Detached, Removed States]]
- [[12. What is Dirty Checking in Hibernate]]
- [[13. How Does the Flush Mechanism Work in Hibernate]]
- [[15. What Does the refresh() Method Do]]