Прокси-серверы 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', как было предложено Виллемом де Витом выше.