views:

76

answers:

3

I know that this topic has been discussed umpteen number of times, but still need some clarifications on the issue we are facing. Hibernate best practices talk about using natural keys for the equals / hashCode methods. We do have a composite key which consists of 2 parameters (say String name, Organization org) to which a particular object belongs. Unfortunately I can't use org since it is lazily loaded and leads to other issues and I don't want to create a surrogate key either (maybe auto generated from the database or a UUID created via an API call prior to object creation). What kind of options do I really have since in the above Object (name, org) fields are unique and I can't just write my logic just based on the name field itself. Is surrogate key the only option, does this mean I have to store this value as part of each row in the table?

A: 

What is the object called has a name and Organization? I'll assume it's called Foo. I take it you can't say foo.getOrganization() because the database connection is closed? Can you change your FooDAO so it loads the Organization as soon as foo is read from the database?

Then it will be available later when you need to do something with equals() and hashCode().

Tony Ennis
+2  A: 

Hibernate best practices talk about using natural keys for the equals / hashCode methods.

Yes so I won't elaborate on this.

We do have a composite key which consists of 2 parameters (say String name, Organization org) to which a particular object belongs. Unfortunately I can't use org since it is lazily loaded and leads to other issues.

Could you elaborate a bit and maybe illustrate with some code? I'd like to understand how you got this working exactly and what the problem is.

What kind of options do I really have since in the above Object (name, org) fields are unique and I can't just write my logic just based on the name field itself.

As I said, providing more details might help. But just in case, note that calling org.getId() on a proxy should not trigger the loading of the entity, as long as you're using property access type, so you should be able to use this in your equals implementation.

Pascal Thivent
@Pascal Thivent Up for specifying property access strategy which allows the user to get the identifier of the associated entity without hitting the database
Arthur Ronald F D Garcia
+3  A: 

If you desire something like

public class MyEntity {

    private String name;

    private Organization organization;

    // getter's and setter's

    public boolean equals(Object o) {
        if(!(o instanceof MyEntity))
            return false;

        MyEntity other = (MyEntity) o;
        return new EqualsBuilder().append(getName(), other.getName())
                                  .append(getOrganization(), other.getOrganization())
                                  .isEquals();
    }

}

But if you want to avoid it because you do now want to load a lazy loaded entity, you can rely on Hibernate.isInitialized method and supply your custom routine

public boolean equals(Object o) {
    if(!(o instanceof MyEntity))
        return false;

    MyEntity other = (MyEntity) o;
    boolean equals = new EqualsBuilder().append(getName(), other.getName())
                                        .isEquals();

    if(Hibernate.isInitialized(getOrganization())) {
       // loaded Organization
    } else {
       // supply custom routine
    }

    return equals;
}

I have a not-updated web page where Hibernate supply the following matrix

                                        no eq/hC at all  eq/hC with the id property  eq/hC with buisness key
use in a composite-id                   No               Yes                         Yes
multiple new instances in set           Yes              No                          Yes
equal to same object from other session No               Yes                         Yes
collections intact after saving         Yes              No                          Yes        

Where the various problems are as follows:

use in a composite-id:

To use an object as a composite-id, it has to implement equals/hashCode in some way, == identity will not be enough in this case.

multiple new instances in set:

Will the following work or not:

HashSet someSet = new HashSet();
someSet.add(new PersistentClass());
someSet.add(new PersistentClass());
assert(someSet.size() == 2);

equal to same object from another session:

Will the following work or not:

PersistentClass p1 = sessionOne.load(PersistentClass.class, new Integer(1));
PersistentClass p2 = sessionTwo.load(PersistentClass.class, new Integer(1));
assert(p1.equals(p2));

collections intact after saving:

Will the following work or not:

HashSet set = new HashSet();
User u = new User();
set.add(u);
session.save(u);
assert(set.contains(u));

It also highlight this Thread where equals/hashCode implementation is heavily discussed

Arthur Ronald F D Garcia
IMHO, the problem is the hashCode implementation. I wonder how the OP could implement a (consistent) hashCode with his lazy loaded Organization.
Pascal Thivent