NonUniqueObjectException is thrown when there is an object already associated with the session with the same id (primary key) (i.e. a duplicate). It is important to understand the concept of a “detached” object in Hibernate. This is one of the most common errors, understanding why and when this error is thrown will save you time in identifying and fixing this issue.
Q. What is a detached object in Hibernate?
When you close an individual Hibernate Session, the persistent objects you are working with become detached. This means the objects are still in the application’s memory, but Hibernate is no longer responsible for tracking changes to those detached objects. If you then modify a detached object and want to update it, you have to reattach that object. During that reattachment process, Hibernate will verify if there are any other copies of the same object. If it finds any already in the session, it gets confused as to which one is the correct object, hence notifies you with a “NonUniqueObjectException” as opposed to saving any bad data.
“LazyInitializationException” is another common error due to a detached object. This error occurs when you try to access properties or associated object of Detached object then it results in Lazy Initialization Exception. This means Hibernate “could not initialize the proxy” when there is no Session”.
Scenario 1: whilst update(..) or saveOrUpdate(..)
Step 1: Load an entity say a “Book” from the database into Hibernate session.
session.get(Book.class, new Integr(123));
Step 2: Convert the entity to a DTO, say “BookDto”, and pass it to populate the view
Step 3: Modify a value in the “BookDto”
Step 4: Convert back the “BookDto” to Hibernate entity “Book”.
Step 5: Try to update the new entity “Book”, and you will get “NonUniqueObjectException”
1) Call merge() instead. Merge() takes the instance passed and merges it with any object of the “same id” associated with the session. Note, if you plan on using the object again you must use the object returned by the call to merge(). The instance object passed to merge will not be updated.
Object mergedObj = session.merge(book);
2) If it is possible, don’t convert the entity “Book” to DTO “BookDto”, and “setSomething” on the entity itself. You don’t have to update the entity “Book” as it is already being maintained by Hibernate. Later flush() will push any changes recorded in the session to the database.
Scenario 2: whilst delete(…)
Even if you try to delete the entity “Book” from the session, you will get the “NonUniqueObjectException”. This is because Hibernate uses the object “identity equals” where you have two differnt physical “Book” objects in the JVM Heap, but logically both are the same objects with the same identifier, say “id = 123”, which corresponds to the primary key in the database table.
Step 1: Load a “Book” from the database, and assign it to the reference “bookXyz”;
Book bookXyz = session.get(Book.class, new Integr(123));
Step 2: Instantiated the same “Book” object again later, and assign it to the reference “bookXyzAgain”.
Book bookXyzAgain = new Book(....);
Step 3: You try to delete “bookXyzAgain” from the session, and you will get “NonUniqueObjectException”.
The exception is thrown because you are trying to delete an object which has the same primary key as “bookXyz”, and it already exists in the hibernate session.
Scenario 3: whilst cascade saves
When there is a cascade save between two object instances say “a” of type “Book” and “b” of type “Author”, but the object instance of “Author” has already been associated with the session but not on the same instance of b, but instance of “c”, then you can get this exception.
A circular save request can throw this exception as well. For example, if you have set “cascade=all” on both ends of a one-to-many relationship resulting in a circular save request.
General tips to fix these types of Hibernate exceptions
These errors can be sometimes hard to debug,
1. You need to break down your code, and then comment out some parts until the error goes away and then put the code back until the error comes back again. Then you know what is causing it.
2. Check if you had forgotten to put @GenerateValue for @Id column in your hibernate entity.