SaveOrUpdate versus Merge in Hibernate

December 7th, 2008 by stevi | Filed under hibernate, Java

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?

No related posts.

Related posts brought to you by Yet Another Related Posts Plugin.

tag_iconTags: | | | |

You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

51 Responses to “SaveOrUpdate versus Merge in Hibernate”.

  1. Hi Stevi,

    It is really a nice post on where to use the merge method. I don’t see the much usage of this API. anyway good write up.

  2. michelle :

    It is really good and concise information on this topic. Thanks!

  3. Alcid :

    Thanks for sharing this detail about merge. It was so helpful!
    I rather to use merge only on specific cases.

  4. Harsha Shettigar :

    Very good explanation on the topic. Thanks!

  5. Deepthi :

    Good article, Appriciate effort.

  6. rob :

    Great write up…easier to understand those cryptic docs.

  7. [...] Looking at that table one may begin to understand why the saveOrUpdate method never became a part of the JPA specification and why the JSR members instead choose to go with the merge method. BTW, you can find a different angle on the saveOrUpdate vs. merge problem in Stevi Deter’s blog about the subject. [...]

  8. Prejith George :

    Excellent explanation. Really appreciate your effort

  9. Raffy :

    Hi Stevi,

    Excellent article!!

    I encounter these NonUniqueObjectExceptions a lot and i’m just wondering if it would be safe to use merge() as a complete replacement for saveOrUpdate() ?

    I have a method in my base class which is mainly responsible for calling saveOrUpdate and I was thinking if it would be “ok” to call saveOrUpdate first, and if it throws an exception (NonUniqueObjectException), I would use merge() instead. Is this a safe approach?

    I admit that I am still new to Hibernate and I am continuing to learn the best practices and approach to these types of problems, any help or input would be greatly appreciated.

  10. Raffy,
    It would be, imho, a reasonable alternative to replace the use of saveOrUpdate() with merge().

    The best practice in that case would probably be to replace your base class’s method with a “persistOrMerge()” which would check the object, if it’s new (e.g., identifier is null), call persist, otherwise call merge.

    Now, Hibernate’s call to merge will combine the two, e.g., it’ll call persist if it recognizes it’s a new object. I’m not at this point convinced there’s a performance overhead in allowing merge to identify your new objects and handle accordingly; in some cases, I think there’s a logical clarity that comes from making sure you understand there’s a difference.

    I would caution against calling saveOrUpdate and then handling the exception with merge.

    We continue to use merge in the exceptional cases in some of our existing projects mostly as a way to clarify for ourselves when this specific case is happening and deciding if there’s a call for refactoring to avoid the issue altogether. Some times the NoUniqueExceptionError has made us aware that we’re hydrating far more objects than we need to and we’ve seen performance improvements from making the changes necessary before we ever call saveOrUpdate(). But sometimes, you just need to have those two versions of the object hydrated and detached, and I find it useful to know why Hibernate doesn’t like that.

  11. Luther Baker :

    Have you dealt with DELETEing an object … which, let’s assume correctly fails with a GenericJDBCException (ie: the object holds references to some other classes and can’t be deleted directly without first removing the other classes).

    To which then, subsequent requests to delete the same object result in NonUniqueObjectException. After the first delete … I’m wondering if I must somehow clear the instance out of the Hibernate memory etc., if I am going to let the user try to delete it again.

  12. Luther,
    I haven’t seen that specific scenario myself, but it does sound like you’re facing a similar problem. The second attempt to delete might be loading a second copy of the object instead of attempting to operate on the original one.

  13. Fernando :

    I have a blazeds (adobe service) who serves objects to a flash widget. I detach any object when the widget asked for some object to the service and I reattach it using merge (for updating).

    The difference is that when the object marshals to the frontend, not all the attributes are marshalled. This is because the front end no use all of them and I dont see any need to send to the fe.
    I would like to know what hibernate assumes with this attributes. Because it could seem that many of them are updated, but they haven’t been marshall and hibernate could understand that they are.

    I hope to be clear enough

    thanks in advance.

  14. Nirmal Kumar :

    Hi,

    Very good explanation of the merge method.

    Here is one more example:
    http://www.hibernate-training-guide.com/merge.html

    Nirmal :)

  15. Nice post. Thanks for writing this.

  16. Munish :

    I am using Table per Subclass hirerachy and while updating the subclass object, i am getting the NonUniqueObjectException.Since, we are using 2.1.6 version of Hibernate, cannot use merge(), but i tried with SaveOrUpdateCopy as mentioned above. But still its giving me the same exception. This is very urgently required. Any suggestions please.

    Thanks,
    Munish

  17. raja :

    Nice article. But I have similar code block and I am not getting “NonUniqueObjectException” in this case. Given below is the code

    Session session1 = sf.openSession();
    Transaction t1 = session1.beginTransaction();
    College college = getCollegeById(collegeId);
    t1.commit();
    session1.close();
    System.out.println(” college identity ” + college.getCollegeId());
    college.setCollegeName(“MMC-Chennai8888″);

    Session session2 = sf.openSession();
    Transaction transaction2 = session2.beginTransaction();
    College college1 = getCollegeById(collegeId);
    System.out.println(” college1 identity ” + college1.getCollegeId());
    session2.update(college);
    transaction2.commit();
    session2.close();

  18. First thanks for this post. I really needed the phrase
    ” If you test for instance equality (item == item3), you’ll find it returns false in this case.”
    Things now are much more clear.

    Thank you.

  19. raja :

    Hi Raffy
    Can you please advise my case which i expalined few days back. Since I am not getting that exception and wondering whts the use of merge(). Your thoughts will be appreciated

  20. nyuby :

    Hello, it’s good tutorial, but i have a little confuse, if i using merge and using session with getCurrentTransaction, why i must be commit transaction manually ? I see if i have getCurrentTransaction the transaction will be closed and commit automatically, in this case, why did’nt work … could you give me some explanation please ?

  21. Siddiq :

    Hi ,

    I am not able to update the database when using merge. and when use update hibernate throws non-Unique object exception.
    I need to update the Case table , in which I need to get the case by a case id , and set the new values and then update the object to the database, while updating if I use merge, updated data is not reflecting in the DB and when using update or save , hibernate throws non-unique object exception.

    Any idea why merge is not updating the database. ?

  22. Surya :

    Excellent ..

  23. Sujatha :

    Hi really very helpful….Thanks for ur article

  24. Ramanathan :

    Excellent write up. Got this page exactly at the time I needed.
    Most of the books provide the (boring) theory of the APIs but not the practical usage. Your chronicle gave a clean insight on where to use it with a simple example.

    Thanks much.

    ps: Do post more of such articles.

  25. yah its good for the developers they got any exception these method is very help full

  26. Suresh :

    Thanks for the great tutorial.

    I want to comment on merge(), it works fine on updating the object as long as object does not have BLOB data fields.

    I have a table with BLOB columns and other data type columns.

    1st Scenario:
    if I use merge() method all other columns will update successfully with modified data except BLOB columns.

    2nd Scenario:
    if I use update() method, it updates the BLOB columns as well, n my UI I don’t show all the columns from table. In such case, it updates with null values.

    Any ideas or suggestions to fix this issue?

    Very much appreciated…

  27. Harish :

    Its a very good tutorial. It helped me a lot in understanding the difference between merge and update method.

  28. SJJ :

    Thanks for the useful information

  29. Jay :

    Woudlnt item2 and item3 point to the same object ??

    item2 == item3 ?

  30. rAHU :

    Fantabulous.. awesome. Thanks much !!!

  31. MTM :

    Thanks for sharing, this is a crystal clear elaboration!

  32. [...] Looking at that table one may begin to understand why the saveOrUpdate method never became a part of the JPA specification and why the JSR members instead choose to go with the merge method. BTW, you can find a different angle on the saveOrUpdate vs. merge problem in Stevi Deter's blog about the subject. [...]

  33. dp :

    very good explanation , excellent must read

  34. PNC :

    Excellent Explanation…

  35. PavanKumarGupta P :

    Your narration is very easy to understand. It helps in understanding the merge() concept and at the same time, the detached objects too.

    Thanks. :)

  36. metB :

    I gues “If we then modify our persistent object [...]” should be: “If we then modify our detached object”

  37. metB,
    Excellent suggestion. I’ve made the change as I think it adds clarity.

  38. mithunc :

    Thanks! This makes the differences between merge and save much clearer.

  39. Indrojeet :

    This was really awesome, Thank you very much Stevi :)

  40. deepak :

    very very nice post. Your example clears the merge concept of hibernate. Good example.

    Thanks! :)

  41. sarthak :

    cannot thank you enough for this.. great work..

  42. Shay :

    Stevi,

    I have used your article to good effect at least once before and I’m glad to see that when I search for “hibernate merge versus save or update”, it still comes up first. I’m also happy to see that this advice is still helping people almost three years after you originally shared it with us. In return, I’d like to share with you yet another reason for Hibernate throwing NonUniqueObjectException: loss of numeric precision.

    Every morning, I download data from a feed and insert it into tables in a Sybase database using org.hibernate.Session.save(Object) [the table is truncated before the download begins]. One entity I insert contains many BigDecimal elements that are mapped to numeric(n, m) in its corresponding table. This has worked fine for over 18 months, until today, when a NonUniqueObjectException was thrown. I scratched my head for quite some time, knowing that the feed handler had not loaded this instance in memory until the point where the exception was being thrown. After a feeble attempt at debugging the feed handler, I decided to try inserting the offending instance directly into the table to see if that worked, and sure enough, it did, but with one small exception – the precision of one of the numeric columns was too small to hold the value I was giving it and Sybase silently truncated one decimal place at insertion. Modifying the entity to increase its precision and scale by several digits fixed my problem.

    I am anything but a Hibernate maven (indomitable or spirited novice would be a more appropriate description) but it occurred to me that perhaps Hibernate is throwing a NonUniqueObjectException in this case because it is comparing the original object with the object returned by the database and finding that they are not the same. Would you agree?

    Thanks again for this article,
    Shay

  43. Dennis Moore :

    Thanks! Your code example is wonderful, very useful.

  44. [...] Looking at that table one may begin to understand why the saveOrUpdate method never became a part of the JPA specification and why the JSR members instead choose to go with the merge method. BTW, you can find a different angle on the saveOrUpdate vs. merge problem in Stevi Deter’s blog about the subject. [...]

  45. That’s really helped me. Thanks.

  46. Good explaination bro..

  47. Willie :

    At the end you ask where we’ve encountered the error. Exactly same place you mention–I have a graph and I’m validating that there aren’t any graph cycles.

  48. thanh pham :

    Nice topic.

    Thanks :)

  49. Varun :

    Thank you! :-)

  50. rishi :

    Nice post!

    I have a question: Currently my app does not use Spring. It is using only Hibernate. I am facing this issue at multiple places and I am trying to make my middle layer and UI layer guys not to populate multiple objects with the same id in the first place. But to very little success.

    Now we are thinking of bringing Spring layer over the hibernate (for many reasons). As spring handles the sessions by itself, do you think these exceptions will vanish and there no longer be attached and detached objects?

  51. Sambath P :

    Thanks for the clear explanation of when to use merge.

Leave a comment.

To leave a comment, please fill in the fields below.