Keywords: JPA | EntityManager | persist method | merge method | performance optimization
Abstract: This article provides an in-depth exploration of the core differences between persist() and merge() methods in JPA EntityManager, analyzing their working mechanisms, applicable scenarios, and performance impacts through detailed code examples. Based on authoritative Q&A data and professional reference articles, it systematically explains the fundamental distinctions where persist() is used for new entities and merge() for detached entities, revealing different behavioral patterns under IDENTITY, SEQUENCE, and ASSIGNED identifier strategies. The article also identifies common performance anti-patterns and provides best practice guidance for developers.
Core Concepts and Fundamental Differences
In the Java Persistence API (JPA), EntityManager provides two primary entity operations: persist() and merge(). While both involve entity persistence, their semantics and behaviors differ fundamentally. The persist() method is specifically designed to add new entity instances to the persistence context, making them managed entities; whereas the merge() method is used to merge the state of detached entities into the current persistence context, returning a managed entity copy.
Working Mechanism of persist()
The persist() method takes an entity instance as a parameter, adds it to the persistence context, and makes that instance managed. This means any subsequent modifications to the entity will be automatically synchronized to the database when the transaction commits. From JPA's perspective, an entity is considered new when it has never been associated with a database row, meaning no table record matches the entity in question.
Consider the following code example:
MyEntity e = new MyEntity();
// Scenario 1
// Transaction starts
em.persist(e);
e.setSomeField(someValue);
// Transaction ends, update to someField is synchronized to databaseIn this scenario, after the persist() call, entity e becomes managed, and subsequent setSomeField() calls are tracked by the persistence context, generating corresponding UPDATE statements upon transaction commit.
Working Mechanism of merge()
The merge() method takes an entity parameter, merges its state into the current persistence context, and returns a managed entity instance. Importantly, the passed entity instance itself does not become managed, and any modifications to it will not be automatically tracked unless merge() is called again.
Consider the following comparative examples:
// Scenario 2
// Transaction starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue);
// Transaction ends, changes to someField are not updated to database
// Scenario 3
// Transaction starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue);
// Transaction ends, changes to someField through e2 are updated to databaseIn Scenario 2, modifications to the original entity e are not persisted because e is not managed. In Scenario 3, modifications made through the managed entity e2 returned by merge() are correctly tracked and persisted.
Impact of Identifier Strategies on Behavior
Different identifier generation strategies affect the specific behaviors of persist() and merge() methods:
IDENTITY Strategy
When an entity uses IDENTITY generator, the persist() operation executes the INSERT statement immediately, as this is the only way to obtain the identifier value. This disables JDBC batch inserts.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;SEQUENCE Strategy
With SEQUENCE strategy, the INSERT statement can be delayed until flush time, allowing Hibernate to apply batch insert optimizations.
ASSIGNED Strategy
For manually assigned identifiers, using merge() instead of persist() results in an additional SELECT statement to verify whether a record with the same identifier already exists in the database. This can be optimized by adding a @Version property.
Performance Considerations and Anti-patterns
In practical applications, a common performance anti-pattern is unnecessarily calling save or merge methods on already managed entities:
@Transactional
public void savePostTitle(Long postId, String title) {
Post post = postRepository.findOne(postId);
post.setTitle(title);
postRepository.save(post); // Unnecessary call
}In this case, the save() call is redundant because the entity is already managed, and Hibernate automatically tracks changes and generates UPDATE statements. Unnecessary merge() calls trigger MergeEvent, wasting CPU cycles, especially when cascade operations are involved.
Best Practices Summary
Based on the above analysis, the following best practices can be derived: Always use persist() for new entities; use merge() to reattach detached entities to the persistence context; for already managed entities, no save method calls are needed as Hibernate automatically synchronizes state. Avoid unnecessarily triggering merge operations in Spring Data JPA's save method, particularly when using assigned identifier strategies.