Ваш hashCode и реализации equals неверны.
Короче говоря, проблемы с ним:
- Они не придерживаются стиля 'делегирования' (они не делегируют задача определения равенства для соответствующих классов)
- Они не отвечают на главный вопрос о том, что представляет объект: строку в БД или понятие, которое строка в БД пытается представить.
Делегированные проверки равенства
Оба hashCode и equals указаны как требовать , чтобы вы не выбрасывали из них NPE. Для равенства это означает, что вы не можете просто позвонить, скажем, a.equals(b)
- вам нужно будет сделать это a == null ? b == null : a.equals(b)
(и поскольку этот 'never throw' транзитивен, a.equals(b)
в порядке, даже если b имеет значение null ) или используйте вместо него помощник Objects.equal(a, b)
.
Для хэш-кода это означает, что нулевые значения должны быть определены как имеющие некоторое предопределенное значение для хеширования. Кроме того, в более общем случае, когда у вас есть «подобъект» (например, поле некоторого непримитивного типа), общая идея - для hashCode и равняется каскаду: используйте productJPA.hashCode()
, а не productJPA.getId()
.
То же самое для равных. Не делайте этого:
(ProductHasHerbJPA)o).getHerbJpa().getId()==herbJpa.getId()
, а делайте так:
Objects.equals(o.getHerbJpa(), herbJpa);
И если 2 JPA трав должны считаться равными, если их идентификаторы равны , то метод equals()
класса HerbJPA должен быть определен соответствующим образом, а если нет, то нет. Работа вашего класса ContainsJPA не заключается в том, чтобы знать, как вычислить, равны ли 2 экземпляра herbJPA - herbJPA может это сделать самостоятельно. вы избежите множества проблем с нулевым значением, сделав это таким образом.
Обратите внимание, вы можете позволить lombok позаботиться обо всем этом шаблоне за вас.
Затем мы добраться до некоторых сложных проблем с JPA и равенством в частности.
Стратегия common для использования equals / hashCode в экосистеме java (за пределами JPA / Hibernate) - это посмотреть на все поля, которые являются частью идентификатора объекта tity, обычно это все они. Проблема в том, что это плохо работает с JPA: большинство методов получения объекта JPA - это прокси, которые вызывают запросы к БД, если вы их вызываете. С достаточно взаимосвязанной структурой БД (множество ссылок) это означает, что один вызов equals
завершает запрос половины вашей БД, занимает тонну памяти и полчаса для завершения, что явно не является возможным решением.
Ключевой вопрос: что на самом деле представляет ваш объект, и, насколько я знаю, JPA не дает четких указаний.
Экземпляр HerbsJPA представляет строку в базе данных
Тогда можно сделать следующие выводы:
- Как всегда, по сп c объект всегда равен самому себе:
if (this == other) return true;
. В противном случае ... - Если один или оба объекта не имеют установленного unid, тогда они не могут быть равны друг другу - 2 незаписанные строки, даже если они полностью идентичны для каждого поля в объект, по-прежнему не представляет «одну и ту же строку», следовательно, не равен!
- Если оба объекта имеют установленный unid, тогда они равны, если unid равны, а в противном случае - нет. Независимо от всех других значений! - 2 разные строки с одинаковыми значениями ... это все еще две разные строки.
Кстати, этот вид удобен тем, что вы полностью избегаете этого "Ой, он запрашивает всю БД". UNID-файлы не являются дорогостоящими и обычно уже предварительно загружены.
Экземпляр HerbsJPA представляет собой «траву».
Если это так, могу ли я предложить вашему классу неправильное название? Наверное, это должно быть «Херб». Может быть, 'HerbJpa' (NB: JPA в заглавных буквах является нарушением наиболее распространенного правила стиля).
Тогда наиболее разумным решением будет ИЗБЕГАТЬ полной проверки unid и посмотреть только во всех других полях (или, по крайней мере, во всех других полях, которые представляют что-то об идентичности травы. Обычно это большинство из них, но иногда вы можете обойтись без определения некоторого свойства, которое вызовет бурю запросов к БД, например, «список связанных трав», представленный в БД с таблицей соединений, как «не часть личность'. В конце концов, «unid in the db» - это случайная деталь реализации понятия «трава» и, следовательно, не может быть частью его идентичности! Конечно, эта проблема «шторм вызовов БД». простое, и имя класса в порядке (ну, оно должно быть «Jpa», а не «JPA», но другое).
@Override public int hashCode() {
return id == null ? super.hashCode() : (int) id;
// note, other answer's id %1000 is silly;
// it is needlessly inefficient, don't do it that way.
}
@Override public boolean equals(Object other) {
if (other == this) return true;
if (other == null || other.getClass() != ContainsJPA.class) return false;
return id == null ? false : id.equals(other.id);
}