JPA EntityManager: Understand Differences between Persist and Merge
- Details
- Written by Nam Ha Minh
- Last Updated on 28 February 2022   |   Print Email
If you’ve been using entity manager in a Java application that uses Hibernate/JPA and/or Spring Data JPA, you would notice that the EntityManager interface defines the persist() and merge() operations like this (from Java EE’s Javadocs):
- persist(Object entity): Make an instance managed and persistent.
- merge(T entity): Merge the state of the given entity into the current persistence context.
It seems that both can be used to persist an entity object into database. So what are the similarity and difference between merge and persist? When using persist? When using merge?
Read on, to find the answers in my explanation with some code examples below.
1. Semantics of the Persist Operation
According to the API specification, the persist() method takes a parameter of type Object and returns void:
void persist(Object entity): Make an instance managed and persistent.
Here, entity is an object of a class that is mapped to a table in database. It should be a new, unmanaged entity instance. After the persist operation executed successfully:
- a new row would be inserted into the corresponding table in database.
- the entity object becomes a managed instance or persistent object.
- the entity manager track changes of persistent objects. Any changes made to the mapped fields of the entity object will be synchronized with the database, provided that the changes happen within the transaction boundary.
Let’s see some code examples to understand these semantics of the persist operation. Say we have an entity class as follows:
package net.codejava; import javax.persistence.*; @Entity public class Contact { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "contact_id") private Integer id; private String name; private String email; private String address; private String phone; // getters and setters are now shown for brevity }
And we write some code for testing the persist operation as follows:
package net.codejava; @Repository public class GenericDAO { @Autowired private EntityManager entityManager; @Transactional public void testPersistNewObject() { Contact newContact = new Contact(); newContact.setName("John Doe"); newContact.setEmail("john.doe@gmail.com"); newContact.setAddress("Fremont, CA"); newContact.setPhone("123456-2111"); boolean entityManaged = entityManager.contains(newContact); System.out.println("Before persist, entity managed: " + entityManaged); entityManager.persist(newContact); entityManaged = entityManager.contains(newContact); System.out.println("After persist, entity managed: " + entityManaged); } }
And the test class is a Spring Boot command line application, like this:
package net.codejava; @SpringBootApplication public class JpaSemanticTester implements CommandLineRunner { @Autowired private GenericDAO dao; public static void main(String[] args) { SpringApplication.run(JpaSemanticTester.class, args); } @Override public void run(String... args) throws Exception { dao.testPersistNewObject(); } }
Run the program and you would see it prints the following output:
Before persist, entity managed: false Hibernate: insert into… After persist, entity managed: true
which means a new row inserted into the database, and the newContact object becomes a managed instance.
Now, let’s modify the code to make changes to the newContact object after persist operation:
@Transactional public void testPersistNewObject1() { Contact newContact = new Contact(); newContact.setName("John Doe"); newContact.setEmail("john.doe@gmail.com"); newContact.setAddress("Fremont, CA"); newContact.setPhone("123456-2111"); entityManager.persist(newContact); newContact.setName("Frank Matt"); newContact.setEmail("frank.matt@gmail.com"); }
Run the program again, and you would see the following output:
Hibernate: insert into contact (address, email, name, phone) values (?, ?, ?, ?) Hibernate: update contact set address=?, email=?, name=?, phone=? where contact_id=?
This means the persist operation results in the SQL insert statement. And the changes made to the persistent object result in the SQL update statement. Note that all the code is within a transaction boundary (the method is marked as @Transactional). It won’t behave like that if that changes were made outside the transaction, e.g. in service or controller layer.
Also note that we can’t pass a detached entity object to the persist() method. Given the following code:
@Transactional public void tryPersistDetachedObject() { Contact contact = new Contact(); contact.setId(13); contact.setName("Nam Ha Minh"); contact.setEmail("nam.ha.minh@gmail.com"); contact.setAddress("New York, USA"); contact.setPhone("123456-2111"); entityManager.persist(contact); }
This code would result in the following error:
org.hibernate.PersistentObjectException: detached entity passed to persist: net.codejava.Contact
That means we can’t use persist operation to save an existing entity object. Make sense?
2. Semantics of the Merge Operation
According to the API docs:
<T> T merge(T entity): Merge the state of the given entity into the current persistence context
Here, entity can be an unmanaged or persistent entity instance. This method returns the managed instance that the state was merged to.
After the merge operation executed successfully:
- a new row would be inserted, or an existing one would be updated.
- the returned object is different than the passed object.
- if the passed object is unmanaged, it says unmanaged.
- the entity manager tracks changes of the returned, managed object within transaction boundary.
Now, let’s see some code examples. Given the following code that is used to update an existing contact:
@Transactional public void testUpdateExistingObject() { Contact existContact = new Contact(); existContact.setId(13); existContact.setName("Nam Ha Minh"); existContact.setEmail("namhm@codejava.net"); existContact.setAddress("Tokyo, Japan"); existContact.setPhone("123456-2111"); Contact updatedContact = entityManager.merge(existContact); boolean passedObjectManaged = entityManager.contains(existContact); System.out.println("Passed object managed: " + passedObjectManaged); boolean returnedObjectManaged = entityManager.contains(updatedContact); System.out.println("Returned object managed: " + returnedObjectManaged); }
Run the code, and you would see the following output:
Hibernate: select ... from contact contact0_ where... Passed object managed: false Returned object managed: true Hibernate: update contact set address=?,...
You see, this means the merge operation firstly selects a row from the database according to ID field of the entity object, then it merges the changes from the code with the one in database, which results in the SQL update statement.
Note that you can also use the merge() method to persist a new, unmanaged entity instance like the persist() method.
3. Summary Differences between Persist and Merge
So far I hope you have had better understand about EntityManager’s persist and merge operations. To summary, use the persist() method if you want to be sure that only new object will be persisted, i.e. implementing Add functionality in your application.
And use the merge() method if you want to persist new object as well as updating existing one, i.e. implementing Save functionality in your application.
I recommend you watch the following video that explains the differences between persist and merge in more visual ways:
Related Spring and Database Tutorials:
- Spring Data JPA EntityManager Examples (CRUD Operations)
- Spring Data JPA Custom Repository Example
- Understand Spring Data JPA with Simple Example
- Spring Data JPA Native Query Examples
- Spring MVC with JdbcTemplate Example
- How to configure Spring MVC JdbcTemplate with JNDI Data Source in Tomcat
- Spring and Hibernate Integration Tutorial (XML Configuration)
- Spring MVC + Spring Data JPA + Hibernate - CRUD Example
Comments