Let us take this scenario.
Say you have this table called words which has two columns, id and word, as below.
CREATE TABLE `words` ( `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, `word` VARCHAR(45) NOT NULL, PRIMARY KEY (`id`) )
You have an admin interface to enter new words and edit existing words. This admin interface is built using a Java web stack which uses Hibernate as the persistence frame work.
You have a page where you list all the words entered and also there is a link to enter new words. If you click on an existing word, it takes you to a page with a form field pre populated with the word for you to edit. If you click on add a new word link, it takes you to a page with an empty form field for you to add a new word. Adding a new word or editing an existing word uses the same action method. In the action method we do a check to see whether id value is one of the http form parameters. If it is present, we assume it is an existing word and the user is trying to edit it. If not we assume it is a new word to be persisted. We create a data transfer object and populate it with the form parameters and send it to the persistence layer.
Now, in the persistence layer we copy the values from the data transfer object to the business object and persist it using Hibernate. If we are editing an existing word, we have two choices while creating the business object:
1. We can create a new object, copy all the fields from the data transfer object along with the id field so that Hibernate knows this is an update(since the id field of the business object would be populated).
2. We can ask hibernate to provide us the already existing business object, copy all the fields from the data transfer object to the hibernate business object (In this scenario it does not matter whether we copy the id or not).
Once the business object has been updated with values we persist it.
Which option will you choose to create the business object?
It is better to choose option 2. Why?
Ok, now our requirement changes and we have to make sure that we save only unique words into db i.e none of the words should be repeated. So what do we do? We add a validation step before the action method is invoked and see whether the entered word is already present in db or not. For this we query hibernate and request all the word business objects from the db. We iterate through each of them and see whether the newly entered word is already present or not. If present, we inform the end user that this word is already present and you need to enter a new word. If not, we pass it on to the next step i.e the action method and the same flow ensues.
If you had chooses option 1 and you are trying to edit an existing word and persisting it, you will now be treated to
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session
Why does hibernate throw this error?
While validating, we request hibernate for all the word business objects from the db. Due to this hibernate session already has a copy of all the word business objects. While persisting, we create a new business object with an id(which already exists in the db) and try to persist it. But hibernate sees that in it’s session a business object corresponding to that id is already present(the one that we had requested for validation) and raises the above exception.
If we had chosen option 2 for persisting, we would ask hibernate for the business object corresponding to that id, and hibernate would have provided us with that object from it’s session since it already has it. Hence while persisting you would not have witnessed this exception.
The above scenario was long winded and cooked up just to simplify the situation in which hibernate throws NonUniqueObjectException.