-->
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.  [ 3 posts ] 
Author Message
 Post subject: hashCode, equals, Proxies and 2nd Level Cache - solution?
PostPosted: Thu May 20, 2004 6:08 am 
Newbie

Joined: Wed May 19, 2004 1:25 pm
Posts: 11
Okay, I'm very new to hibernate. I've got the problem where I'm composing a Set before I save an object graph. After a bit of thinking, and a bit of reading, I dont think anyone else has the solution.

fyi

Its obvious that the hashcode will change if its based on the identity, so persist the hashcode when the object is first created. This will satisfiy java collection contracts aswell as logical identity semantics that are enforced by the database. The identity ID is only assigned once the object is persisted. Before then, if we do an "equals" we can test that we have not been given a DB identity, therefore we use ==.

Now, i'm not too sure about proxies (still a bit new here). When do they get created and used? do they get put into the 2nd level cache? if there is
a proxy, then where is the real object? (since the hashcode is in the database).

(I had a big problem with 2nd level cache not working, thought I was missing something.... didnt mark my classes as @cachable)

Pro: collections work before DB and identity is maintained
Con: extra data column (hashcode). it doesnt need to be indexed like the identity ID/whatever. ALL(?) data always loaded because hashcode and equals have been overided.

Code:
/**
* @hibernate.class table="Cat"
* @hibernate.cache usage="nonstrict-read-write"
*/
class Cat
{
    // Synthentic Identity (unique enforced by database)
    Integer id;
    /**
     * @hibernate.id generator-class="native" column="euid"
     */
    public Integer getId()
    {
        return id;
    }
    public void setId(Integer id)
    {
   this.id = id;
    }

    // store this otherwise evil things happen with sets
    private int hashcode = super.hashCode();
    /**
     * @hibernate.property not-null="true"
     */
    public int getHashCode()
    {
        return hashcode;
    }
    public void setHashCode(int hashcode)
    {
        this.hashcode = hashcode;
    }
    public int hashCode()
    {
        return hashcode;
    }
    public boolean equals(Object obj)
    {
        // (1) same instance (creation or same session)
        if (obj == this) return true;

        // (2) hashcode must match (we honour hashcode from creation)
        if (hashcode != obj.hashCode()) return false;

        // (3) hashcode might match, but duplicate hashcode the same
        if (obj instanceof Cat)
        {
            Cat le = (Cat)obj;

            // (4) if we dont have an id, then we're not persisted and rule (1) is authorative
            if (this.id == null || le.getId() == null) return false;

            // (5) we've been persisted, check our identifier
            if (this.id.intValue() == le.getId().intValue()) return true;
        }

        // (6) no
        return false;
    }
}


Top
 Profile  
 
 Post subject: hmm...
PostPosted: Thu May 20, 2004 8:07 am 
Newbie

Joined: Wed May 19, 2004 1:25 pm
Posts: 11
Thinking about it a bit more, everything works fine with sets, lists, maybe identity maps aswell, but consider,

Map<Integer,Cat> catmap = new ...
Cat cat = new Cat();
catmap.put(cat.getId(),cat); // ID has not been allocated (1)

There is no simple way around that one since even if you create some contrived ID based on the hashcode or what the hashcode will become you cant retrieve it from the the map unless you know the cat's hashcode (bucket issues).

Obviously you could make an identity key object that notifies its Set containers when its hashCode has changed. The containers then move it to the correct bucket.


Fortunately,
(1) If you've got the identity number of entity, the entity has already been saved (otherwise why do i have an identity number).
(1.1) I came up with adding static data from a text file (annotated with IDs). The IDs are invalid because you've got to merge it into your new object graph. The new IDs will be valid when persisted.

(2) If you've got the identity number, you can defer retrieval to the 2nd level cache. ie:why do you need to map by ID at all? (cache)

(3) If you need to put the objects into maps, it might be easier to do it after persisting the objects than to save the objects. I've only ever (sofar) needed to iterate through sets and access lists by index.

(3) shouldnt need to worry about ternary ops (not tested) as the key will be known on persistance.


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 20, 2004 11:50 am 
Newbie

Joined: Wed May 19, 2004 1:25 pm
Posts: 11
To sort things out for map, you can use a non-persistant BaseID that is based on the hashcode and identity... but how will you know the hashcode? if you know the ID you might want to send that along with the hashcode... but then it gets messy (bad vibes). Key thing is that the BaseID may only be used as legal entity comparison once the entity has been persisted to disk since persistant identity has not been established.

Alternatively, BaseID could always return 0 as a hashcode... then you wouldnt need to worry about the messy business of keeping hashcode and ID together. still, i dont know what it'll do to collections. Set as per List being 'normalised' on every insert? horrible.

Code:
class Cat
{
    ... as above ...

    CatID catid = new CatID();

    class CatID extends BaseID
    {
        CatID() { super(0); }
        public int hashCode() { return hashcode; }
        public boolean legal() { return id != null; }
        public int intValue() { return id.intValue(); }
    }
    public BaseID getCatID() { return catid; }
}

public class BaseID
{
    int value, hashcode;
    public BaseID(int value, int hashcode) { this.value = value; this.hashcode = hashcode;}
    public int intValue() { return value; }
    public boolean legal() { return true; }
    public int hashCode() { return hashcode; }
    public boolean equals(Object o)
    {
        if (o == this) return true;
        if (!legal()) return false;
        if (o instanceof BaseID)
        {
            BaseID bid = (BaseID) o;
            if (!bid.legal()) return false;
            return intValue() == bid.intValue();
        }
        return false;
    }
}


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