У меня есть класс History
, в котором хранится история State
объектов. Для этой цели History
поддерживает внутренний список, точнее, 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>();
}
Метод equals
опирается на поле 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;
}
В модульных тестах моей модели метод equals
ведет себя как ожидалось; проверено equalsverifier .
Однако, выше уровня персистентности, метод equals
ведет себя по-другому. Хотя два экземпляра History
имеют одинаковый список состояний, они не равны.
Это связано с классом Hibernate PersistentBag
, который - намеренно - нарушает 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);
}
Ну, это делает мой equals
метод бесполезным ...
Как я мог сохранить класс History
без потери семантики equals
?
P.S .: Существует соответствующий открытый выпуск на https://hibernate.onjira.com/browse/HHH-5409.
Обновление
Это мой тестовый пример:
- Я создаю историю
h
и добавляю несколько состояний.
- В сеансе
s1
я сохраняю h
в базе данных, используя Hibernate.
- В сеансе
s2
я извлекаю h
из базы данных, снова используя какой-то поисковый запрос через Hibernate. Давайте назовем этот извлеченный экземпляр h*
.
- Теперь я звоню
h.equals(h*)
. Я ожидаю, что равенство имеет место. Однако h.equals(h*)
возвращает false
. Примечание: конечно, h != h*
имеет место.
Это связано с тем, что Hibernates использует экземпляр PersistentBag
вместо ArrayList
для представления состояний h*
.
На первый взгляд трудно найти другой бизнес-ключ для History
. Две истории равны, если они имеют одинаковый ход событий, не так ли? Кроме того, почему я должен перенести мою модель на уровень персистентности, в то время как ее логика кажется здравой (конечно, я могу ошибаться).