-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 posts ] 
Author Message
 Post subject: org.hibernate.NonUniqueObjectException
PostPosted: Wed May 03, 2006 6:09 pm 
Beginner
Beginner

Joined: Tue May 02, 2006 10:04 am
Posts: 29
Hi,

What is the correct way to deal with this kind of exception:

org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session

I am trying to delete a few objects. I guess I am using the same local
variable to refer to different instances, but why would it get saved to the session?

Thanks.


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 03, 2006 9:51 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
Because attached/persistent objects are associated with database rows: if you make a change to the object, the database gets updated.

However, that's not your problem. Your problem is that you have a transient object with a given id, when there's a persistent object with the same id in memory. You must use the persistent object. You can get around it by using Session.merge, but that's almost certainly not what you want. You want to be using the correct object, all the time.

Post some code that exhibits this behaviour, and I'll see if I can point out the problem. We don't need mappings, just java code. Don't forget the code tags!

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 04, 2006 6:14 pm 
Beginner
Beginner

Joined: Tue May 02, 2006 10:04 am
Posts: 29
Here are the methods from a facade class, with irrelevant stuff removed.

Code:
   public void deleteAssignment(long mapId, long relId, long assignmentId)
   throws SystemException, FinderException {
      this.assignmentDAO.deleteAssignment(mapId, relId, assignmentId);
   }


   public Assignment mergeAssignments(long mapId, long relId, List assignmentIds, String label,
                         UserContainer user)
   throws SystemException, FinderException {

      /* Create new assignments that contains all concepts from the given assignments. */
      Assignment newAsgn = this.assignmentDAO.mergeAssignments(
                        mapId, relId, assignmentIds, label, user);

      /* Delete all assignments whose concepts were merged. */
      for (Iterator it = assignmentIds.iterator(); it.hasNext(); ) {
         long assignmentId = ((Long)it.next()).longValue();
         this.deleteAssignment(mapId, relId, assignmentId);
      }
      return newAsgn;
   }


Here are the DAO object methods (that talk to Hibernate):

Code:

   public void deleteAssignment(long mapId, long relId, long assignmentId)
   throws SystemException, FinderException {
        try {
         /* Get the assignment from the DB. Make sure we don't get a stale
          * instance of Assignment from the Hibernate cache.
          */
         Assignment assignment = this.getAssignment(mapId, relId, assignmentId);
         this.hibernateTemplate.evict(assignment);
         assignment = this.getAssignment(mapId, relId, assignmentId);

         /* Delete the assignment from the DB. */
            this.hibernateTemplate.delete(assignment);
            this.hibernateTemplate.flush();
        } catch (DataRetrievalFailureException e) {
            throw new FinderException(e.getMessage(), e);
        } catch (Exception e) {
            throw new SystemException(e.getMessage(), e);
        }
   }

   public Assignment mergeAssignments(long mapId, long relId, List assignmentIds, String label,
                            UserContainer user)
   throws SystemException, FinderException {

      try {
         Assignment newAsgn = this.createAssignment(mapId, relId, label);
         for (Iterator it = assignmentIds.iterator(); it.hasNext(); ) {
            long assignmentId = ((Long)it.next()).longValue();
            Assignment asgn = this.getAssignment(mapId, relId, assignmentId);
            newAsgn.merge(asgn, user);
         }
         this.hibernateTemplate.save(newAsgn);
         this.hibernateTemplate.flush();
                  
         return newAsgn;
        } catch (DataRetrievalFailureException e) {
            throw new FinderException(e.getMessage(), e);
        } catch (Exception e) {
            throw new SystemException(e.getMessage(), e);
        }
   }



The DAO class is an member variable of the facade class.

I am calling mergeAssignments(). It works fine if I comment out the calls to this.deleteAssignments() (in the loop). If the calls is there, it fails.

Here is the exception trace, if it can help:

Code:
org.hibernate.engine.PersistenceContext.checkUniqueness(Ljava.io.Serializable;Lorg.hibernate.persister.entity.EntityPersister;Ljava.lang.Object;)V(PersistenceContext.java:586) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(Lorg.hibernate.event.SaveOrUpdateEvent;Ljava.lang.Object;Lorg.hibernate.persister.entity.EntityPersister;)V(DefaultSaveOrUpdateEventListener.java:254) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(Lorg.hibernate.event.SaveOrUpdateEvent;)V(DefaultSaveOrUpdateEventListener.java:214) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(Lorg.hibernate.event.SaveOrUpdateEvent;)Ljava.io.Serializable;(DefaultSaveOrUpdateEventListener.java:91) at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(Lorg.hibernate.event.SaveOrUpdateEvent;)Ljava.io.Serializable;(DefaultSaveOrUpdateEventListener.java:69) at org.hibernate.impl.SessionImpl.saveOrUpdate(Ljava.lang.String;Ljava.lang.Object;)V(SessionImpl.java:468) at org.hibernate.engine.Cascades$5.cascade(Lorg.hibernate.engine.SessionImplementor;Ljava.lang.Object;Ljava.lang.String;Ljava.lang.Object;Z)V(Cascades.java:154) at org.hibernate.engine.Cascades.cascadeAssociation(Lorg.hibernate.engine.SessionImplementor;Ljava.lang.Object;Lorg.hibernate.type.Type;Lorg.hibernate.engine.Cascades$CascadingAction;Lorg.hibernate.engine.Cascades$CascadeStyle;ILjava.lang.Object;Z)V(Cascades.java:771) at org.hibernate.engine.Cascades.cascade(Lorg.hibernate.engine.SessionImplementor;Ljava.lang.Object;Lorg.hibernate.type.Type;Lorg.hibernate.engine.Cascades$CascadingAction;Lorg.hibernate.engine.Cascades$CascadeStyle;ILjava.lang.Object;Z)V(Cascades.java:720) at org.hibernate.engine.Cascades.cascadeCollection(Lorg.hibernate.engine.Cascades$CascadingAction;Lorg.hibernate.engine.Cascades$CascadeStyle;Lorg.hibernate.type.CollectionType;Lorg.hibernate.type.Type;Ljava.lang.Object;ILorg.hibernate.engine.SessionImplementor;Ljava.lang.Object;Z)V(Cascades.java:895) at org.hibernate.engine.Cascades.cascadeAssociation(Lorg.hibernate.engine.SessionImplementor;Ljava.lang.Object;Lorg.hibernate.type.Type;Lorg.hibernate.engine.Cascades$CascadingAction;Lorg.hibernate.engine.Cascades$CascadeStyle;ILjava.lang.Object;Z)V(Cascades.java:792) at org.hibernate.engine.Cascades.cascade(Lorg.hibernate.engine.SessionImplementor;Ljava.lang.Object;Lorg.hibernate.type.Type;Lorg.hibernate.engine.Cascades$CascadingAction;Lorg.hibernate.engine.Cascades$CascadeStyle;ILjava.lang.Object;Z)V(Cascades.java:720) at org.hibernate.engine.Cascades.cascade


It seems that it's trying to do a save or update just before the delete. But I can't figure out what it's updating.

Thanks.


Last edited by imchi on Thu May 04, 2006 6:44 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Thu May 04, 2006 6:42 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
You haven't said where the exception is being thrown from, and I can't see anything obvious in what you've posted. The reason is that an assignment in a collection is being deleted: do assignments nest other assignments? Anyway, one of the in-collection assignments is being deleted using a detached object (i.e. an assignment object with the correct id but not currently in the session cache), but a persistent version of that assignment exists in the session cache.

There are all sorts of reasons this might be happening, but the most likely is due to the fact that your deleteAssignment methods use ids rather than actual Assignment objects to do their deleting. This brings up the potential for getting out of sync with the session cache. A workaround is to Hibernate.initialize every object before you delete it. A better solution is to only delete the already-in-memory persistent objects, and never try to load an object that might already be in memory.

FYI, you don't have to get/evict/get an object to ensure it's in sync with the DB: get/refresh does the same thing, and potentially more efficiently.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 04, 2006 6:59 pm 
Beginner
Beginner

Joined: Tue May 02, 2006 10:04 am
Posts: 29
The exception is thrown in the deleteAssignment() method in the final flush().
No, assignments cannot nest other assignments.
Do you know if there is a way to check all outstanding operations in the Hibernate session (e.g. updates, saves, deletes) with an Eclipse debugger. I want to see what it's about to do before the flush. I just need the collection to look at.
Thanks.


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 04, 2006 7:03 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
Which "final flush"? There's no indication of initial entry points, or in what order the various methods are called, or if it happens only if one method is called before another.. you know, application-level code.

Yes, you can check what actions are outstanding. The easiest thing to do is to step into Session.flush(), but you can skip a couple of steps and put a breakpoint in org.hibernate.event.def.DefaultFlushEventListener.onFlush.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 04, 2006 7:10 pm 
Beginner
Beginner

Joined: Tue May 02, 2006 10:04 am
Posts: 29
tenwit wrote:
Which "final flush"? There's no indication of initial entry points, or in what order the various methods are called, or if it happens only if one method is called before another.. you know, application-level code.


I am calling the facade method mergeAssignments(),
which then calls the DAO method mergeAssignments() followed by a loop
with multiple calls to the deleteAssignment(), which calls DAO's method
deleteAssignment(), which has a flush at the end (before return). This is
where the exception occurs.


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 04, 2006 7:23 pm 
Newbie

Joined: Wed Apr 19, 2006 12:41 am
Posts: 6
try evicting the assignment in dao.mergeAssignments().

Gautam


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 04, 2006 7:53 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
The facade mergeAssignment loads the assignments into the session cache.
Then the DAO mergeAssignment loads the assignments from the cache (unless getAssignement evicts the assignment, in which case assignments are loaded from the DB). Then DAO deleteAssignment loads, evicts, loads and deletes objects.

There are so many possibilities for out-of-sync errors that I'd have to recommend changing the way you do things. Make all the DAO methods take objects instead of IDs. Pass persistent objects around where possible. Have the facade methods get/refresh objects, and then pass those objects around. If facade methods have to call other facade methods, then those second methods should be overloaded to allow objects to be passed into them: only the "initial" method would have ids passed in.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 04, 2006 8:59 pm 
Beginner
Beginner

Joined: Tue May 02, 2006 10:04 am
Posts: 29
Now I get this when I try to delete an Assignment:

org.springframework.orm.hibernate3.HibernateObjectRetrievalFailureException: deleted object would be re-saved by cascade (remove deleted object from associations).

If I try to do another get, I get the same error as before.
I am passing all assignments as objects, not IDs.


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 04, 2006 9:11 pm 
Beginner
Beginner

Joined: Tue May 02, 2006 10:04 am
Posts: 29
I was finally able to solve the problem by calling hibernateTemplate.clear() before the delete.
Thanks everyone for help.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.