2012-06-13 3 views
10

У меня есть одно BaseEntity, которое абстрагирует свойство id и version. этот класс также реализует hashcode и equals на основе свойства PK (id).Hibernate равно и прокси

BaseEntity{ 

    Long id; 
    Long version; 

public int hashCode() { 
    final int prime = 31; 
    int result = 1; 
    result = prime * result + ((id == null) ? 0 : id.hashCode()); 
    return result; 
} 

public boolean equals(Object obj) { 
    if (this == obj) 
     return true; 
    if (obj == null) 
     return false; 
    if (getClass() != obj.getClass()) 
     return false; 
    BaseEntity other = (BaseEntity) obj; 
    if (id == null) { 
     if (other.id != null) 
      return false; 
    } else if (!id.equals(other.id)) 
     return false; 
    return true; 
} 


} 

теперь два предприятия А и Б проходит BaseEntity, как показано ниже

A extends BaseEntity{ 
    `B b` 
    B getB(){return b;) 
    void setB(B b){this.b=b;} 
} 

B extends BaseEntity{ 
} 

object b1; 
object a1; 
a1.set(b1); 
session.save(a1) //cascade save; 

закрытия сессии загрузить с ленивым б и попробовать a1.getB(). Равно (b1) дает ложные но если я сравните с a1.getB(). getId(). equals (b1.getId()), тогда дает истинное странное !! Я думаю, что это из-за объекта-посредника java assist, так или иначе, чтобы разрешить это?

ответ

15

Для удобства использования команды a.b, Hibernate устанавливает поле b в a прокси. Прокси - это экземпляр класса, который расширяет B, но не является B. Таким образом, ваш метод equals() всегда будет терпеть неудачу при сравнении экземпляра non-proxy B с экземпляром proxy B, поскольку он сравнивает классы обоих объектов:

if (getClass() != obj.getClass()) 
    return false; 

В случае Hibernate лиц, вы должны заменить это

if (!(obj instanceof B)) { 
    return false; 
} 

Кроме того, обратите внимание, что

  • Hibernate не рекомендует реализации equals() и hashCode() с использованием идентификатора, но с использованием естественного идентификатора. Реализация этого с идентификаторами может вызвать проблемы, потому что у объектов нет идентификатора до тех пор, пока они не будут сохранены, и генерируется идентификатор.
  • При использовании наследования объектов проблема еще хуже. Предположим, что B является суперклассом двух субинтетов B1 и B2. Hiberante не может знать, какой тип (B1 или B2) равен a.b перед его загрузкой. Таким образом, a.b будет инициализирован прокси, который является подклассом B, но не является подклассом B1 или B2. Поэтому методы hashCode() и equals() должны быть реализованы в B, но не должны быть переопределены в B1 и B2. Два экземпляра B следует считать равными, если они являются экземплярами B и имеют одинаковый идентификатор.
+0

Спасибо, понял. соглашайтесь с вами по вопросам, которые могут возникнуть из-за equals() и hashcode(), основанных на Id. Я думаю, что сейчас я поеду с вариантом instanceof, так как введение естественного идентификатора на этом этапе в приложении будет сложно. –

-1

Это в основном эффект стандартного наследования Java.

a1.getB().equals(b1) использует Object.equals() (за исключением случаев, когда вы переопределили equals() в своем классе), который возвращает true, если a1.getB() и b1 - это один и тот же экземпляр. Я не знаю, что вы сделали точно (форматирование кода нарушено), но похоже, что вы снова загрузили a в другой сеанс, поэтому вы получаете новый экземпляр для a и a.getB(), и, следовательно, Object.equals() возвращает false ,

a1.getB().getId().equals(b1.getId()) использует Long.equals(), который возвращает true, если длинные значения одинаковы (даже для разных экземпляров объекта Long), и эти значения, очевидно, одинаковы.

+0

исправленного форматирование кода, согласно моему коду я думаю, что он должен вызвать равно на 'BaseEntity', а не' Object.equals() ', в настоящее время она вызова' Object.equals () 'не знаю, почему –

+0

Можете ли вы добавить код для A.getB() (какой тип?), BaseEntity.equals() и для отображения A (не менее id и члена b)? Возможно, тогда я увижу больше. – Johanna

+0

обновленный код для геттерного сеттера. –

1

Вы также можете сделать это работает таким образом, полезно, если вы не знаете, Wich экземпляр является B (может произойти, если ваш equals находится в суперкласс)

if (HibernateProxyHelper.getClassWithoutInitializingProxy(this) != HibernateProxyHelper.getClassWithoutInitializingProxy(obj)) 
    return false 
+0

Пожалуйста, см. Мой комментарий к ответу Ральфа. Вы также связываете свои классы домена с зависимостью Hibernate. –

8

Я использую Hibernate.getClass в течение многих лет и я не заметил проблему:

@Override  
public boolean equals(final Object obj) { 
    if (this == obj) { 
     return true; 
    } 
    if (obj == null) { 
     return false; 
    } 
    if (Hibernate.getClass(this) != Hibernate.getClass(obj)) { 
     return false; 
    } 

    ... check for values 

    return true; 
} 
+3

За исключением того, что ваши классы домена теперь почти не связаны с Hibernate. Предположим, вы хотите поделиться своим доменом с каким-либо другим приложением, не использующим спящий режим вообще. Теперь у вас проблемы. Не поймите меня неправильно, это сработает, но это очень плохо пахнет. Также, если ребята из спящего режима решают переместить \ удалить прокси-ресурс и обновить hibernate, домен больше не будет компилироваться. –

Смежные вопросы