1. Обзор
В этом руководстве мы рассмотрим обработку равенства с объектами JPA Entity.
2. Соображения
В общем, равенство просто означает, что два объекта одинаковы. Однако в Java мы можем изменить определение равенства, переопределив методы Object.equals()
и Object.hashCode()
. В конечном счете, Java позволяет нам определить, что значит быть равным. Но сначала нам нужно рассмотреть несколько вещей.
2.1. Коллекции
Коллекции Java группируют объекты вместе. Логика группировки использует специальное значение, известное как хэш-код, для определения группы объекта.
Если значение, возвращаемое методом hashCode()
, одинаково для всех сущностей, это может привести к нежелательному поведению. Допустим, наш объект сущности имеет первичный ключ, определенный как id
, но мы определяем наш метод hashCode()
как:
@Override
public int hashCode() {
return 12345;
}
Коллекции не смогут различать разные объекты при их сравнении, потому что все они будут иметь один и тот же хэш-код. К счастью, решить эту проблему так же просто, как использовать уникальный ключ при создании хэш-кода. Например, мы можем определить метод hashCode()
, используя наш идентификатор
:
@Override
public int hashCode() {
return id * 12345;
}
В этом случае мы использовали идентификатор
нашей сущности для определения хэш-кода. Теперь коллекции могут сравнивать, сортировать и хранить наши объекты.
2.2. Переходные объекты
Вновь созданные объекты сущности JPA, которые не связаны с контекстом постоянства , считаются находящимися в переходном состоянии . Эти объекты обычно не имеют заполненных членов @Id .
Следовательно, если equals()
или hashCode()
используют идентификатор
в своих вычислениях, это означает, что все переходные объекты будут равны, потому что все их идентификаторы
будут нулевыми
. Есть не так много случаев, когда это желательно.
2.3. Подклассы
Подклассы также вызывают беспокойство при определении равенства. Обычно классы сравнивают в методе equals() .
Поэтому включение метода getClass()
поможет отфильтровать подклассы при сравнении объектов на предмет равенства.
Давайте определим метод equals()
, который будет работать, только если объекты относятся к одному классу и имеют одинаковый идентификатор
:
@Override
public boolean equals(Object o) {
if (o == null || this.getClass() != o.getClass()) {
return false;
}
return o.id.equals(this.id);
}
3. Определение равенства
Учитывая эти соображения, у нас есть несколько вариантов обработки равенства. Соответственно, подход, который мы выбираем, зависит от специфики того, как мы планируем использовать наши объекты. Давайте посмотрим на наши варианты.
3.1. Нет переопределений
По умолчанию Java предоставляет методы equals()
и hashCode()
благодаря всем объектам, происходящим от класса Object .
Поэтому самое простое, что мы можем сделать, это ничего не делать. К сожалению, это означает, что при сравнении объектов, чтобы считаться равными, они должны быть одними и теми же экземплярами, а не двумя отдельными экземплярами, представляющими один и тот же объект.
3.2. Использование ключа базы данных
В большинстве случаев мы имеем дело с объектами JPA, которые хранятся в базе данных. Обычно эти объекты имеют первичный ключ, который является уникальным значением. Следовательно, все экземпляры этой сущности, имеющие одинаковое значение первичного ключа, равны. Итак, мы можем переопределить equals()
, как мы делали выше для подклассов, а также переопределить hashCode()
, используя только первичный ключ в обоих случаях.
3.3. Использование бизнес-ключа
В качестве альтернативы мы можем использовать бизнес-ключ для сравнения объектов JPA. В этом случае ключ объекта состоит из элементов сущности, отличных от первичного ключа. Этот ключ должен сделать сущность JPA уникальной. Использование бизнес-ключа дает нам тот же желаемый результат при сравнении сущностей без необходимости использования первичных ключей или ключей, сгенерированных базой данных.
Допустим, мы знаем, что адрес электронной почты всегда будет уникальным, даже если это не поле @Id
. Мы можем включить поле электронной почты в методы hashCode()
и equals()
:
public class EqualByBusinessKey {
private String email;
@Override
public int hashCode() {
return java.util.Objects.hashCode(email);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (obj instanceof EqualByBusinessKey) {
if (((EqualByBusinessKey) obj).getEmail().equals(getEmail())) {
return true;
}
}
return false;
}
}
4. Вывод
В этом руководстве мы обсудили различные способы обработки равенства при написании объектов сущностей JPA. Мы также описали соображения, которые мы должны учитывать при выборе подхода. Как всегда, полный исходный код можно найти на GitHub .