Hibernate save(), update() and saveOrUpdate()

Learn the different methods for persisting and updating the entity states in the database using Hibernate Session APIs to use save(), update() and saveOrUpdate() methods under different usecases.

Starting Hibernate 6.0, all save(), update() and saveOrUpdate() methods have been marked deprecated in favor of Jakarta persistence API provided persist() and merge() methods.

1. Entity Lifecycle States

A JPA or Hibernate entity can be in one of the following four states:

  • Transient (New)
  • Managed (Persistent)
  • Detached (Not associated with any Session)
  • Removed (Deleted)

The Hibernate Session provides state transition methods like savesaveOrUpdate and update apart from methods implemented from JPA specs, for example, persist(), merge() and remove().

2. Using Session.save()

2.1. With Transient Entity

The save() method is used make a TRANSIENT entity PERSISTENT by associating it with either an org.hibernate.Session. Before persisting the entity, it assigns a generated identifier to the ID field.

The save() method returns the generated identifier so it has to immediately execute the SQL INSERT statement (it does not matter if we are inside or outside of a transaction) because identifiers are generated by the database during the INSERT query execution only.

Note that if we call save() outside the transaction then associated entities may not be inserted immediately because save() has to return the generated identifier only for the main entity. This can cause data inconsistency if we forget to flush the changes or some application error happens.

EmployeeEntity employee = new EmployeeEntity();
employee.setEmail("demo-user@email.com");
employee.setFirstName("demo");
employee.setLastName("user");

Long id = session.save(employee);

This will execute the SQL INSERT statement.

Hibernate: insert into Employee (ID, email, firstName, lastName) values (default, ?, ?, ?)

2.2. With Persistent Entity

For PERSISTENT entities, save() works as update() method and executes UPDATE SQL queries.

Long id = session.save(employee);

emp.setLastName("userOne");
session.save(employee);

Check out that the second statement is UPDATE query.

Hibernate: insert into Employee (ID, email, firstName, lastName) values (default, ?, ?, ?)
Hibernate: update Employee set lastName=? where ID=?

2.3. With Detached Entity

A detached entity already has the identifier associated with it so the behavior of the save() method depends on the ID generation strategy when executing the INSERT query.

  • If the strategy is GenerationType.SEQUENCE then a new identifier will be generated and a duplicate record will be inserted into the database.
  • If the strategy is GenerationType.IDENTITY then we will get ConstraintViolationException for duplicate primary key violation.

3. Using update()

The update() is pretty much a simpler method.

  • It executes the SQL UPDATE query for PERSISTENT entities.
  • It throws TransientObjectException if there is no identifier associated (transient entity).
EmployeeEntity emp = new EmployeeEntity();
emp.setEmail("demo-user@mail.com");
emp.setFirstName("demo");
emp.setLastName("user");

//Transient entity
session.update(emp);

It will throw an exception.

org.hibernate.TransientObjectException: The given object has a null identifier: 
  com.howtodoinjava.basics.entity.EmployeeEntity

To correctly update an entity, make it persistent first using save() or persist() methods.

EmployeeEntity emp = new EmployeeEntity();
emp.setEmail("demo-user@mail.com");
emp.setFirstName("demo");
emp.setLastName("user");

//Transient entity
session.save(emp);

emp.setLastName("userOne");

//Persistent entity
session.update(emp);

Now notice the logs.

Hibernate: insert into Employee (email, firstName, lastName, ID) values (?, ?, ?, ?)
Hibernate: update Employee set lastName=? where ID=?

4. Using saveOrUpdate()

The saveOrUpdate(), as the name implies, works as either save() or update() on the basis of the ID field present in the entity or not. In most cases, it is the preferred method to save().

  • If ID is not present then save() is called.
  • If ID is present then update() is called.

The saveOrUpdate() can be used with PERSISTENT as well as TRANSIENT entities both. Persistent entities will get updated, and transient entities will be inserted into the database.

EmployeeEntity emp = new EmployeeEntity();
emp.setEmail("demo-user@mail.com");
emp.setFirstName("demo");
emp.setLastName("user");

//Transient entity
session.saveOrUpdate(emp);

emp.setLastName("userOne");

//Persistent entity
session.saveOrUpdate(emp);

Check out the logs.

Hibernate: insert into Employee (email, firstName, lastName, ID) values (?, ?, ?, ?)
Hibernate: update Employee set lastName=? where ID=?

5. Conclusion

  • The save() method INSERTs an object in the database. It will persist the given transient instance, first assigning a generated identifier. It returns the id of the entity created.
  • The saveOrUpdate() calls either save() or update() on the basis of identifier exists or not. e.g if identifier does not exist, save() will be called or else update() will be called.

Happy Learning !!

Was this post helpful?

Join 7000+ Awesome Developers

Get the latest updates from industry, awesome resources, blog updates and much more.

* We do not spam !!

19 thoughts on “Hibernate save(), update() and saveOrUpdate()”

  1. Dear Sir,

    While submitting the web form entitymanager.merge() is inserting data instead of updating.Why is it happening.

    Regards
    Vinay

    Reply
  2. In Clusterred environment, we have 2 nodes. I have a job which runs on clustered env. Situation is , when this cron job started, using its status whether it is PENDING or InProgress , node will pick that job and start it.
    Node picks up the job on basis of status ie. if Status is PENDING then job will start by respective node. So here to avoid , job to be picked up by both the node, before start the job we, updated the status from Pending to In Progress.

    In current code, we first update Status=IN Progress and start the process but this change doesn’t get committed to DB till whole transaction gets completed and in between that 2nd node picks this job again and starts working on it.

    This situation puts job into deadlock.

    Is there any way in Hibernate Transaction where i can commit status before complete the transaction so other node can understand that this job is already in progress with first node.?

    Reply
    • Hi Deepak, I am not aware of any such technique in hibernate. Though I have faced similar problem in one of projects, but we were using iBatis there so the solution does not apply here.

      Reply
  3.  
    
    Session session = sessionFactory.openSession();
    Transaction tx = session.beginTransaction();
       
    for ( int i=0; i<100000; i++ ) {
        Customer customer = new Customer(.....);
        session.save(customer);
        if ( i % 20 == 0 ) { //20, same as the JDBC batch size
            //flush a batch of inserts and release memory:
            session.flush();
            session.clear();
        }
    }
       
    tx.commit();
    session.close();  
    

    In this example, for every 20 records data will be flushed i.e. data will be persisted in the database, if any record thrown error while processing any record after flush what will happen? as per the transaction concept whole activity i.e. saving 1000000 records should be rollback , but we already saved first 20 records in the database. How Transaction concept will work this scenario? please explain me in understanding session.flush vs tx.commit when we use together .

    Reply
    • Well, you are right in your understanding. Ultimately, it all boils down to fact how you want to handle the error.

      I will put the code inside for-loop in a try-catch block, if any error is received I will try again to run that 20 statements, if succeeded – move ahead to next set of 20; if not then simply log them somewhere and continue processing next set of 20 records.

      Reply
    • Hi Satish,

      You can also used try catch block. As Lokesh suggest. If you want to continue you can do something in catch block( mean get the index number where it stop and start again).

      If you want to stop execution and you want those 20 record should not be in database you can rollback the transaction in catch block.

      Thanks,
      Bhargav

      Reply
  4. 2.SaveOrUpdate() calls either save() or update() on the basis of identifier exists or not. e.g if identifier does not exist, save() will be called or else update() will be called

    I am confused as ID is given when we save so it should not be vice- versa.
    i.e if identifier does not exist, update() will be called or else save() will be called ?

    Reply
  5. I didn’t understand the contents of “Suggestion For Production Code” section. In this section you are telling to pass VO object instead of calling session.save() or session.saveOrUpdate(). What is the meaning of VO object here. we generally use saveOrUpdate() method. What’s wrong in using it explicitly. What’s the benefit of using VO object???

    Reply
    • Probably in your application, VO and entities are same objects. In that case, your argument is valid.
      If both are different objects, then I “suggested” the way. How you guys do it?

      Reply
      • We use AbstractDao layer or baseDao you can say. And we write DAO classes extending this abstract dao class. So all I understand that it removes boiler plate code. And after all we define these generic methods like saveOrupdate(), delete, findById(). So that we don’t have to write these methods in every DAO class. But I don’t know what you mean here using VO object. What is this term VO. Could you please elaborate it.

        Reply
        • VO = View Objects; which you pass to your view layer. These are generally separate than DTO (or entities in this case) objects. You fetch data onto DTO, copy to VO and then return VO to controller. Controller passes VO to view later. Generally happen like this.

          Keeping DTO and VO separate helps in evolving view layer and data layer independently.

          Reply
          • Yes, I think you are telling that sometime DTO objects are exactly as same as entity class and sometime some of the fields of DTO are different from entity class. For example One DTO or VO may contain data from two entity classes and we process it in service layer I mean transfer the values to entity object so that we can save or update in database. I don’t know if I understood the same what you meant or not. But what ever you are telling sounds good. Could you please provide some link or example where I can see this scenario implemented. I mean I can practically understand the benefit of using VO object. Thanks 🙂

          • You got it right. Please note that what I call View Object, is called Domain Object [DO] as well by many others. If you search for “DO vs DTO”, you will find many good links.

            In short, most notifiable difference between them is that DO can have some view related business logic i.e. formatting field or validating it; whereas DTOs contain only setter and getter methods (well mostly).

Leave a Comment

HowToDoInJava

A blog about Java and related technologies, the best practices, algorithms, and interview questions.