How to persist class that depends on internal list using Hibernate?

I have class History that stores, well, a history of State objects. For that purpose, History maintains an internal list, more precisely an ArrayList:

// states is 'life cycle object' -> cascading
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private final List<State> states;

History() {
    states = new ArrayList<State>();
}

Method equals relies on field states:

public final boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if (obj == null) {
        return false;
    }
    if (!(obj instanceof History)) {
        return false;
    }
    History other = (History) obj;
    // we do not use fields to ensure compatibility with lazy loading
    if (!Objects.equal(getStates(), other.getStates())) {
        return false;
    }
    return true;
}

In the unit tests of my model, method equals behaves like expected; it is tested by equalsverifier.

However, above the persistence layer, method equals behaves differently. Although two History instances have the same list of states, they are not equal.

This is due to Hibernate's class PersistentBag which - deliberately - violates the collection API:

/**
 * Bag does not respect the collection API and do an
 * JVM instance comparison to do the equals.
 * The semantic is broken not to have to initialize a
 * collection for a simple equals() operation.
 * @see java.lang.Object#equals(java.lang.Object)
 */
public boolean equals(Object obj) {
    return super.equals(obj);
}

Well, this renders my equals method useless...

How could I persist class History without loosing semantics of equals?

P.S.: There is an corresponding open issue at https://hibernate.onjira.com/browse/HHH-5409.

Update

This basically is my test case:

  1. I create history h and add some states.
  2. In session s1, I save h to the database using Hibernate.
  3. In session s2, I fetch h from the database using some search query again via Hibernate. Let's call that fetched instance h*.
  4. Now, I call h.equals(h*). I would expect that equality holds. However, h.equals(h*) returns false. Note: of course, h != h* holds.

This is due the fact that Hibernates uses an instance of PersistentBag instead of ArrayList to represent h*'s states.

At a first glance it is hard to find another business key for History. Two histories are equal if they have the same course of events, aren't they? Furthermore, why should I have to adopt my model to the persistence layer while its logic seems sound (I may err, of course).

Answers


Why would you want to include a List in a equals/hashCode comparison for a root entity? This is a wrong approach.

Equality should be based on the underlying table record unicity constraint. Ideally, you should have a natural key in every database table like a SSN, email address, domain name, UUID.

Sometimes, you don't have a natural id, but then you still have the primary key. Contrary to popular belief, you can use the PK for equals/hashCode, you just have to make sure that hashCode renders a constant value for every entity state transition.

If you worry about hashCode performance, then you should know that a persistent collection is not meant to store large amounts of data. So, prior to having the hashCode as a real bottleneck (as depicted in Effective Java), you'd have to fetch the collection, and the fetching tons of data is way more costly anyway. If that's the case, you are better off using a query instead.


I'm guessing you did not (correctly) implement the equals method of State? If you did, are the states in the List in the same order?

But, you should not use referenced entities to define your equals/hashCode especially if, like in your case, they are lazy loaded. If you load your history without the states and then for any of numerous reasons the equals gets hibernate will perform 2 queries for this. Furthermore, if you detach History and then call equals on it you will get a LazyIntializationException.

Try to find another business key for your History that does not depend and other entities.


Need Your Help

Embedded Font Styling - Embedded Font Styles vs CSS Styling

css fonts font-face webfonts

I'm hoping to understand what the difference is between embedding fonts in a site like this:

how to know is it iphone or ipad?

iphone xcode ipad uiimagepickercontroller device

i want to know the user uses the iphone or ipad,if the user uses the iphone i want to open the camera,if he uses the ipad or runs in simulator i want to open the library. how it is possible?