Должно ли свойство @Transient использоваться в equals / hashCode / toString? - PullRequest
7 голосов
/ 01 июня 2010

У меня есть объекты JPA, где некоторые свойства помечены @Transient.

Должен ли я использовать эти свойства в equals/hashCode/toString методах?

Моя первая мысль: НЕТ , но я не знаю почему.

  • Советы
  • Идеи
  • Пояснения

Ответы [ 3 ]

6 голосов
/ 01 июня 2010

Случай toString() отличается, вы можете делать все, что захотите, с помощью toString(), поэтому я покрою только equals()hashCode()).

Во-первых, правило: , если вы хотите сохранить объект в List, Map или Set , тогда требуется, чтобы equals и hashCode были реализованы , поэтому они подчиняются стандартному контракту, указанному в документации .

Теперь, как реализовать equals() и hashCode()? «Естественной» идеей было бы использовать свойства, отображаемые как Id, как часть equals():

public class User {
    ...
    public boolean equals(Object other) {
        if (this==other) return true;
        if (id==null) return false;
        if ( !(other instanceof User) ) return false;
        final User that = (User) other;
        return this.id.equals( that.getId() );
    }
    public int hashCode() {
        return id==null ? System.identityHashCode(this) : id.hashCode();
  }
}

К сожалению, это решение имеет серьезную проблему : при использовании сгенерированных идентификаторов значения не присваиваются до тех пор, пока объект не станет постоянным , поэтому если переходный объект добавляется к Set перед сохранением, его хеш-код будет изменяться, пока он находится в Set, и это нарушает контракт Set.

Таким образом, рекомендуется использовать атрибуты, которые являются частью бизнес-ключа , то есть комбинацию атрибутов, уникальных для каждого экземпляра с одинаковыми идентификационными данными базы данных. Например, для класса User это может быть имя пользователя:

public class User {
    ...
    public boolean equals(Object other) {
        if (this==other) return true;
        if ( !(other instanceof User) ) return false;
        final User that = (User) other;
        return this.username.equals( that.getUsername() );
    }
    public int hashCode() {
        return username.hashCode();
  }
}

Справочная документация Hibernate обобщает это следующим образом:

" Никогда не используйте идентификатор базы данных для реализации равенства; используйте бизнес-ключ, комбинацию уникальных, обычно неизменяемых, атрибутов . Идентификатор базы данных изменится, если временный объект станет постоянным. Если временный Экземпляр (обычно вместе с отдельными экземплярами) хранится в Set, изменение hashcode нарушает контракт Set. Атрибуты для бизнес-ключей не должны быть такими же стабильными, как первичные ключи базы данных, у вас есть только чтобы гарантировать стабильность, пока объекты находятся в одном наборе. " - 12.1.3. С учетом идентичности объекта

" Рекомендуется реализовать equals() и hashCode(), используя равенство бизнес-ключей . Равенство бизнес-ключей означает, что метод equals() сравнивает только свойства, которые формируют бизнес-ключ. ключ, который идентифицирует наш экземпляр в реальном мире (естественный ключ-кандидат) "- 4.3. Реализация equals () и hashCode ()

Итак, вернемся к первоначальному вопросу:

  • Используйте бизнес-ключ, если это возможно. @Transient атрибуты, скорее всего, не являются частью такого ключа.
  • Если это невозможно, используйте свойства идентификатора, но не забудьте получить значения, назначенные ранее, чтобы добавить объект в List, Map, Set.

Смотри также

0 голосов
/ 09 мая 2012

Исключением может быть то, что оно будет transient, и в то же время вы предоставляете writeObject() и readObject() для обработки.

0 голосов
/ 01 июня 2010

Два типичных использования @Transient и transient, о которых я знаю, это использовать их либо для вещей, которые не могут быть сериализованы / сохранены (например, удаленный ресурс handle ) или вычисленные свойства, которые могут быть восстановлены из других.

Для вычисляемых данных не имеет смысла использовать их в соотношении равенства (equals/hashCode), потому что оно будет избыточным. Значение вычисляется из другого значения, которое уже используется в равенстве. Однако может иметь смысл печатать их в toString (например, базовая цена и коэффициент используются для вычисления фактической цены).

Для не сериализуемые / постоянные данные , это зависит. Я могу представить дескриптор ресурса, который не сериализуем, но вы все равно можете сравнить имя ресурса, которое представляет дескриптор. То же самое для toString, может быть полезна печать имени ресурса дескриптора.

Это были мои 2 цента, но если вы объясните свое конкретное использование @Transient, кто-то может дать лучший совет.

...