Equals and hashCode implementation
The hashCode method can be simplified down to (just simply restructuring your logic and removing redundancy):

@Override
public int hashCode() {
    return id == null ? 0 : id.hashCode();
}


The equals method is a bit more complicated. Your code does appear to keep the hashCode/equals contract, so that is good, but, sometimes there are better ways than others to test for things. For example, often when you override hashCode and equals it is because these instances are used in a Set or Map. A shortcut is to often check for identity before checking for equality.... i.e. if (other == this) return true; This can (often/sometimes) save a lot of internal processing to resolve equality.

I have a template I tend to use for equals(...) and it goes like this:

@Override
public boolean equals(final Object other) {
    if (this == other) {
        // nothing declared for simplest case.
        return true;
    }
    if (!(other instanceof MyClass)) {
        // other is null or not one of us.....
        return false;
    }
    // Code appropriate for **your** class goes here
    final Myclass their = (MyClass)other;
    if (this.id == their.id) {
        // identity goes first.
        // this works for both values null too.
        return true;
    }
    if (this.id == null) {
        // we are null, they are not.
        return false;
    }
    return id.equals(their.id);
}


This method is longer than yours, but it makes the logic clear.

When you have to conform to 'bigger' contracts like the hashCode/equals contract (or the Comparable contract), it is often better to make the logic progression very obvious....