Прокси-серверы hibernate javassist и `Object # equals`
При предоставлении реализации #equals
для UDT в Java одно из условий заключается в том, что переданный объект-аргумент должен быть экземпляром текущего класса, иначе мы с ошибкой return false
см. Эффективная Java (EJ2). Тем не менее, при использовании Hibernate 4 мы можем получить экземпляры javassist proxy из-за ленивой загрузки, где это условие #equals
не удастся. Какой был бы лучший выбор для преодоления этого? Несколько вариантов, о которых я могу думать, следующие:
- расширить реализацию
equals
, чтобы принять во внимание дело прокси. Минусы: плата за совместимость, жесткая зависимость от инфраструктуры прокси-сервера Hibernate, хакерских, сущностных или доменных моделей должны быть агностическими для используемого ORM, т.е. Поскольку они могут быть повторно использованы в разных контекстах, где нет необходимости в ORM, например. Swing UI.
- проверьте, является ли это прокси до вызова
equals
. Минусы: не всегда возможно, т.е. Работа с коллекциями и неявные вызовы equals
, например, Карта.
- Воздержитесь от ленивой загрузки. Минусы: не разумны и не эффективны во всех случаях использования.
ОБНОВЛЕНИЕ
Повторное рассмотрение EJ2. Я считаю, что следующее будет работать для всех сценариев (Type-Type, Type-Proxy, Proxy-Type и Proxy-Proxy), но, как указано в одном из комментариев ниже, он может зацикливаться навсегда, если Type сравнивается с совершенно другим типом, например Person.equals(Employee)
и оба используют одинаковые критерии EJ2.
if (this.getClass() != anObject.getClass())
{
return anObject.equals(this);
}
Ответы
Ответ 1
Я наткнулся на ту же проблему. Способ, который я исправил, состоял в том, чтобы изменить способ .equals
.
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!getClass().isAssignableFrom(obj.getClass()))
return false;
AbstractEntity other = (AbstractEntity) obj;
if (getId() == null) {
if (other.getId() != null)
return false;
} else if (!getId().equals(other.getId()))
return false;
return true;
Трюк заключается в том, чтобы не сравнивать классы одинаковыми, но использовать метод isAssignableFrom
. Другой трюк заключается в том, чтобы не использовать прямые свойства (other.id
), но использовать get-method (other.getId()
)
Ответ 2
У меня нет репутации, чтобы прокомментировать ответ Виллема де Вита. Затем мне нужно отправить новый ответ.
Чтобы решить проблему djechelon, вы должны заменить эту строку:
if (!getClass().isAssignableFrom(obj.getClass()))
для
if ( !obj.getClass().isAssignableFrom(getClass()) && !getClass().isAssignableFrom(obj.getClass()) )
Затем вы убедитесь, что равные будут работать для всех сценариев (Type-Type, Type-Proxy, Proxy-Type и Proxy-Proxy).
У меня нет репутации, чтобы проголосовать за ваш ответ. Я так несчастен!
Ответ 3
Вы можете сделать две вещи:
1. Измените equals на использование instanceof вместо равенства классов. Тип прокси не равен типу объекта, а расширяет тип объекта.
- Разверните прокси-сервер, чтобы получить сам объект (есть несколько инструментов спящего режима, которые помогут вам это сделать)
Ответ 4
Ответ DHansen выше близок, но для меня (с использованием Hibernate) это решило проблему:
if (!Hibernate.getClass(this).equals(Hibernate.getClass(obj))) {
return false;
}
как предложено Dr. Hans-Peter Störr
Также важно, чтобы всегда использовал 'getters', как было предложено Виллемом де Витом выше.