|

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.