Say I have a screen showing animals on my farm. If the user wants to add animals, they can click the "add" button, which will lead you to another screen that lists the names of animals. If the user selects βdogβ, my application will query the database and return the βDogβ object to me, and I will add it to my collection of animals in my farm.
In the above situation, if I were to save the Farm object, the farm would be successfully saved as expected.
However, if the user adds the βdogβ to the farm, and then decides to add the βdogβ again, saving the Farm object results in:
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session
Now this makes sense because the user has selected the βdogβ twice, causing my application to dig up two instances of the βdogβ. What is the best way to handle this situation?
Edit: Let me clarify that animals are not directly attached to the Farm. The farm will contain an AnimalShelters collection in which anyone can place an animal. AnimalShelters are unique and have their own identifiers. I suppose you could think of a βdogβ as a privileged dog that could boast between several shelters.
Edit: This is how the work works:
- The first screen shows the farm. No AnimalShelters.
- User clicks the add button to add AnimalShelter
- ,
- , , "find" [ ]
- AnimalShelter
- ββ AnimalShelter
- "" AnimalShelter, ( 5 )
: , , :
- animal1 = session.get(Animal.class, 1L);
- Commit Transaction
- 1 AnimalShelter,
- animal2 = session.get(Animal.class, 1L); // Animal Animal 3, , . Animal, ?
- Commit Transaction
- 2 AnimalShelter, Farm // , Animal
: :
+---------------+
| Farm |
+---------------+
| Id (pk) |
| Name |
+---------------+
+---------------+
| AnimalShelter |
+---------------+
| Id (pk) |
| AnimalId |
| FarmId |
+---------------+
+---------------+
| Animal |
+---------------+
| Id (pk) |
| Name |
+---------------+
: Stacktrace:
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.spike.model.Animal
at org.hibernate.engine.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:637)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:305)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:246)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:112)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:677)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:669)
at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.java:252)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:392)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:335)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204)
at org.hibernate.engine.Cascade.cascade(Cascade.java:161)
at org.hibernate.event.def.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:451)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:288)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:144)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:117)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:677)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:669)
at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.java:252)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:392)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:335)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204)
at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:425)
at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:362)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:338)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204)
at org.hibernate.engine.Cascade.cascade(Cascade.java:161)
at org.hibernate.engine.Cascade.cascade(Cascade.java:127)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.cascadeOnUpdate(DefaultSaveOrUpdateEventListener.java:376)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:350)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:246)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:112)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:677)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:669)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:665)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:344)
at $Proxy14.saveOrUpdate(Unknown Source)
at com.spike.ui.SaveFarmActionListener.actionPerformed(SaveFarmActionListener.java:29)
: , , , :
- animal1 = session.get(Animal.class, 1L);
- 1 AnimalShelter,
- animal2 = session.get(Animal.class, 1L); // Animal,
- 2 AnimalShelter, Farm // , Animal
- Commit Transaction - , , , Animal
: , , , , , Animal. , , , - , , . AnimalShelter, Dog, , , , , .