While trying to remove a child from its parent's collection of children, if the collection contains proxies to the children (rather than the children themselves), the child cannot remove itself.
Code:
// Following code won't work because "this" != proxy.
parent.getChildren().remove(this);
In the code provided below, the equals and hashCode methods have not been overridden since we don't use detached objects. The Object.equals() is being used to compare instances of the Child class. If all loads are performed using session.get(...), then this works correctly since proxies won't come into play. However, we would like to use session.load(...) specifically for the lazy-load benefit.
Questions:1. What is the suggested way to handle removing a child from its parent's collection of child proxies without tricking Hibernate into not using proxies?
2. Should the CGLIB-generated proxy override the equals() method such that proxy.equals(POJO) is true?
Hibernate version: 3.2.2
Mapping documents:Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="hibernate.example.Child" table="child">
<id name="id" column="id">
<generator class="native"/>
</id>
<many-to-one name="parent" column="parent_id" class="hibernate.example.Parent" />
</class>
</hibernate-mapping>
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="hibernate.example.Parent" table="parent">
<id name="id" column="id">
<generator class="native"/>
</id>
<set name="children" cascade="save-update">
<key column="parent_id" />
<one-to-many class="hibernate.example.Child" />
</set>
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():( POJOs )
Code:
package hibernate.example;
import java.util.HashSet;
import java.util.Set;
public class Parent {
private long id;
private Set<Child> children = new HashSet<Child>();
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Set<Child> getChildren() {
return children;
}
public void setChildren(Set<Child> children) {
this.children = children;
}
public void addChild(Child newChild) {
newChild.setParent(this);
this.children.add(newChild);
}
}
Code:
package hibernate.example;
public class Child {
private long id;
private Parent parent;
public Parent getParent() {
return parent;
}
public void setParent(Parent newParent) {
if (this.parent != null && !(this.parent.equals(newParent))) {
// bug: if the collection of children has a reference to the proxy,
// this call will not succeed (since this != proxy)
this.parent.getChildren().remove(this);
}
this.parent = newParent;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
( JUnit test )
Code:
@Test
public void removeChild() throws Exception {
long childId = persistChild();
session.beginTransaction();
Child childProxy = (Child) session.load(Child.class, childId);
Parent oldParent = childProxy.getParent();
Parent newParent = new Parent();
assertTrue(oldParent.getChildren().contains(childProxy));
childProxy.setParent(newParent);
assertFalse(oldParent.getChildren().contains(childProxy));
session.save(newParent);
session.getTransaction().commit();
}
private long persistChild() {
session.beginTransaction();
Child child = new Child();
Parent parent = new Parent();
parent.addChild(child);
session.save(parent);
session.getTransaction().commit();
// reopen the session to load new objects/proxies into the session
session.close();
session = HibernateUtil.getSessionFactory().openSession();
return child.getId();
}
Full stack trace of any exception that occurs:No exception occurs; test fails.
Name and version of the database you are using:MySQL 5.0.x
The generated SQL (show_sql=true):Quote:
10:05:28,686 DEBUG SQL:393 - insert into parent values ( )
Hibernate: insert into parent values ( )
10:05:28,733 DEBUG SQL:393 - insert into child (parent_id) values (?)
Hibernate: insert into child (parent_id) values (?)
10:05:28,749 DEBUG SQL:393 - update child set parent_id=? where id=?
Hibernate: update child set parent_id=? where id=?
10:05:28,796 DEBUG SQL:393 - select child0_.id as id9_0_, child0_.parent_id as parent2_9_0_ from child child0_ where child0_.id=?
Hibernate: select child0_.id as id9_0_, child0_.parent_id as parent2_9_0_ from child child0_ where child0_.id=?
10:05:28,796 DEBUG SQL:393 - select parent0_.id as id8_0_ from parent parent0_ where parent0_.id=?
Hibernate: select parent0_.id as id8_0_ from parent parent0_ where parent0_.id=?
10:05:28,811 DEBUG SQL:393 - select children0_.parent_id as parent2_1_, children0_.id as id1_, children0_.id as id9_0_, children0_.parent_id as parent2_9_0_ from child children0_ where children0_.parent_id=?
Hibernate: select children0_.parent_id as parent2_1_, children0_.id as id1_, children0_.id as id9_0_, children0_.parent_id as parent2_9_0_ from child children0_ where children0_.parent_id=?
Debug level Hibernate log excerpt:88k log available on request
Code: