Свойство @Transient должно использоваться в equals/hashCode/toString?

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

Если я использую эти свойства в equals/hashCode/toString методы?

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

  • Совет?
  • Идеи?
  • Объяснения?
8
задан Rob Hruska 9 May 2012 в 13:31
поделиться

2 ответа

Случай с 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();
  }
}

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

Таким образом, рекомендуемый подход заключается в использовании атрибутов, которые являются частью бизнес-ключа, т.е. комбинации атрибутов, уникальной для каждого экземпляра с одинаковым идентификатором базы данных. Например, для класса 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 резюмирует это следующим образом:

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

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

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

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

См. также

7
ответ дан 5 December 2019 в 21:16
поделиться

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

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

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

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

0
ответ дан 5 December 2019 в 21:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: