-->
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.  [ 10 posts ] 
Author Message
 Post subject: contains() method for a collection is not working properly !
PostPosted: Mon Sep 26, 2005 11:37 am 
Newbie

Joined: Fri Apr 15, 2005 3:59 am
Posts: 7
Hi all !

I have a relation 1-to-n between Constitution and Information. In Constitution I have a set of Information objects.
I think that the method "contains()" for the set does not work properly. If I used an iterator instead the object was found in the collection. When I have used contains() instead it returned false. I do not why. Below you have my configuration and an unit test.

Please help me !!

Thanks in advance

Hibernate version:3.0.5

Domain classes:
Code:
// Information
....
  public static Information generate(Constitution constitution,
                                       InfoType infoType)
    {
        Information info = new Information();
        info.constitution = constitution;
        constitution.addInformation(info);
         
         // .....
    }
public boolean equals(Object other)
    {
        if (this == other) {
            return true;
        }
        if (!(other instanceof Information)) {
            return false;
        }

        final Information o = (Information) other;

        if (id != null && !id.equals(o.id)) {
            return false;
        }
        if (id == null && !(hashCode() == o.hashCode())) {
            return false;
        }
        return true;
    }

    public int hashCode()
    {
        if (id == null) {
            return super.hashCode();
        } else {
            return id.hashCode();
        }
    }
public Constitution getConstitution()
    {
        return constitution;
    }

    protected void setConstitution(Constitution constitution)
    {
        this.constitution = constitution;
    }
....

// Constitution
....
public static Constitution generate()
    {
        Constitution constitution = new Constitution();
        constitution.informations = new LinkedHashSet();
        return constitution;
    }

public boolean equals(Object other)
    {
        if (this == other) {
            return true;
        }
        if (!(other instanceof Constitution)) {
            return false;
        }

        final Constitution o = (Constitution) other;
        if (id != null && !id.equals(o.id)) {
            return false;
        }
        if (id == null && !(hashCode() == o.hashCode())) {
            return false;
        }
        return true;
    }

    public int hashCode()
    {
        if (id == null) {
            return super.hashCode();
        } else {
            return id.hashCode();
        }
    }

    public Set getInformations()
    {
        return informations;
    }

    protected void setInformations(Set informations)
    {
        this.informations = informations;
    }

    public void addInformation(Information information)
    {
        information.setPosn(new Integer(this.informations.size()));
        this.informations.add(information);
        information.setConstitution(this);

    }
....



Mapping documents:
Code:
<!-- Constitution.hbm.xml-->
.....

        <set
            name="informations"
            lazy="true"
            inverse="true"
            cascade="all-delete-orphan"
            sort="unsorted"
            order-by="posn"
        >

            <key
                column="constitution_id"
            >
            </key>

            <one-to-many
                  class="test.Information"
            />

        </set>
....
<!-- Information -->
...
        <many-to-one
            name="constitution"
            class="test.Constitution"
            cascade="none"
            outer-join="auto"
            column="constitution_id"
            not-null="true"
        />

        <property
            name="posn"
            type="java.lang.Integer"
            column="posn"
        />
....


Code between sessionFactory.openSession() and session.close():
Code:

/// I have the following unit test

       Constitution c = Constitution.generate();
        InfoType type = InfoType.generate(typeName);
        service.saveInfoType(type);


        service.saveConstitution(c);
        Information info1 = Information.generate(c, type);
        Information info2 = Information.generate(c, type);
        service.saveConstitution(c);
     System.out.println("-------------------------111");
        Integer pos1 = info1.getPosn();
        Integer pos2 = info2.getPosn();
        System.out.println("-------------------------222");
// ++++ Here we will get an error. I do not why !!!!. ++++
//+++++  If I iterate the collection "info1" is found !!!  ++++
//++++  Seems that contains is not working properly +++++
        assertTrue("1. information set must contain " + info1 +
                   ", but contains:" +
                   c.getInformations(), c.getInformations().contains(info1));
     System.out.println("-------------------------333");


Full stack trace of any exception that occurs:

1. information set must contain (20,0), but contains:[(20,0), (21,1)]

junit.framework.AssertionFailedError: 1. information set must contain (20,0), but contains:[(20,0), (21,1)] at test.ConstitutionServiceTest.testSwapInformationPosition(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

Name and version of the database: MySQL 4.1 InnoDB.

The generated SQL (show_sql=true):

Hibernate: insert into info_type (name) values (?)
Hibernate: insert into constitution values ( )
Hibernate: insert into information (constitution_id, posn, info_type_id) values (?, ?, ?)
Hibernate: insert into information (constitution_id, posn, info_type_id) values (?, ?, ?)
-------------------------111
-------------------------222


Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 26, 2005 12:39 pm 
Expert
Expert

Joined: Sat Jun 12, 2004 4:49 pm
Posts: 915
hashCode or equals isn't good (I think hashCode in your example, but eheck it)


Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 26, 2005 1:37 pm 
Newbie

Joined: Fri Apr 15, 2005 3:59 am
Posts: 7
But in my case "equals" works because when I iterate the collection I found the object. This is strange !!!
if you found an error in my code please show me exactly where.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 26, 2005 2:27 pm 
Expert
Expert

Joined: Sat Jun 12, 2004 4:49 pm
Posts: 915
I don't find error in code , but if any collection don't work then problem is equals or hashCode - you have any class with equals and without hashCode (it can be any class in hierarchy) - this error is hard for find, but this is it


Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 26, 2005 4:31 pm 
Expert
Expert

Joined: Sat Jan 17, 2004 2:57 pm
Posts: 329
Location: In the basement in my underwear
Since you are using the ID as part of your Hashcode, I am assuming that this seems to be the following sequence of events.

You add an unsaved object to a Set and then save it and then contains() cannot find the child object.

Problem in that case would be that your hashcode value is changed after the child was added to the parent.

In you look at the HashSet code it uses an underlying HashMap to keep track of the values. So if you add something to a set with a hashcode of 1 then the underlying HashMap gets an entry with a key of 1.

If the hashcode changes the HashMap still contains a key of 1 so when you go to look up the object with a hashcode of 2 then you won't be able to find the object in the Set.

Take a look at the FAQs, there's a good one on Equals and HashCode that should explain it all in gory detail.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 26, 2005 5:11 pm 
Beginner
Beginner

Joined: Mon Sep 27, 2004 4:28 pm
Posts: 44
There's an extensive discussion of the cascade-save hashcode problem in the hibernate in action book. The suggestion made in the book is to use a natural key for your hashcode implementation instead of a primary-key one.

In my case, saving a new object tends to be the very last thing I do in a transaction and so I don't run in to the problems of using a primary key in my equals/hashcode implementation...

The other thing to consider is whether or not you need to implement them at all. You'll probably only need them if you're entities are used across instances of a session or serialized across RMI, etc. Otherwise you'll be fine since the first level cache assures you the same session won't return more than one
instance per entity.

If you _really_ have to use the pk in equals() and you _really_ have to do cascade saves (and then use the collection), then it is possible to implement a hybrid equals()/hashcode() implementation. I tried this for a bit but it was annoyingly complex.. You might feel differently though. It basically involved using a natural key in hashcode, and an id (if I had one) in equals. It's very easy to code a hybrid solution that breaks the hashCode/equals contract though so if you go this route I'd write a set of unit tests first!

All of this is only a problem when an entity gets it's identity (pk) while it's in a collection. Hopefully that doesn't happen too often in your application. If it does, then the natural key route might be just the thing.

Hope that helps,
Phill


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 27, 2005 4:03 am 
Newbie

Joined: Fri Apr 15, 2005 3:59 am
Posts: 7
What it is strange is if I remove equals and hashcode from my domain classes the unit test will work. But, in this case an another unit test will fail. The last one is calling the contains method into another session than the session where the objects are saved in the collection.
Anyway, for me it is strange why when I am iterating the object info1 is found in the collection in both cases but the contains() method has a different behaviour. I know that Hibernate generate a proxy for each persistent object and maybe there is a problem regarding contains().
I whould like to have an example how to write my own equals() and hashCode() method.
I have used the example provided in the documentaion with some small changes.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 27, 2005 5:17 am 
Newbie

Joined: Fri Apr 15, 2005 3:59 am
Posts: 7
Hi all !

I have removed my hashCode() method from my domain class and now everythings are ok. but I whould like to have a working example with a custom hashCode() method implementaion. Could somebody provide one ?

Thanks


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 27, 2005 9:06 am 
Expert
Expert

Joined: Sat Jun 12, 2004 4:49 pm
Posts: 915
see Effective java with Joshua Blosh and wiki page on hibernate
If you use eclipse you can use common4e plugin (search google or www.eclipseplugincentral.com)
This is my example (use commons4e and commons-lang form appache)
Code:
   public int hashCode() {
        return new HashCodeBuilder().appendSuper(super.hashCode()).append(id)
                .toHashCode();
    }

    public boolean equals(final Object other) {
        if (this == other)
            return true;
        if (!(other instanceof FinVn))
            return false;
        FinVn castOther = (FinVn) other;
        return new EqualsBuilder().appendSuper(super.equals(other)).append(id,
                castOther.id).isEquals();
    }


Top
 Profile  
 
 Post subject:
PostPosted: Wed Sep 28, 2005 7:59 am 
Newbie

Joined: Fri Apr 15, 2005 3:59 am
Posts: 7
Many thanks for help.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 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.