3

I am using hibernate and have bi-directional relations. How should I correctly override equals() for both classes.

Here is the code (using guava-Objects): (PS: This is a bad example and choice of entities, but I am interested in learning the recommended way to go about it)

Destination:

@Entity
@Table(name = "DESTINATION")
public class Destination{
    private Integer id;
    private String name;
    private Set<DestinationAlias> aliases = new HashSet<DestinationAlias>(0);


    @Override
    public boolean equals(Object obj) {
        if(obj == this) return true;

        if(obj instanceof Destination){
            final Destination otherDestination = (Destination) obj;
            return Objects.equal(getName().toUpperCase(), otherDestination.getName().toUpperCase()) 
                    && Objects.equal(getAliases(), otherDestination.getAliases());
        }
        return false;
    }
}

DestinationAlias:

@Entity
@Table(name = "DESTINATIONALIAS")
public final class DestinationAlias {
    private Integer idDestinationAlias;
    private String alias;   
    private Destination mainCity;

    @Override
    public boolean equals(Object obj) {
        if(obj == this) return true;

        if(obj instanceof DestinationAlias){
            final DestinationAlias otherAlias = (DestinationAlias) obj;
            return Objects.equal(getAlias().toUpperCase(), otherAlias.getAlias().toUpperCase())
                    && Objects.equal(getMainCity(), otherAlias.getMainCity());
        }
        return false;
    }
}

This is the test case:

@Test
public void testEqualsto(){
    Destination dest = new Destination("abc", 1.0f, 1.0f);
    dest.getAliases().add(new DestinationAlias("abc alias", dest));

    Destination dest1 = new Destination("abc", 1.0f, 1.0f);
    dest1.getAliases().add(new DestinationAlias("abc alias", dest1));

    assertEquals(dest, dest1);
}

As expected, stackoverflow occurs, since each equals() in turns calls the other equals() and a cycle occurs.

What is the recommended way to override equals() for bidirectional entities.

brainydexter
  • 19,826
  • 28
  • 77
  • 115

1 Answers1

3

We have to deconflict manually. In DestinationAlias I'd change the equals expression so that it will compare the Destionation IDs only (they should be unique):

return Objects.equal(getAlias().toUpperCase(), otherAlias.getAlias().toUpperCase())
            && Objects.equal(getMainCity().getId(), otherAlias.getMainCity().getId());
                                          ^^^^^^^^                          ^^^^^^^^

Further Reading

Community
  • 1
  • 1
Andreas Dolk
  • 113,398
  • 19
  • 180
  • 268
  • but, I am using destinationID to uniquely identify destination, by having it mapped to primary key on my database. (and might be null for proxy objects) – brainydexter Feb 13 '12 at 13:01
  • Ideally, you should only ever have one instance of an entity with the same ID in a given unit of work. You could rely on the `if (obj == this)` shortcut, and infinite recursion would indicate this constraint being violated. – millimoose Feb 13 '12 at 13:02
  • @Inerdial Can you please elaborate a bit on that statement? – brainydexter Feb 13 '12 at 13:04
  • What ORMs do is when you load an entity with the same key several times, you always get back the same object. Having two `Destination` objects around that represent the same database record is something I prefer to avoid, instead of trying to support it. If you made sure in your app that this doesn't happen, your original code wouldn't recurse infinitely. – millimoose Feb 13 '12 at 13:09