Какую аннотацию использовать: @IdClass или @EmbeddedId

129

В JPAспецификации (Java Persistence API) есть 2 разных способа указать составные ключи сущности: @IdClassи @EmbeddedId.

Я использую обе аннотации для отображаемых объектов, но это оказывается большим беспорядком для людей, которые не очень знакомы с ними JPA.

Я хочу использовать только один способ указания составных ключей. Какой на самом деле лучший? Зачем?

Сакана
источник

Ответы:

87

Я считаю, что @EmbeddedIdэто, вероятно, более многословно, потому что @IdClassвы не можете получить доступ ко всему объекту первичного ключа с помощью любого оператора доступа к полю. С помощью @EmbeddedIdвы можете сделать так:

@Embeddable class EmployeeId { name, dataOfBirth }
@Entity class Employee {
  @EmbeddedId EmployeeId employeeId;
  ...
}

Это дает четкое представление о полях, составляющих составной ключ, поскольку все они агрегированы в классе, доступ к которому осуществляется через оператор доступа к полю.

Еще одно отличие от @IdClassи @EmbeddedIdзаключается в написании HQL:

С @IdClassвами напишите:

выберите e.name из Employee e

и при этом @EmbeddedIdнадо написать:

выберите e.employeeId.name из Employee e

Вам нужно написать больше текста для того же запроса. Некоторые могут возразить, что это отличается от более естественного языка, подобного тому, который продвигается IdClass. Но в большинстве случаев понимание того, что данное поле является частью составного ключа, прямо из запроса является неоценимой помощью.

Хорхе Феррейра
источник
10
Хотя я согласен с приведенным выше объяснением, я также хотел бы добавить один уникальный вариант использования, @IdClassхотя я предпочитаю @EmbeddedIdв большинстве ситуаций (я должен знать это из сеанса Антонио Гонсалвеса. Он предложил использовать @IdClassв случае, если составной ключевой класс недоступен или поступает из другого модуля или устаревшего кода, в который мы не можем добавить аннотацию. В этих сценариях @IdClassнам будет проще наш.
Гаурав Рават,
1
Я думаю, что случаи использования @IdClassаннотации, предоставленной @Gaurav, являются той самой причиной, по которой в спецификации JPA перечислены оба метода создания составного ключа .. @IdClassи@EmbeddidId
kapad
20

Есть три стратегии использования составного первичного ключа:

  • Отметьте его как @Embeddableи добавьте в свой класс сущности обычное свойство для него, отмеченное значком @Id.
  • Добавьте в свой класс сущности обычное свойство для него, отмеченное значком @EmbeddedId.
  • Добавьте свойства в свой класс сущности для всех его полей, пометьте их @Idи пометьте свой класс сущности с помощью @IdClass, предоставляя класс вашего класса первичного ключа.

Использование @Idс классом, помеченным как, @Embeddableявляется наиболее естественным подходом. @EmbeddableТег может быть использован для других ключей вкладываемых значений в любом случае. Он позволяет рассматривать составной первичный ключ как отдельное свойство и разрешает повторное использование @Embeddableкласса в других таблицах.

Следующим наиболее естественным подходом является использование @EmbeddedIdтега. Здесь класс первичного ключа не может использоваться в других таблицах, поскольку он не является @Embeddableсущностью, но он позволяет нам рассматривать ключ как отдельный атрибут некоторого класса.

Наконец, использование из @IdClassи @Idаннотаций позволяет сопоставить класс первичного ключа соединения , используя свойства самого объекта , соответствующие имена свойств в классе первичного ключа. Имена должны соответствовать (нет механизма для переопределения этого), и класс первичного ключа должен соблюдать те же обязательства, что и с двумя другими методами. Единственным преимуществом этого подхода является его способность «скрыть» использование класса первичного ключа от интерфейса включающей сущности. @IdClassАннотаций принимает параметр значения типа класса, который должен быть класс для использования в качестве составного первичного ключа. Поля, которые соответствуют свойствам используемого класса первичного ключа, должны быть аннотированы @Id.

Ссылка: http://www.apress.com/us/book/9781430228509

Аделин
источник
17

Я обнаружил случай, когда мне пришлось использовать EmbeddedId вместо IdClass. В этом сценарии есть объединенная таблица, в которой определены дополнительные столбцы. Я попытался решить эту проблему, используя IdClass для представления ключа объекта, который явно представляет строки в объединенной таблице. Я не мог заставить это работать таким образом. К счастью, в «Java Persistence With Hibernate» есть раздел, посвященный этой теме. Одно из предложенных решений было очень похоже на мое, но вместо него использовалось EmbeddedId. Я смоделировал свои объекты по образцу тех, что указаны в книге, теперь они ведут себя правильно.

Лаз
источник
13

Насколько я знаю, если ваш составной ПК содержит FK, его проще и проще использовать @IdClass

При этом @EmbeddedIdвам нужно определить сопоставление для столбца FK дважды, один @Embeddedableраз и один раз для as, т.е. @ManyToOneгде @ManyToOneдолжно быть только чтение ( @PrimaryKeyJoinColumn), потому что у вас не может быть одного столбца, установленного в двух переменных (возможные конфликты).
Таким образом, вы должны установить свой FK, используя простой ввод @Embeddedable.

На другом сайте с @IdClassэтой ситуацией можно справиться намного проще, как показано в разделе Первичные ключи через отношения OneToOne и ManyToOne :

Пример аннотации идентификатора ManyToOne в JPA 2.0

...
@Entity
@IdClass(PhonePK.class)
public class Phone {

    @Id
    private String type;

    @ManyToOne
    @Id
    @JoinColumn(name="OWNER_ID", referencedColumnName="EMP_ID")
    private Employee owner;
    ...
}

Пример класса идентификатора JPA 2.0

...
public class PhonePK {
    private String type;
    private long owner;

    public PhonePK() {}

    public PhonePK(String type, long owner) {
        this.type = type;
        this.owner = owner;
    }

    public boolean equals(Object object) {
        if (object instanceof PhonePK) {
            PhonePK pk = (PhonePK)object;
            return type.equals(pk.type) && owner == pk.owner;
        } else {
            return false;
        }
    }

    public int hashCode() {
        return type.hashCode() + owner;
    }
}
Ондрей Бозек
источник
1
Только не забудьте добавить геттеры в свой класс PK
Соната
@Sonata зачем нам геттеры? Я пробовал его без каких-либо геттеров / сеттеров, и он отлично работает
xagaffar
Спасибо за пример класса id! Хотя в конечном итоге мне также пришлось реализовать Serializable. Также можно добавить геттеры и сеттеры, особенно если ваша IDE может их автоматически генерировать.
Starwarswii
8

Я думаю, что основным преимуществом является то, что мы могли бы использовать @GeneratedValueидентификатор при использовании @IdClass? Я уверен, что мы не можем использовать @GeneratedValueдля @EmbeddedId.

Берти
источник
1
Разве нельзя использовать @GeneratedValue в Embeddedid ??
Kayser
1
Я успешно использовал его с EmbeddedId, но, очевидно, его поддержка зависит от БД. Это также верно в отношении использования его с IdClass. В спецификации сказано: «Аннотацию GeneratedValue можно переносимо использовать только для простых (т. Е. Несоставных) первичных ключей».
BPS
4

При использовании составной ключ не должен иметь @Idсвойства @EmbeddedId.

BK
источник
1

С EmbeddedId вы можете использовать предложение IN в HQL, например: FROM Entity WHERE id IN :idsгде id - это EmbeddedId, тогда как получить тот же результат с IdClass больно, вы захотите сделать что-то вродеFROM Entity WHERE idPartA = :idPartA0 AND idPartB = :idPartB0 .... OR idPartA = :idPartAN AND idPartB = :idPartBN

Алекс Бен Маза
источник