Я реализую equals()
, hashCode()
и toString()
из моих объектов с помощью всех доступных полей в бобе.
Я получаю некоторое Ленивое init Исключение на frontend, когда я пытаюсь сравнить равенство или когда я печатаю состояние obj. Поэтому некоторый список в объекте может быть ленив инициализированный.
Я задаюсь вопросом, к чему корректный путь для реализации equals()
и toString()
на объекте объекта.
Помимо того, что сказали другие, я также думаю, что объект Hibernate должен быть подключен к сеансу, чтобы получить ленивый Информация. Без подключения к базе данных эти списки не могут быть загружены :)
Когда вы реализуете методы equals и hashCode для объектов Hibernate, важно
instanceof
вместо этогоДополнительная информация:
Stackoverflow: overriding-equals-and-hashcode-in-java
Документация Hibernate: Equals и HashCode
Редактировать: те же правила о недопустимости прямого доступа к свойствам класса применимы и к методу toString - только использование геттеров гарантирует, что будет возвращена информация, действительно содержащаяся в классе.
Вы можете полагаться на идентификатор объекта , чтобы сравнить свой объект с помощью equals
public boolean equals(Object o) {
if(o == null)
return false;
Account account = (Account) o;
if(!(getId().equals(account.getId())))
return false;
return true;
}
Но что происходит, когда у вас есть непостоянная сущность. Он не будет работать , потому что его идентификатор не был назначен.
Итак, давайте посмотрим, что говорит об этом Java Persistence with Hibernate Book
Бизнес-ключ - это свойство или некоторая комбинация свойств, , которая уникальна для каждого экземпляра с одним и тем же идентификатором базы данных .
Итак
Это естественный ключ, который вы бы использовали , если бы вместо этого вы не использовали суррогатный первичный ключ.
Итак, предположим, что у вас есть пользовательский объект, и его естественными ключами являются firstName и lastName (по крайней мере, его / ее firstName и lastName часто не меняются). Так что он будет реализован как
public boolean equals(Object o) {
if(o == null)
return false;
if(!(o instanceof User))
return false;
// Our natural key has not been filled
// So we must return false;
if(getFirstName() == null && getLastName() == null)
return false;
User user = (User) o;
if(!(getFirstName().equals(user.getFirstName())))
return false;
if(!(getLastName().equals(user.getLastName())))
return false;
return true;
}
// default implementation provided by NetBeans
public int hashcode() {
int hash = 3;
hash = 47 * hash + ((getFirstName() != null) ? getFirstName().hashcode() : 0)
hash = 47 * hash + ((getLastName() != null) ? getLastName().hashcode() : 0)
retrun hash;
}
Он отлично работает! Я использую даже с Mock-объектами, такими как репозитории, службы и т. Д.
А насчет метода toString (), как сказал @Bozho, вы можете помещать любую интересную информацию. Но помните, что некоторые веб-фреймворки, такие как Wicket и Vaadin, например, используют этот метод для отображения его значений.
equals ()
и hashCode ()
должны быть реализованы с использованием бизнес-ключа - то есть набора свойств, которые однозначно идентифицируют объект, но не являются его автоматическими сгенерированный ID.
в toString ()
вы можете поместить любую интересующую информацию - например, все поля.
Используйте вашу IDE (Eclipse, NetBeans, IntelliJ), чтобы сгенерировать все это для вас.
Чтобы избежать LazyInitializationException
, независимо от того, в equals ()
или в вашем представлении (jsp), вы можете использовать OpenSessionInView
.
Моя реализация toString() für Hibernate entities выглядит следующим образом:
@Override
public String toString() {
return String.format("%s(id=%d)", this.getClass().getSimpleName(), this.getId());
}
Каждый подкласс моего AbstractEntity (выше) переопределяет этот метод при необходимости:
@Override
public String toString() {
return String.format("%s(id=%d, name='%s', status=%s)",
this.getClass().getSimpleName(),
this.getId(),
this.getName(),
this.getStatus());
}
Для hashCode() и equals() имейте в виду, что Hibernate часто использует прокси-классы. Поэтому мой equals() обычно выглядит так:
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
Class<AbstractEntity> c1 = Hibernate.getClass(this);
Class<AbstractEntity> c2 = Hibernate.getClass(obj);
if (!c1.equals(c2)) return false;
final AbstractEntity other = (AbstractEntity) obj;
if (this.getId() == null) {
if (other.getId() != null) return false;
}
else if (!this.getId().equals(other.getId())) return false;
return true;
}
И, как уже было сказано другими... будьте осторожны с доступом к лениво загруженным свойствам! Простой toString() или даже log.debug(entity) может вызвать огромную активность при каскадировании на несколько лениво загруженных объектов и свойств.
Мы реализуем equals () и hashCode () в нашем суперклассе. Это работает безупречно, особенно в картах, списках и т. Д. Это должно было быть правильным, поскольку мы делаем много переходной настойчивости.
/**
* Compare two entity objects, following hibernate semantics for equality. Here we assume that
* new objects are always different unless they are the same object. If an object is loaded from
* the database it has a valid id and therefore we can check against object ids.
*
* @see com.dolby.persist.bean.EntityObject#equals(java.lang.Object)
*/
@SuppressWarnings("unchecked")
@Override
public final boolean equals(final Object object) {
if (this == object) return true;
if (object == null || this.getClass() != object.getClass()) return false;
final AbstractModelObject<ID> other = (AbstractModelObject<ID>) object;
if (this.getId() == null || other.getId() == null) return false;
return this.getId().equals(other.getId());
}
/**
* Returns an enttiy objects hashcode.
* <p>
* What we are doing here is ensuring that once a hashcode value is used, it never changes for
* this object. This allows us to use object identity for new objects and not run into the
* problems.
* </p>
* <p>
* In fact the only case where this is a problem is when we save a new object, keep it around
* after we close the session, load a new instance of the object in a new session and then
* compare them.
* </p>
* <p>
* in this case we get A==B but a.hashcode != b.hashcode
* </p>
* <p>
* This will work in all other scenarios and don't lead to broken implementations when the
* propety of the object are edited. The whole point in generating synthetic primary keys in the
* first place is to avoid having a primary key which is dependant on an object property and
* which therefore may change during the life time of the object.
* </p>
*
* @see java.lang.Object#hashCode()
*/
@Override
public final synchronized int hashCode() {
if (this.hashcodeValue == null) {
if (this.getId() == null) {
this.hashcodeValue = new Integer(super.hashCode());
}
else {
final int generateHashCode = this.generateHashCode(this.getId());
this.hashcodeValue = new Integer(generateHashCode);
}
}
return this.hashcodeValue.intValue();
}