Hi,
I have extracted the classes from a bigger project and isolated the problem (which is still the same as in my first posting). It should be reproducable. The only external dependencies are ToStringBuilder, HashCodeBuilder, EqualsBuilder and Log, which are all from the jakarta commons project.
Parent:
Code:
public class Parent extends BaseEntity {
transient private Log log = LogFactory.getLog(Parent.class);
private String name;
private Set<Child> childs = new HashSet<Child>();
Parent() {
super();
}
public Parent(String name) {
super();
setName(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Child> getChilds() {
return childs;
}
private void setChilds(Set<Child> childs) {
for (Child child : childs) {
log.info("setter: hashcode = " + child.hashCode());
log.info("setter: object = " + child);
}
log.info("setter: size " + childs.size());
this.childs = childs;
}
public void addChild(Child child) {
log.info("adder: hashcode = " + child.hashCode());
log.info("adder: object = " + child);
childs.add(child);
log.info("adder: size " + childs.size());
}
public void removeChild(Child child) {
childs.remove(child);
}
public int hashCode() {
return new HashCodeBuilder(-665290105, 1060143345).append(this.name).toHashCode();
}
public boolean equals(Object object) {
if (object == this) {
return true;
}
if (!(object instanceof Parent)) {
return false;
}
Parent rhs = (Parent) object;
return new EqualsBuilder().append(this.name, rhs.getName()).isEquals();
}
}
Child:
Code:
public class Child extends BaseEntity {
transient private Log log = LogFactory.getLog(Child.class);
private String name;
private Parent parent;
Child() {
super();
}
public Child(String name, Parent parent) {
super();
setName(name);
setParent(parent);
}
public Parent getParent() {
return parent;
}
private void setParent(Parent parent) {
if (!(parent == null ? this.parent == null : parent.equals(this.parent))) {
if (this.parent != null) {
this.parent.removeChild(this);
}
this.parent = parent;
if (parent != null) {
parent.addChild(this);
} else {
log.warn("trying to set a business key to null, this will fail!");
}
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int hashCode() {
return new HashCodeBuilder(1765049893, 159477687).append(this.name).append(this.parent).toHashCode();
}
public boolean equals(Object object) {
if (object == this) {
return true;
}
if (!(object instanceof Child)) {
return false;
}
Child rhs = (Child) object;
return new EqualsBuilder().append(this.name, rhs.getName()).append(this.parent, rhs.getParent()).isEquals();
}
}
BaseEntity:
Code:
public abstract class BaseEntity implements Serializable {
private Long id;
private Integer version;
public Long getId() {
return id;
}
protected void setId(Long id) {
this.id = id;
}
protected Integer getVersion() {
return version;
}
protected void setVersion(Integer version) {
this.version = version;
}
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.DEFAULT_STYLE, false);
}
}
Parent hbm.xml
Code:
<hibernate-mapping>
<class name="test.Parent">
<id name="id" column="id">
<generator class="increment"/>
</id>
<version name="version" column="version" type="java.lang.Integer" />
<property name="name" />
<set name="childs" inverse="true" cascade="all,delete-orphan">
<key column="parent" not-null="true" update="true" />
<one-to-many class="test.Child" />
</set>
</class>
</hibernate-mapping>
Child hbm.xml
Code:
<hibernate-mapping>
<class name="test.Child">
<id name="id" column="id">
<generator class="increment" />
</id>
<version name="version" column="version" type="java.lang.Integer" />
<property name="name" />
<many-to-one name="parent" class="test.Parent" not-null="true" insert="true" update="true" />
</class>
</hibernate-mapping>
Logging output:
Code:
6820 [main] INFO dao.BaseDAOTestCase - retrieve
6836 [main] INFO datasource.JdbcTransactionObjectSupport - JDBC 3.0 Savepoint class is available
6868 [main] DEBUG def.DefaultLoadEventListener - loading entity: [test.Child#1]
6869 [main] DEBUG def.DefaultLoadEventListener - attempting to resolve: [test.Child#1]
6869 [main] DEBUG def.DefaultLoadEventListener - object not resolved in any cache: [test.Child#1]
6921 [main] DEBUG def.DefaultLoadEventListener - loading entity: [test.Parent#1]
6921 [main] DEBUG def.DefaultLoadEventListener - creating new proxy for entity
6923 [main] DEBUG def.DefaultLoadEventListener - attempting to resolve: [test.Parent#1]
6923 [main] DEBUG def.DefaultLoadEventListener - object not resolved in any cache: [test.Parent#1]
6947 [main] DEBUG def.DefaultInitializeCollectionEventListener - initializing collection [test.Parent.childs#1]
6947 [main] DEBUG def.DefaultInitializeCollectionEventListener - checking second-level cache
6947 [main] DEBUG def.DefaultInitializeCollectionEventListener - collection not cached
6950 [main] DEBUG def.DefaultLoadEventListener - loading entity: [test.Child#1]
6951 [main] DEBUG def.DefaultLoadEventListener - entity found in session cache
6951 [main] DEBUG def.DefaultLoadEventListener - attempting to resolve: [test.Child#1]
6951 [main] DEBUG def.DefaultLoadEventListener - resolved object in session cache: [test.Child#1]
6955 [main] DEBUG def.DefaultInitializeCollectionEventListener - collection initialized
6956 [main] INFO test.Parent - setter: hashcode = -2100885451
6979 [main] INFO test.Parent - setter: object = test.Child@24de7d[name=<null>,parent=<null>,id=1,version=0]
6979 [main] INFO test.Parent - setter: size 1
6981 [main] INFO test.Parent - adder: hashcode = 1649469942
6984 [main] INFO test.Parent - adder: object = test.Child@24de7d[name=<null>,parent=test.Parent@1bdb58[name=parent,childs=[test.Child@24de7d[test.Child@24de7dtest.Child@24de7dtest.Child@24de7d]],id=1,version=0],id=1,version=0]
6984 [main] INFO test.Parent - adder: size 2
6984 [main] DEBUG def.AbstractFlushingEventListener - flushing session
6985 [main] DEBUG def.AbstractFlushingEventListener - processing flush-time cascades
6986 [main] DEBUG def.AbstractSaveEventListener - persistent instance of: test.Child
6986 [main] DEBUG def.DefaultSaveOrUpdateEventListener - ignoring persistent instance
6986 [main] DEBUG def.DefaultSaveOrUpdateEventListener - object already associated with session: [test.Child#1]
6986 [main] DEBUG def.AbstractSaveEventListener - persistent instance of: test.Child
6986 [main] DEBUG def.DefaultSaveOrUpdateEventListener - ignoring persistent instance
6986 [main] DEBUG def.DefaultSaveOrUpdateEventListener - object already associated with session: [test.Child#1]
6988 [main] DEBUG def.AbstractFlushingEventListener - dirty checking collections
6989 [main] DEBUG def.AbstractFlushingEventListener - Flushing entities and processing referenced collections
7007 [main] DEBUG def.DefaultFlushEntityEventListener - Updating entity: [test.Parent#1]
7016 [main] DEBUG def.AbstractFlushingEventListener - Processing unreferenced collections
7017 [main] DEBUG def.AbstractFlushingEventListener - Scheduling collection removes/(re)creates/updates
7020 [main] DEBUG def.AbstractFlushingEventListener - Flushed: 0 insertions, 1 updates, 0 deletions to 2 objects
7020 [main] DEBUG def.AbstractFlushingEventListener - Flushed: 0 (re)creations, 1 updates, 0 removals to 1 collections
7021 [main] DEBUG def.AbstractFlushingEventListener - executing flush
7026 [main] DEBUG def.AbstractFlushingEventListener - post flush
Mysql query log:
Code:
050920 10:22:45 42 Query SET autocommit=0
42 Prepare [1] select child0_.id as id0_, child0_.version as version10_0_, child0_.parent as parent10_0_, child0_.name as name10_0_ from Child child0_ where child0_.id=?
42 Execute [1] select child0_.id as id0_, child0_.version as version10_0_, child0_.parent as parent10_0_, child0_.name as name10_0_ from Child child0_ where child0_.id=1
42 Prepare [2] select parent0_.id as id0_, parent0_.version as version4_0_, parent0_.name as name4_0_ from Parent parent0_ where parent0_.id=?
42 Execute [2] select parent0_.id as id0_, parent0_.version as version4_0_, parent0_.name as name4_0_ from Parent parent0_ where parent0_.id=1
42 Prepare [3] select childs0_.parent as parent__, childs0_.id as id__, childs0_.id as id0_, childs0_.version as version10_0_, childs0_.parent as parent10_0_, childs0_.name as name10_0_ from Child childs0_ where childs0_.parent=?
42 Execute [3] select childs0_.parent as parent__, childs0_.id as id__, childs0_.id as id0_, childs0_.version as version10_0_, childs0_.parent as parent10_0_, childs0_.name as name10_0_ from Child childs0_ where childs0_.parent=1
42 Prepare [4] update Parent set version=?, name=? where id=? and version=?
42 Execute [4] update Parent set version=2, name='parent' where id=1 and version=1
42 Query commit