nested exception is org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session
If you see the above message, you're probably calling The root of the problem is usually somewhere before whatever line (of your code) the stacktrace reports, so you'll have to find and fix it.
In this post I'll talk about a little bit how hibernate deals with objects, what can make this problem appear and how you can list what hibernate currently has in it's session.
The problem and how to spot it
Often the problem is solved with a
So what is merge() and why is it "solving" the problem?
First of all, I'd like to say that if you didn't have a Like I said before, you're seeing this
- Fetched an object, changed it, saved it, changed it again and saved it a second time. (When a hibernate session is closed, the objects are detached from it but they remain in the application's memory, meaning you can still change them).
- Created a new object
newX that is equal to one that exists in the database (oldX ), fetcholdX , then save thenewX .
Check if you have any of the scenarios above (double fetching, double edit+save or something else which can create two versions of the same persistent object).
How does merge() work?
You can use it to attach an object to the current hibernate session without any consideration for what is already there. If you have two instances of Object A with the same identifier*, then (a copy of) the object that you pass in to * meaning they are "the same" as far your persistent layer (aka database) is concerned
Above is a very rough picture of what happens when you call
In practical terms, this means that
How is it different from saveOrUpdate() ?
Before you say "So saveOrUpdate() does the following:
- if the object is already persistent in this session, do nothing
- if another object associated with the session has the same identifier, throw an exception
- if the object has no identifier property, save() it
- if the object's identifier has the value assigned to a newly instantiated object, save() it
- if the object is versioned by a
or , and the version property value is the same value assigned to a newly instantiated object, save() it - otherwise update() the object
and merge() is very different:
- if there is a persistent instance with the same identifier currently associated with the session, copy the state of the given object onto the persistent instance
- if there is no persistent instance currently associated with the session, try to load it from the database, or create a new persistent instance
- the persistent instance is returned
- the given instance does not become associated with the session, it remains detached
How do I know if the object in the session is different?
Regardless of which scenario you're experiencing, you can compare what you have currently in the session and what you have in memory.It's somewhat easy to list all the objects inside a hibernate session and the code snippet below can be helpful when you're debugging your application.
static class HibernateSessionUtils {
public static Map listAllObjects(Session session){
SessionImplementor sessionImplementor = (SessionImplementor) session;
StatefulPersistenceContext context = (StatefulPersistenceContext) sessionImplementor.getPersistenceContext();
return context.getEntitiesByKey();
}
}
then just use it like so
Map hibernateObjectsMap = HibernateSessionUtils.listAllObjects(session)
Hopefully by comparing the two objects (the one in the session and the one you have), you'll find out what the problem is.
How do I fix it?
Finally, the part that matters.After knowing what you're experiencing, you should know how to avoid it (and if you can avoid it). So first you should try to prevent double-fetching or double-(edit+save)
If you cannot avoid the scenarios above, then it could be safe to use
Now that you know a little bit more about this subject and you still think
Object attachedObject = session.merge(detachedObject);
Happy coding!
No comments:
Post a Comment