What’s the difference between persist, save, merge and update? Which one should you use?
Take your skills to the next level!
The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.
JPA and Hibernate provide different methods to persist new and to update existing entities. You can choose between JPA’s persist and merge and Hibernate’s save and update methods.
It seems like there are 2 pairs of 2 methods that do the same. You can use the methods persist and save to store a new entity and the methods merge and update to store the changes of a detached entity in the database. That’s why a lot of developers are wondering which of these methods they should use. Let’s take a closer look at the details and small differences of these methods.
Special thanks to Steve Ebersole (Lead Developer – Hibernate ORM) who provided his feedback and great insights on some of Hibernate’s hidden implementation details!
Entity State Transitions
Before we get into the details of these 4 methods, I need to give you a quick introduction to JPA’s entity lifecycle states.
If an entity is attached to the current persistence context, it has the lifecycle state managed. That means that it is mapped to a database record. Your persistence provider generates the required SQL INSERT and UPDATE statements to propagate all changes. A managed entity is also stored in the 1st level cache.
When you create a new entity, it’s in the transient state. It remains in this state until you attach it to the current persistence context. I will show you how you can do that with JPA’s persist and Hibernate’s save method, in the following section. As long as an entity is in the transient state, it is not mapped to a database record and not managed by any persistence context.
Entities in the detached lifecycle state are no longer managed by the persistence context. That can be the case because you closed the persistence context or you explicitly detached the entity from the current context. I will get into more details about how you can reattach these entities with JPA’s merge and Hibernate’s update methods in a later part of this post.
And the last lifecycle state is removed. These entities were previously in the state managed, before you scheduled them for removal. Removing entities is outside of the scope of this post, so I will not get into too many details about it. You can schedule an entity for removal by calling the remove method on the EntityManager interface.
Persisting A New Entity Using persist Or save
When you create a new entity object, it’s in the transient lifecycle state. It does not map any database record.
Author a = new Author(); a.setFirstName("Thorben"); a.setLastName("Janssen");
You need to attach the entity to a persistence context so that it becomes managed and gets persisted in the database. You can either use JPA’s persist or Hibernate’s save method to do that. Both methods seem to do the same, but there are a few differences.
Specification vs. Proprietary API
The most obvious difference is that the JPA specification defines the persist method. You can use it with all JPA implementations. The save method, on the other hand, is Hibernate-specific. It is, therefore, not available in other JPA implementations.
But that’s only relevant if you want to be able to replace Hibernate with another JPA implementation, like Eclipse Link or OpenJPA.
Return Types And Execution Of SQL Statements
Another obvious difference between these 2 methods is their return type. JPA’s persist method returns void and Hibernate’s save method returns the primary key of the entity.
That might seem like a huge difference, especially when you take a closer look at Hibernate’s Javadoc and the JPA specification:
- The Javadoc of Hibernate’s save method states that it generates the primary key value first:
Persist the given transient instance, first assigning a generated identifier.
Javadoc Session.save(entity) - You don’t find any information about this in the JPA specification. It doesn’t define when the primary key value has to be assigned. So, the persistence provider can do that at any time between the call of the persist method and the flush of the persistence context.
In most cases, it doesn’t make any difference if you call the save or persist method. Hibernate uses the name of the entity class and the primary key value to store the entity in the first level cache. It, therefore, needs a primary key value when it executes the persist method.
In almost all situations, Hibernate generates the primary key value immediately and triggers an SQL statement if necessary, when you call the persist or save method.
But that is not the case if you use the IDENTITY strategy and try to persist an entity without an active transaction or with FlushMode.MANUAL. If you call the persist method in one of these situations, Hibernate delays the execution of the SQL INSERT statement and creates a temporary primary key value. But if you call the save method, Hibernate performs the SQL INSERT statement immediately and retrieves the primary key value from the database.
You can then retrieve it as the return value of the save method.
Author a = new Author(); a.setFirstName("Thorben"); a.setLastName("Janssen"); Long id = (Long) em.unwrap(Session.class).save(a);
Or you can call the getter method of the primary key attribute of your managed entity if you use JPA’s persist method.
Author a = new Author(); a.setFirstName("Torben"); a.setLastName("Janssen"); em.persist(a); Long id = a.getId();
Hibernate executes the same SQL statements when you call the persist or the save method. Which and when it does that depends on your primary key generation strategy:
Not generated
If you set the primary key value programmatically, e.g. to a natural identifier, Hibernate only performs an SQL INSERT statement when it flushes the persistence context.
14:08:34,979 INFO TestPersistSaveMerge:237 - Save entity 14:08:35,052 INFO TestPersistSaveMerge:240 - Commit transaction 14:08:35,123 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)
Generated with IDENTITY strategy
If you use the IDENTITY strategy to generate the primary key value, Hibernate needs to execute the INSERT statement when you call the save or persist method to retrieve the primary key value from the database.
14:09:28,264 INFO TestPersistSaveMerge:237 - Save entity 14:09:28,336 DEBUG SQL:92 - insert into Author (firstName, lastName, version) values (?, ?, ?) 14:09:28,354 INFO TestPersistSaveMerge:240 - Commit transaction
Generated with SEQUENCE strategy
And if you use the SEQUENCE, Hibernate performs an SQL SELECT statement to retrieve the next value from the database sequence. Hibernate then delays the INSERT statement until it flushes the persistence context. In this example, the flush happens when the transaction gets committed.
14:10:27,994 INFO TestPersistSaveMerge:237 - Save entity 14:10:28,002 DEBUG SQL:92 - select nextval ('hibernate_sequence') 14:10:28,042 INFO TestPersistSaveMerge:240 - Commit transaction 14:10:28,096 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)
Generated with TABLE strategy
You shouldn’t use the TABLE strategy because it requires row level locks on the primary key table and doesn’t scale well. If you use this strategy anyways, Hibernate performs an SQL SELECT statement to retrieve the next primary key value from the database and writes the new value to the database table. It delays the execution of the SQL INSERT statement for the new entity until it flushes the persistence context.
14:11:17,368 INFO TestPersistSaveMerge:237 - Save entity 14:11:17,482 DEBUG SQL:92 - select tbl.next_val from hibernate_sequences tbl where tbl.sequence_name=? for update of tbl 14:11:17,531 DEBUG SQL:92 - insert into hibernate_sequences (sequence_name, next_val) values (?,?) 14:11:17,534 DEBUG SQL:92 - update hibernate_sequences set next_val=? where next_val=? and sequence_name=? 14:11:17,584 INFO TestPersistSaveMerge:240 - Commit transaction 14:11:17,655 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)
Which one to choose?
You could expect that the save and persist method behave differently because there are a few differences between the JPA specification and the Javadoc of Hibernate’s proprietary methods.
But almost all of these differences disappear when you take a look at the internal implementation. The only ones that remain are 2 corner cases in which Hibernate might delay the retrieval of the primary key, the return type of the method and the support by other JPA implementations.
For most applications, it doesn’t make any difference if you get the generated primary key value as the return type of Hibernate’s save method or from the getter method of your primary key attribute. As long as you don’t use an extended persistence context and perform all database operations with an active transaction, I recommend using JPA’s persist method.
Updating a detached entity
When you close the current persistence context or explicitly remove an entity from it by calling the clear or detach methods on the EntityManager interface, the entity becomes detached. That means that it is no longer stored in the 1st level cache and that Hibernate will not replicate any of the applied changes to the database.
You can use Hibernate’s update or JPA’s merge method to associate a detached entity with a persistence context. After you’ve done that, Hibernate will update the database based on the entity attribute values.
The effect of the update and merge method seem to be the same, but as you will see in the following sections, there is an important difference.
JPA’s merge method
JPA’s merge method copies the state of a detached entity to a managed instance of the same entity. Hibernate, therefore, executes an SQL SELECT statement to retrieve a managed entity from the database. If the persistence context already contained a managed instance of the entity, Hibernate uses the existing one instead. It then copies all attribute values to the managed entity and returns it to the caller.
Author managedAuthor = em.merge(a);
After activating the logging of SQL statements, you can see the executed SELECT and UPDATE statements in the log output.
11:37:21,172 DEBUG SQL:92 - select books0_.bookId as bookId1_2_0_, books0_.authorId as authorId2_2_0_, book1_.id as id1_1_1_, book1_.fk_author as fk_autho6_1_1_, book1_.format as format2_1_1_, book1_.publishingDate as publishi3_1_1_, book1_.title as title4_1_1_, book1_.version as version5_1_1_, author2_.id as id1_0_2_, author2_.firstName as firstNam2_0_2_, author2_.lastName as lastName3_0_2_, author2_.version as version4_0_2_ from BookAuthor books0_ inner join Book book1_ on books0_.authorId=book1_.id left outer join Author author2_ on book1_.fk_author=author2_.id where books0_.bookId=? 11:37:21,180 INFO TestPersistSaveMerge:82 - Before commit 11:37:21,182 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?
When Hibernate flushes the persistence context for the next time, its dirty checking mechanism checks all managed entities. If it detects that the merge operation changed any entity attribute value, it triggers the required SQL UPDATE statement.
There is one important detail you need to know when you use JPA’s merge method. Hibernate copies the attribute values of the detached entity to the managed entity. This overwrites any changes that you performed on this entity within the current Session.
Hibernate’s update method
Hibernate’s update method doesn’t trigger an SQL SELECT statement. It just attaches the entity to the current persistence context. In contrast to JPA’s merge method, you can’t lose any changes by calling the update method. If the persistence context already contains a managed instance of the entity you want to update, it throws an exception.
em.unwrap(Session.class).update(a);
When Hibernate performs the next flush, it doesn’t perform any dirty checks. That’s not possible because Hibernate didn’t read the latest version of the entity from the database. It just executes an SQL UPDATE statement for the reattached entity.
11:38:28,151 INFO TestPersistSaveMerge:121 - Before commit 11:38:28,153 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?
The missing dirty check causes an unnecessary SQL UPDATE statement when the entity and the corresponding database record contain the same values. This might be a problem if your DBA registered an update trigger for the database table. In these situations, you can annotate your entity with @SelectBeforeUpdate.
@Entity @SelectBeforeUpdate public class Author { ... }
That tells Hibernate to select the entity and perform a dirty check before it generates the SQL UPDATE statement. As you can see in the log output, the behavior of the update method is now similar to JPA’s merge method.
19:08:16,530 INFO TestPersistSaveMerge:121 - Before commit 19:08:16,531 DEBUG SQL:92 - select author_.id, author_.firstName as firstNam2_0_, author_.lastName as lastName3_0_, author_.version as version4_0_ from Author author_ where author_.id=? 19:08:16,592 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?
But there is a significant difference between the 2 methods. When you call the update method, Hibernate will only select the entity which you provided as a method parameter. But when you call JPA’s merge method, Hibernate will also select all associations with CascadeType.MERGE. You should, therefore, prefer JPA’s merge method if you reattach a huge graph of entities.
Which one to choose?
There is no general answer to this questions. As you have seen, both methods have their advantages and disadvantages. You have to decide for your specific use case if Hibernate needs to select the entity before it triggers the SQL UPDATE statement. And if that’s the case, you also need to consider the depth of your entity graph and the performance implications of the provided fetching behavior.
Updating a managed entity
JPA and Hibernate make it very easy to update a managed entity. If your entity is in the lifecycle state managed, e.g. because you fetched it with a JPQL query or the find method of the EntityManager, you just need to change the values of your entity attributes.
em = emf.createEntityManager(); em.getTransaction().begin(); a = em.find(Author.class, a.getId()); a.setFirstName("Thorben"); log.info("Before commit"); em.getTransaction().commit(); em.close();
When Hibernate decides to flush the persistence context, the dirty checking mechanism will detect the change and perform the required SQL UPDATE statement.
11:41:49,178 DEBUG SQL:92 - select author0_.id as id1_0_0_, author0_.firstName as firstNam2_0_0_, author0_.lastName as lastName3_0_0_, author0_.version as version4_0_0_ from Author author0_ where author0_.id=? 11:41:49,191 INFO TestPersistSaveMerge:335 - Before commit 11:41:49,193 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?
You don’t need to, and you should not call Hibernate’s save method after you updated an entity. That triggers an additional SaveOrUpdate event without providing any benefits. When Hibernate decides to flush the persistence context, it will perform the dirty check anyway to detect all changes before it executes the required SQL UPDATE statements.