SaveOrUpdate versus Merge in Hibernate

We all have those problems that we encounter just infrequently enough that when we see them again, we know we’ve solved this, but can’t remember how.

The NonUniqueObjectException thrown when using Session.saveOrUpdate() in Hibernate is one of mine. I’ll be adding new functionality to a complex application. All my unit tests work fine. Then in testing the UI, trying to save an object, I start getting an exception with the message “a different object with the same identifier value was already associated with the session.” Here’s some example code from Java Persistence with Hibernate.

Session session = sessionFactory1.openSession();
Transaction tx = session.beginTransaction();
Item item = (Item) session.get(Item.class, new Long(1234));
tx.commit();
session.close(); // end of first session, item is detached
 
item.getId(); // The database identity is "1234"
item.setDescription("my new description");
Session session2 = sessionFactory.openSession();
Transaction tx2 = session2.beginTransaction();
Item item2 = (Item) session2.get(Item.class, new Long(1234));
session2.update(item); // Throws NonUniqueObjectException
tx2.commit();
session2.close();

To understand the cause of this exception, it’s important to understand detached objects and what happens when you call saveOrUpdate() (or just update()) on a detached object.

When we close an individual Hibernate Session, the persistent objects we are working with are detached. This means the data is still in the application’s memory, but Hibernate is no longer responsible for tracking changes to the objects.

If we then modify our detached object and want to update it, we have to reattach the object. During that reattachment process, Hibernate will check to see if there are any other copies of the same object. If it finds any, it has to tell us it doesn’t know what the “real” copy is any more. Perhaps other changes were made to those other copies that we expect to be saved, but Hibernate doesn’t know about them, because it wasn’t managing them at the time.

Rather than save possibly bad data, Hibernate tells us about the problem via the NonUniqueObjectException.

So what are we to do? In Hibernate 3, we have merge() (in Hibernate 2, use saveOrUpdateCopy()). This method will force Hibernate to copy any changes from other detached instances onto the instance you want to save, and thus merges all the changes in memory before the save.

Session session = sessionFactory1.openSession();
Transaction tx = session.beginTransaction();
Item item = (Item) session.get(Item.class, new Long(1234));
tx.commit();
session.close(); // end of first session, item is detached
 
item.getId(); // The database identity is "1234"
item.setDescription("my new description");
Session session2 = sessionFactory.openSession();
Transaction tx2 = session2.beginTransaction();
Item item2 = (Item) session2.get(Item.class, new Long(1234));
Item item3 = session2.merge(item); // Success!
tx2.commit();
session2.close();

It’s important to note that merge returns a reference to the newly updated version of the instance. It isn’t reattaching item to the Session. If you test for instance equality (item == item3), you’ll find it returns false in this case. You will probably want to work with item3 from this point forward.

It’s also important to note that the Java Persistence API (JPA) doesn’t have a concept of detached and reattached objects, and uses EntityManager.persist() and EntityManager.merge().

I’ve found in general that when using Hibernate, saveOrUpdate() is usually sufficient for my needs. I usually only need to use merge when I have objects that can have references to objects of the same type. Most recently, the cause of the exception was in the code validating that the reference wasn’t recursive. I was loading the same object into my session as part of the validation, causing the error.

Where have you encountered this problem? Did merge work for you or did you need another solution? Do you prefer to always use merge, or prefer to use it only as needed for specific cases?

65 thoughts on “SaveOrUpdate versus Merge in Hibernate

  1. Swapnil

    Hey Stevi,
    Nice article, It explains me complete usage of merge and saveOrUpdate methods.

  2. Mallesh

    Read many books to understand the merge and saveorupdate, but this explanation covered complete idea about that. Thanks a lot.

  3. Pingback: JPA implementation patterns: Saving (detached) entities « Stuck with IT

  4. Jeff

    Thanks for sharing. I’ve seen several examples of merge() used online, and very few actually use the results. I thought it merged that object into the session, which was definitely not the case.

    Thanks again,
    Jeff

  5. Thai Tran

    Thank you very much. This is exactly I have 10 minutes ago. We used a thread to update and clean the database and sometimes, we got this annoying exception

  6. Pingback: Difference between Merge and Update in Hibernate! | Engineering Experiments

  7. Pingback: GORM : deleting a child in a one-to-many relationship | GrailsLog

  8. Pingback: Blog When to use merge() before save()?a - Blog

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>