-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 
Author Message
 Post subject: session.save(hbm2JavaGeneratedPOJO) breaks HashSets
PostPosted: Wed Feb 25, 2004 7:35 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
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


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 6:36 am 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
We've updated this page with some ideas
http://www.hibernate.org/Documentation/EqualsAndHashCode

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 2:54 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
I have edited the wiki page pretty heavily. Most importantly, there was a whole section saying "You only need to implement equals() and hashCode() if you have objects in a single Set that needs to persist across multiple Hibernate sessions" or some such. This is wrong; you *always* need to implement them if you *ever* want your objects to live in *any* Java collection, persistently *or* transiently. I have updated the wiki accordingly.

I have also added some further discussion of the issues with the "Workaround: save before adding to collection" pattern.

Cheers,
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 7:08 pm 
Hibernate Team
Hibernate Team

Joined: Tue Sep 09, 2003 2:10 pm
Posts: 3246
Location: Passau, Germany
Quote:
This is wrong; you *always* need to implement them if you *ever* want your objects to live in *any* Java collection, persistently *or* transiently. I have updated the wiki accordingly.


No, this is not correct. If you can live with == based identity, you do not have to implement equals/hashCode at all, it is even better than id based identity.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 8:02 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
Boy, do I feel dumb.

You're right, of course.

I will update the wiki page to fix the wrongness I introduced.

Cheers!
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 8:27 pm 
Hibernate Team
Hibernate Team

Joined: Tue Sep 09, 2003 2:10 pm
Posts: 3246
Location: Passau, Germany
I will try to put together a little table tomorrow on what does/does not work with which equals/hashCode strategy, to clear this up a bit further.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 9:08 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
That sounds good. I did update the wiki page to fix my wrongheadedness. I have also updated the JIRA issue about hbm2java's behavior to recommend that hbm2java's default behavior be to NOT generate equals() or hashCode() methods at all.

Cheers,
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 27, 2004 10:25 am 
Hibernate Team
Hibernate Team

Joined: Tue Sep 09, 2003 2:10 pm
Posts: 3246
Location: Passau, Germany
Okay, I have added a little summary to the equals/hashCode page on what works with which strategy. Take a look.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 27, 2004 1:57 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
Looks good to me. Thanks :-)

Cheers!
Rob


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.