Hibernate равно и прокси
У меня есть один BaseEntity, который абстрагирует свойство id и version. этот класс также реализует hashcode и equals на основе свойства PK (id).
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;
}
}
теперь два объекта A и B расширяют 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;
закрыть сеанс
загрузить a с ленивым b и попробовать a1.getB(). equals (b1) дает false
но если я сравню с a1.getB(). getId(). equals (b1.getId()), то дает истинное странное!!
я думаю, что это из-за объекта-посредника java assist, так или иначе, чтобы разрешить это?
Ответы
Ответ 1
Чтобы иметь возможность lazy-load ассоциации 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 и имеют одинаковый идентификатор.
Ответ 2
Я использую 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
Вы также можете заставить его работать таким образом, полезно, если вы не знаете, какой экземпляр B (может случиться, если ваш equals
находится в суперклассе)
if (HibernateProxyHelper.getClassWithoutInitializingProxy(this) != HibernateProxyHelper.getClassWithoutInitializingProxy(obj))
return false
Ответ 4
Это в основном эффект стандартного наследования 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), и эти значения, очевидно, одинаковы.