Hello everybody,
I recently started working with Hibernate and to get some first impressions I was playing around with the "Parent/Child" example in the reference documentation.
My problem is that I always get an NonUniqueObjectException when trying to add the same Child twice to the same Parent.
I'm using assigend identifiers, a version property and a addChild() method in the Parent class (as described in the ref docs).
I specified thread-bound transaction management in the hibernate.cfg.xml configuration file.
I apologize that this might be a really stupid question, but I just cannot figure out what I'm doing wrong. I've spent days reading the docs, searching the forum and the internet and everything I tried didn't work. I really hope somebody can give me some clues as to what I'm doing wrong.
Thanks a lot,
Martin
P.S.: I tried to isolate the error and it seems it occurs because of the line "this.getChildren().add(child);" in the method addChid() of the Parent class.
Hibernate version:
3.2.1 GA
Mapping documents:
HIBERNATE.CFG.XML
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.driver_class">org.postgresql.Driver</property>
<property name="connection.url">jdbc:postgresql://localhost/PARENTCHILD</property>
<property name="connection.username">postgres</property>
<property name="connection.password">postgres</property>
<property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<property name="current_session_context_class">thread</property>
<property name="transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<property name="cache.use_query_cache">false</property>
<property name="cache.use_minimal_puts">false</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="use_sql_comments">true</property>
<mapping resource="Parent.hbm.xml"/>
<mapping resource="Child.hbm.xml"/>
</session-factory>
</hibernate-configuration>
JAVA CODE: PARENT
Code:
import java.util.*;
public class Parent {
private String id;
private Integer version;
private String name;
private Set<Child> children = new HashSet<Child>();
public Parent() {}
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public Integer getVersion() { return version; }
public void setVersion(Integer version) { this.version = version; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Set<Child> getChildren() { return children; }
public void setChildren(Set<Child> set) { this.children = set; }
public void addChild(Child child) {
child.setParent(this);
this.getChildren().add(child);
}
}
MAPPING: PARENT
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Parent" table = "PARENT">
<id name = "id" column = "P_ID">
<generator class = "assigned"></generator>
</id>
<version name="version" column="P_VERSION"/>
<property name = "name" column = "P_NAME" type = "string"></property>
<set name = "children" inverse = "true" cascade="all-delete-orphan">
<key column = "P_ID"/>
<one-to-many class = "Child"/>
</set>
</class>
</hibernate-mapping>
JAVA CODE: CHILD
Code:
import java.util.*;
public class Child {
private String id;
private Integer version;
private Parent parent;
private String name;
public Child() {}
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public Integer getVersion() { return version; }
public void setVersion(Integer version) { this.version = version; }
public Parent getParent() { return parent; }
public void setParent(Parent parent) { this.parent = parent; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
MAPPING: CHILD
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Child" table = "CHILD">
<id name = "id" column = "C_ID">
<generator class = "assigned"></generator>
</id>
<version name="version" column="C_VERSION"/>
<property name = "name" column = "C_NAME" type = "string"/>
<many-to-one name = "parent" column = "P_ID" not-null = "true"/>
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():JAVA: TEST CLASS
Code:
import org.hibernate.*;
public class ParentChildTest {
public void insertParent(Parent p) {
Session s = HibernateUtil.getSessionFactory().getCurrentSession();
s.beginTransaction();
s.save(p);
s.getTransaction().commit();
}
public void insertChild(Child c) {
Session s = HibernateUtil.getSessionFactory().getCurrentSession();
s.beginTransaction();
Parent p = (Parent)s.get(Parent.class, c.getParent().getId());
p.addChild(c);
s.getTransaction().commit();
}
public static void main(String[] args) {
ParentChildTest t = new ParentChildTest();
Parent p = new Parent();
p.setId("P_01");
p.setName("Arnold Schwarzenegger");
t.insertParent(p);
Child c = new Child();
c.setId("C_01");
c.setParent(p);
c.setName("Sylvester Stallone");
t.insertChild(c);
t.insertChild(c);
}
}
Full stack trace of any exception that occurs:Code:
Exception in thread "main" org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [Child#C_01]
at org.hibernate.engine.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:556)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:259)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:217)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
at org.hibernate.engine.CascadingAction$1.cascade(CascadingAction.java:218)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:268)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:216)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:296)
at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:242)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:219)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
at org.hibernate.engine.Cascade.cascade(Cascade.java:130)
at org.hibernate.event.def.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:131)
at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:122)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:65)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at ParentChildTest.insertChild(ParentChildTest.java:23)
at ParentChildTest.main(ParentChildTest.java:43)
Name and version of the database you are using:
PostgreSQL 8.2.1
The generated SQL (show_sql=true):
Debug level Hibernate log excerpt: