Each entity instance has three states:

  • Transient: has no corresponding rows in the database.
  • Persistent: associated with a unique Session object. Upon flushing the Session to the database, this entity is guaranteed to have a corresponding consistent record in the database.
  • Detached: an instance enters this state if we evict it from the context, clear or close the Session, or put the instance through serialization/deserialization process.

It’s important to understand from the beginning that all of the methods (persist(), save(), update(), merge(), saveOrUpdate()) don’t immediately result in the corresponding SQL UPDATE or INSERT statements. The actual saving of data to the database occurs upon committing the transaction or flushing the Session.

Take this entity as an example:

@Entity
public class Person {
 
    @Id
    @GeneratedValue
    private Long id;
 
    private String name;
 
}

Persist

The persist() method is intended to add a new entity instance to the persistence context, i.e. transitioning an instance from a transient to persistent state.

Person person = new Person();
person.setName("John");
session.persist(person);

The person object has transitioned from a transient to persistent state. The object is in the persistence context now, but not yet saved to the database. The generation of INSERT statements will occur only upon committing the transaction, or flushing or closing the session.

We may call this method on an already persistent instance, and nothing happens. But if we try to persist a detached instance, the implementation will throw an exception:

Person person = new Person();
person.setName("John");
session.persist(person);
 
session.evict(person);
 
session.persist(person); // PersistenceException!

Merge

The main intention of the merge() method is to update a persistent entity instance with new field values from a detached entity instance. This is what it does:

  • Finds an entity instance by ID taken from the passed object (either an existing entity instance from the persistence context is retrieved, or a new instance loaded from the database).
  • Copies fields from the passed object to this instance.
  • Returns a newly updated instance.
Person person = new Person();
person.setName("John");
session.save(person);
 
session.evict(person);
person.setName("Mary");
 
Person mergedPerson = (Person) session.merge(person);

Note that the merge() method returns an object. It’s the mergedPerson object we loaded into the persistence context and updated, not the person object that we passed as an argument. They’re two different objects, and we usually need to discard the person object.

As with the persist() method, the merge() method is specified by JSR-220 to have certain semantics that we can rely upon:

  • If the entity is detached, it copies upon an existing persistent entity.
  • If the entity is transient, it copies upon a newly created persistent entity.
  • This operation cascades for all relations with cascade=MERGE or cascade=ALL mapping.
  • If the entity is persistent, then this method call doesn’t have an effect on it (but the cascading still takes place).

Save

The save() method is an “original” Hibernate method that doesn’t conform to the JPA specification.

The effect of saving an already persisted instance is the same as with persist(). The difference comes when we try to save a detached instance:

Person person = new Person();
person.setName("John");
Long id1 = (Long) session.save(person);
 
session.evict(person);
Long id2 = (Long) session.save(person);

The id2 variable will differ from id1. The save call on a detached instance creates a new persistent instance and assigns it a new identifier, which results in a duplicate record in the database upon committing or flushing.

Update

As with save(), the update() method is an “original” Hibernate method. Its semantics differ in several key points:

  • It acts upon a passed object (its return type is void). The update() method transitions the passed object from a detached to persistent state.
  • This method throws an exception if we pass it a transient entity.
Person person = new Person();
person.setName("John");
session.update(person); // PersistenceException!

SaveOrUpdate

The main difference of the saveOrUpdate() method is that it doesn’t throw an exception when applied to a transient instance, instead it makes this transient instance persistent. The following code will persist a newly created instance of Person:

Person person = new Person();
person.setName("John");
session.saveOrUpdate(person);

We can think of this method as a universal tool for making an object persistent regardless of its state, whether it’s transient or detached.

Starting with hibernate 6 the following methods was marked as deprecated and is not encouraged to use:

  • save
  • update
  • saveOrUpdate