Calling session.save on a POJO generated by hbm2java (or any POJO which uses persistent identity for its equals() and hashCode()) can break the invariants of (and hence essentially corrupt) any HashSets containing that POJO.
If this is well known in the Hibernate community, it should certainly be mentioned a lot more prominently in all documentation of saveOrUpdate.
The following mapping:
Code:
<class name="com.nimblefish.core.domain.Client">
<id name="id" type="long" unsaved-value="null" >
<generator class="native"/>
</id>
<property name="name" type="string"/>
...
</class>
and the following (hbm2java-generated) class:
Code:
package com.nimblefish.core.domain;
public class ClientGen implements Serializable {
/** identifier field */
private Long id;
/** nullable persistent field */
private String name;
/** persistent field */
private Set campaigns;
/** full constructor */
public ClientGen(String name, Set campaigns) {
this.name = name;
this.campaigns = campaigns;
}
...
public boolean equals(Object other) {
if ( !(other instanceof ClientGen) ) return false;
ClientGen castOther = (ClientGen) other;
return new EqualsBuilder()
.append(this.getId(), castOther.getId())
.isEquals();
}
public int hashCode() {
return new HashCodeBuilder()
.append(getId())
.toHashCode();
}
}
and the following test case:
Code:
HashSet set = new HashSet();
Client client = new Client("test", new HashSet());
set.add(client);
log.info("client.hashcode = "+client.hashCode());
log.info("set contains client: "+set.contains(client));
session.save(client);
log.info("client.hashcode = "+client.hashCode());
log.info("set contains client: "+set.contains(client));
produces the following output:
Code:
INFO: client.hashcode = 629
INFO: set contains client: true
INFO: client.hashcode = 630
INFO: set contains client: false
In other words, whenever an hbm2java object that has been added to a HashSet is saved in the session, that HashSet will be corrupt from then on with respect to that object. (There may be other kinds of collections which will also then be broken -- possibly *all* hash-based collections!)
Since HashSets are quite commonly the type of Set used when constructing object graphs to be saved with a cascading Session.save, this bug could seriously affect users who are doing a lot of cascading saves of newly created object graphs which use persistent identity. Essentially, saving a graph of objects had better be the last thing you do with any of those objects, and you had better discard any hash-based collections containing any objects that get saved for the first time.
Is this already covered in the Hibernate book and if so what advice is given?
The adjoining thread about equals() and hashCode() has been discussing these issues in great depth. I hope some Hibernate folks will comment.
Cheers,
Rob