-->
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.  [ 147 posts ]  Go to page Previous  1 ... 5, 6, 7, 8, 9, 10  Next
Author Message
 Post subject:
PostPosted: Thu Apr 22, 2004 10:19 pm 
Regular
Regular

Joined: Wed Dec 31, 2003 4:26 am
Posts: 108
Location: Berkeley, CA
Sorry I missed that post. Thanks Rob. And thanks again Christian.

RobJellinghaus wrote:
The reason is that Hibernate will be constructing instances of these composite id objects as it reads ...


This clearly shows why the composite key object needs to implement equals(). Got that. I don't see why the object that uses the composite key would also need to override equals(). Yet by default hbm2java generates an equals() and hashCode() method. Isn't that "wrong" -- or rather not needed?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 22, 2004 10:20 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
The recommended practice is to use the entity class _itself_ as an identifier class, avoiding the additional EntityID class.

_________________
JAVA PERSISTENCE WITH HIBERNATE
http://jpwh.org
Get the book, training, and consulting for your Hibernate team.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 22, 2004 10:43 pm 
Regular
Regular

Joined: Wed Dec 31, 2003 4:26 am
Posts: 108
Location: Berkeley, CA
christian wrote:
The recommended practice is to use the entity class _itself_ as an identifier class, avoiding the additional EntityID class.


I don't follow. Here's the mapping I have:

Code:
...
<class name="com.publishworks.merchandise.entity.Item"
    table="item" schema="merchandise"
    dynamic-update="true" dynamic-insert="true"
    optimistic-lock="version">
   <meta attribute="generated-class" inherit="false">com.publishworks.merchandise.entity.Persistent_Item</meta>
   <meta attribute="extends">com.publishworks.data.PWBase</meta>

    <composite-id name="id" class="com.publishworks.merchandise.entity.ItemPK">
      <meta attribute="generated-class" >com.publishworks.merchandise.entity.Persistent_ItemPK</meta>
        <!-- bi-directional many-to-one association to DataSlice -->
       <key-many-to-one name="dataSlice"
           class="com.publishworks.merchandise.entity.DataSlice"
       >
          <column name="dsrc_id" />
       </key-many-to-one>      
      <!-- key-property name="dataSliceId" type="int" column="dsrc_id"/ -->
      <key-property name="clientKey" type="java.lang.String" column="client_key"/>
    </composite-id> 
... 


The generated code for the equals() method looks like the following on the Persistent_PKItem:

Code:
    public boolean equals(Object other) {
        if ( (other == other ) ) return true;
        if ( !(other instanceof Persistent_ItemPK) ) return false;
        Persistent_ItemPK castOther = (Persistent_ItemPK) other;
        return new EqualsBuilder()
            .append(this.getClientKey(), castOther.getClientKey())
            .append(this.getDataSlice(), castOther.getDataSlice())
            .isEquals();
    }


(The first line is wrong and should read this==other, but let's ignore that for now.)

This looks OK to me. The thing is I also get a generated equals() method for the Persistent_Item class, which I don't think is needed! Indeed I even try setting

Code:
      <meta attribute="implement-equals">false</meta>

In the Item.hbm.xml file and hbm2java still generates this method! :

Code:
    public boolean equals(Object other) {
        if ( (other == other ) ) return true;
        if ( !(other instanceof Persistent_Item) ) return false;
        Persistent_Item castOther = (Persistent_Item) other;
        return new EqualsBuilder()
            .append(this.getId(), castOther.getId())
            .isEquals();
    }


Now my original question: I do not think (based on Rob's good explanation) that this method should be or needs to be generated.

Isn't that right? Should I open a Jira?

Apologies if this is too basic.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Apr 23, 2004 12:26 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
I think the basic fundamental misunderstanding here is that some people have decided (on the basis of what, I'm not sure) that Hibernate calls equals() and hashCode() on an entity object.

It does not. Ever.

(well, it does for classes with embedded composite ids, but it is trivial to avoid that by writing a composite identifier class)

The only time that MyEntity.hashCode() gets called is if you, the user, add the entity to a HashSet. Now, that does include associations mapped with <set>, since Hibernate delegates to HashSet. But you could easily use <bag> instead, if this is such an enormous problem.

FYI, I have written an entire large system using detached objects, w/o ever overriding equals() and hashCode(), but I would not now consider that to be a good practice.

equals()/hashCode() should use the natural key. If you plan to change the natural key value, do it in a txn where the object does not belong to any loaded set-valued association. This is almost always straightforward.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Apr 23, 2004 12:28 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
christian wrote:
The recommended practice is to use the entity class _itself_ as an identifier class, avoiding the additional EntityID class.



eh? That is not the recommendation! Its better to use a composite identifier class....


Top
 Profile  
 
 Post subject:
PostPosted: Fri Apr 23, 2004 5:29 am 
Regular
Regular

Joined: Wed Dec 31, 2003 4:26 am
Posts: 108
Location: Berkeley, CA
gavin wrote:
christian wrote:
The recommended practice is to use the entity class _itself_ as an identifier class, avoiding the additional EntityID class.


eh? That is not the recommendation! Its better to use a composite identifier class....


After some time to think, it seems Christian meant that I need never use "parent_id" in the equals() method but just use the parent reference itself.

So should I open a Jira since the current hbm2java DOES override equals() and hashCode() on classes that use composite keys even when those keys are in separate classes as shown above?


Top
 Profile  
 
 Post subject: Why are proxies any different.
PostPosted: Fri Apr 23, 2004 5:37 am 
Regular
Regular

Joined: Wed Dec 31, 2003 4:26 am
Posts: 108
Location: Berkeley, CA
To simplfiy things a bit... let's say I only focus on the Use Cases that don't require detached objects. For this case it seems we don't need to override eq/hc and everything will be fine (with the caveat for composite key identifiers -- which should act like value objects).

Now the issue of proxies. melq has concluded that one needs to override eq/hc for proxies. Is that even without disconnected objects? I'm wondering whether this depends on usage. If one is consistent in never using find() (or, alternatively, only using find()) won't Hibernate only ever return either an instance of the class itself OR an instance of proxy. If so (and that's the question), then why override eq/hc ?


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 04, 2004 10:46 am 
Newbie

Joined: Tue May 04, 2004 10:24 am
Posts: 1
You could end up with two objects with the same id, that are not considered equal, right?


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 05, 2004 1:45 pm 
Regular
Regular

Joined: Wed Dec 31, 2003 4:26 am
Posts: 108
Location: Berkeley, CA
sridhar wrote:
You could end up with two objects with the same id, that are not considered equal, right?


Ignoring the issue with proxies (which is a central issue in this thread) the problem only exists if one mixes objects from multiple Sessions.

Since we already use a version of the ThreadLocal pattern (via Spring) we only ever have on active session. Then we just ensure that objects which we kept in memory (e.g., in the http session) is reassociated with the hibernate session at the start of the request; or at least before using the hibernate session to fetch new objects. Doing this has avoided the problem of multiple instances with the same Id and we have not needed to override equals/hashCode. It does "just work". For other use cases I'd try to follow the recommended approach of using a business key's value (e.g., username) for equals() and hashCode(). This does run into the problem of what to do when that property changes and the associated object is in a collection....


Top
 Profile  
 
 Post subject:
PostPosted: Wed May 05, 2004 1:48 pm 
Regular
Regular

Joined: Wed Dec 31, 2003 4:26 am
Posts: 108
Location: Berkeley, CA
sridhar wrote:
You could end up with two objects with the same id, that are not considered equal, right?


Previous post didn't really answer this question directly. Using a single session means that hibernate will unique object instance by id. So there will never be 2 objects, only one instance. Thus the default equals which is instance equality, will hold for the "two objects".


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 09, 2005 2:45 am 
Newbie

Joined: Wed Mar 09, 2005 2:31 am
Posts: 1
Location: Saitama-ken - Japan
Hi all. I'm new to Hibernate. I found a link to this post in 109.html, and I read it all. It was very interesting. I only have two questions:

If I use "Long session and automatic versioning" in my web application (as described in the Hibernate Reference Documentation) and leave the original implementation of equals() and getHashCode() as is (that is, ==), am I safe from the issues described here? If I understood correctly, yes my model objects would not span multiple sessions, but I'd like to have someone with more experience confirm that.

If I keep my Hibernate session in the server (meaning I don't send it back and forth to the browser), and synchronize access to it (in case the user feels like playing with the browser's navigation buttons like a crazy maniac), are there any drawbacks of using this approach?

Thanks, and sorry for bringing up an old post!

Best regards,

_________________
Boogerman

Bouger, travailler, manger et se reposer, c'est la devise de la tortue!


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 09, 2005 12:41 pm 
Newbie

Joined: Fri Dec 09, 2005 11:02 am
Posts: 7
My situation:
    Using the well establish DAO pattern for clean separation of data from business layers, in a Web environment using spring.
    Using the DAO pattern, access and use of the Hibernate Session is at very well defined points. The rest of the application has no access. Therefore using Collections of pre-persisted and disconnected objects is the norm, not the exception. They must support the contracts of Equals and Comparable.

The advice given in 109.html has significant issues, as identified in this forum by members of the hibernate team. One example:
    -> Instead of using the database identifier for the equality comparison, you should use a set of properties for equals() that identify your individual objects. For example, if you have an "Item" class and it has a "name" String and "created" Date, I can use both to implement a good equals() method. No need to use the persistent identifier, the so called "business key" is much better. It's a natural key, but this time there is nothing wrong in using it!

    This is deeply flawed logic "use the business key to determine an objects equality". The problem is that once you add said object to a Set/Collection, the contents of the Collection must be treated as immutable to maintain the contracts of Equals and Comparable! Not OOP, and not practical..

A summary of the Hibernate advice given to date in this forum:
    -> Why not make equals and hashCode dependant on the Hibernate ID or the Hibernate ID plus object version?

    Fails because you can't add the object to a Set/Map key until it's been saved

    -> Detached Objects don't work well if you can't find a business key for equals. The business key has to be stable/unique for the duration of the comparison, that is, while the detached objects are in a Set, for example.

    This is deeply flawed logic which is the same as 109.html "use the business key to determine an objects equality" The problem is that once you add said object to a Set/Collection, the contents of the Collection must be treated as immutable to maintain the contracts! Not OOP, and not practical..


So, building on the excellent discourse of others in the forum, my solution is presented below in the hopes that it might help a few of the 20k plus readers of this forum with a similar situation.

The base class is designed to use a unique id for pre-persisted classes, and continue to use that id after being persisted for the duration of the objects life. In the "disconnected is the norm" situation I have to ensure that my Equals and Comparable contracts are honoured for pre-persisted to persisted objects, so I can just set and forget when using my Collections. After the object is persisted, it will continue to use the generated key. Future fetches from Hibernate and the object will use it's database id.

It does mean that a pre-persisted object and [b]new instance[b] / retrieved object will not be equal, but two new instances of the same retrieved object will always be equal regardless of session or JVM. So you can freely mix and match disconnected objects (even across JVMs), and you can mix and match pre-persisted and persisted objects (with the same JVM), but you can't mix and match newly retrieved and disconnected objects with pre-persisted objects.

Code:
import java.io.Serializable;
import java.rmi.dgc.VMID;

public class BaseModel implements Serializable {
   
    private volatile VMID vmid;
    private Long id;
   
    public Long getId() {
        return id;
    }

    private void setId(Long id) {
        this.id = id;
    }
               
    public boolean equals(Object obj) {
        final boolean returner;
        if (obj instanceof BaseModel) {
            return getVmid().equals(((BaseModel)obj).getVmid());
        } else {
            returner = false;
        }
        return returner;
    }
   
    public int hashCode() {
        return getVmid().hashCode();
    }
   
    private Object getVmid() {
        if (vmid != null || vmid == null && id == null) {
            if (vmid == null) { //Avoid the performance impact of synchronized if we can
                synchronized(this) {
                    if (vmid == null)
                        vmid = new VMID();
                }
            }
            return vmid;
        }
        return id;
    }
}


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 09, 2005 12:55 pm 
Newbie

Joined: Fri Dec 09, 2005 11:02 am
Posts: 7
Here is a slightly optimized version of the code. The vmid was being used in some earlier work, where I was storing it in the DB. Might be of interest for some folk tho, where being "born" with your key is required. :-)

Code:
public class BaseModel implements Serializable {
   
    private volatile Object object;
    private Long id;
   
    public Long getId() {
        return id;
    }

    private void setId(Long id) {
        this.id = id;
    }
               
    public boolean equals(Object obj) {
        final boolean returner;
        if (obj instanceof BaseModel) {
            return getObject().equals(((BaseModel)obj).getObject());
        } else {
            returner = false;
        }
        return returner;
    }
   
    public int hashCode() {
        return getObject().hashCode();
    }
   
    private Object getObject() {
        if (object != null || object == null && id == null) {
            if (object == null) { //Avoid the performance impact of synchronized if we can
                synchronized(this) {
                    if (object == null)
                        object = new Object();
                }
            }
            return object;
        }
        return id;
    }
}


Top
 Profile  
 
 Post subject: What Are You Trying to Accomplish?
PostPosted: Fri Dec 09, 2005 1:29 pm 
Newbie

Joined: Wed Nov 23, 2005 1:09 pm
Posts: 6
All of this advice pertaining to implementing .equals() and .hashCode() with respect to hibernate session, second level caching, etc. all reeks of leaky abstraction (http://www.joelonsoftware.com/articles/ ... tions.html) that should be plugged up.

What if you moved to another persistence mechanism, would all your decisions about .equals() and .hashCode() translate correctly if you had to care about hibernate when you implemented them? I doubt it.

What this means it that in the ideal world, we should implement .equals() and .hashCode() in terms of properties not associated with any persistence mechanism. In otherwords, do not use the database id.

Now you might be asking, well with out it, there's no way to separate two instances of a particular entity. To this, I ask the following questions:

1. Do you really need two different instances? Could the application use the same row, and be fine? It is the same data afterall.
2. If not, isn't another property that could be used to identify uniqeness?
3. If not, why are you using a database generated id to indicate uniqueness at a business level? What if your entity had to be stored in two different databases, and your logic needs to look it up?
4. If people can have social security numbers, customers have account numbers, and producs have UPC codes, why can't your entity have its own code?

Once all these have been answered, it's usually evident that there's another approach to determine uniqueness, that there was lack of foresight when designing the database schema, or both.


Top
 Profile  
 
 Post subject: Re: What Are You Trying to Accomplish?
PostPosted: Fri Dec 09, 2005 3:22 pm 
Newbie

Joined: Fri Dec 09, 2005 11:02 am
Posts: 7
nshupe wrote:
All of this advice pertaining to implementing .equals() and .hashCode() with respect to hibernate session, second level caching, etc. all reeks of leaky abstraction (http://www.joelonsoftware.com/articles/ ... tions.html) that should be plugged up.

Excellent article! Agreed, this leaky abstraction needs plugging!

Quote:
What if you moved to another persistence mechanism, would all your decisions about .equals() and .hashCode() translate correctly if you had to care about hibernate when you implemented them?

The code above works independently of Hibernate, or any persistence mechanism for that matter. That said, it IS designed for Hibernate; there is probably some law of invasiveness for frameworks, and there are always trade-offs. So, this code would likely not be required in another framework.

Quote:
1. Do you really need two different instances? Could the application use the same row, and be fine? It is the same data afterall.

At the business level, my code has no concept of row, and yes, there may be multiple instances of the same object, that need to be identified properly as being the same.

Quote:
2. If not, isn't another property that could be used to identify uniqeness?

As stated above, in my context, the property must remain unchanged for the duration of the objects existence. That pretty much eliminates all "business" type properties. I can generate my own, using Java's VMID, but that has tradeoffs, primarily in performance.

Quote:
3. If not, why are you using a database generated id to indicate uniqueness at a business level? What if your entity had to be stored in two different databases, and your logic needs to look it up?

Because it is a good candidate, because it is known to be unique for a given class of objects. If it needed to be moved to another database and treated as equal, the objects implementation of Equal would need to take that into consideration. I believe the RMID approach would work; albeit with performance considerations.

Quote:
4. If people can have social security numbers, customers have account numbers, and producs have UPC codes, why can't your entity have its own code?

It can. As stated above, I would choose VMID. But, the tradeoff is performance.

Quote:
Once all these have been answered, it's usually evident that there's another approach to determine uniqueness, that there was lack of foresight when designing the database schema, or both.

Well, we are talking ORM, which also implies there could be design issues with the "Object" part of the problem, or the Bridge part of the problem.

I am definitely no expert here, but I stand aghast that no one has addressed this critical issue by summarizing the key real life issues, and the Hibernate issues and implications to consider for each approach. 109.html doesn't do it, nor does this thread, nor does the documentation.

So I ask the community at large, is there a Hibernate expert out there that can address this critical issue for us?


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 147 posts ]  Go to page Previous  1 ... 5, 6, 7, 8, 9, 10  Next

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.