Identity

From cdd
Jump to: navigation, search

Identity is described as "...the property of objects that distinguishes them from other objects" in Wikipedia. CDD divides stateful objects into two groups, values and entities which is common in OO. The difference between the two is that values includes all attribute in the identity while entities only include the key column(s). The key columns is either a surrogate key annotated with @Id or a natural key's annotated with one or more @Key.

You can think of the natural key as the attribute(s) that would be the primary key in a database table if not using a surrogate key (id/counter).

Which attributes that should be part of the identity (be included in hashCode/equals) follows these rules:

  1. If an attribute is annotated with @Id then that attribute will be included in hashCode/equals. Only one attribute should be marked as @Id.
  2. If one ore more attributes is annotated with @Key then these attributes will be included in hashCode/equals.
  3. If no columns are annotated with @Id or @Key then all columns will be included in hashCode/equals.


All columns are included in hashCode/equals:

@State
public class PersonState implements ImmutableState {
    public final int ssn;
    public final String givenName;
    public final String surname;
 
    // ===== Generated code =====
    ...
 
    @Override
    public int hashCode() {
        int result = ssn;
        result = 31 * result + (givenName != null ? givenName.hashCode() : 0);
        result = 31 * result + (surname != null ? surname.hashCode() : 0);
 
        return result;
    }
 
    @Override
    public boolean equals(Object that) {
        if (this == that) return true;
        if (that == null || getClass() != that.getClass()) return false;
 
        PersonState state = (PersonState)that;
 
        if (ssn != state.ssn) return false;
        if (givenName != null ? !givenName.equals(state.givenName) : state.givenName != null) return false;
        if (surname != null ? !surname.equals(state.surname) : state.surname != null) return false;
 
        return true;
    }
    ...
}


If we mark ssn as the natural key, only ssn will be included:

public class PersonState implements ImmutableState {
    @Key public final int ssn;
    public final String givenName;
    public final String surname;
 
    // ===== Generated code =====
    ...
 
    @Override
    public int hashCode() {
        int result = ssn;
 
        return result;
    }
 
    @Override
    public boolean equals(Object that) {
        if (this == that) return true;
        if (that == null || getClass() != that.getClass()) return false;
 
        PersonState state = (PersonState)that;
 
        if (ssn != state.ssn) return false;
 
        return true;
    }
    ...
}


For entities (with at least one @Id or @Key attribute) the methods hashCodeValue and equalsValue is also generated that include all attributes:

public class PersonState implements ImmutableState {
    @Key public final int ssn;
    public final String givenName;
    public final String surname;
 
    // ===== Generated code =====
    ...
 
    public int hashCodeValue() {
        int result = ssn;
        result = 31 * result + (givenName != null ? givenName.hashCode() : 0);
        result = 31 * result + (surname != null ? surname.hashCode() : 0);
 
        return result;
    }
 
    public boolean equalsValue(Object that) {
        if (this == that) return true;
        if (that == null || getClass() != that.getClass()) return false;
 
        PersonState state = (PersonState)that;
 
        if (ssn != state.ssn) return false;
        if (givenName != null ? !givenName.equals(state.givenName) : state.givenName != null) return false;
        if (surname != null ? !surname.equals(state.surname) : state.surname != null) return false;
 
        return true;
    }
    ...
}


The hashCodeValue and equalsValue can be used in contexts where you need to compare two entities. Test contexts is a type of context where you often wants to do that.


< Back          Next >