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

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

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

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

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
133
0
90 814
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Ответ принят как подходящий

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

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

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

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

С @IdClass вы пишете:

select e.name from Employee e

а с @EmbeddedId вы должны написать:

select e.employeeId.name from Employee e

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

Хотя я согласен с приведенным выше объяснением, я также хотел бы добавить один уникальный вариант использования @IdClass, хотя я предпочитаю @EmbeddedId в большинстве ситуаций (я должен знать это из сеанса Антонио Гонсалвеса. Он предложил использовать @IdClass в в случае, если класс составного ключа недоступен или поступает из другого модуля или устаревшего кода, где мы не можем добавить аннотацию. В этих сценариях @IdClass предоставит нам способ.

Gaurav Rawat 10.02.2016 11:01

Я думаю, что случаи использования аннотации @IdClass, предоставленные @Gaurav, являются той самой причиной, по которой в спецификации JPA перечислены оба метода создания составного ключа. @IdClass и @EmbeddidId

kapad 16.05.2019 12:25

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

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

Разве нельзя использовать @GeneratedValue в Embeddedid ??

Kayser 24.10.2012 12:05

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

BPS 22.07.2015 18:12

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

С @EmbeddedId вам нужно дважды определить сопоставление для столбца FK, один раз в @Embeddedable и один раз, например, для @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;
    }
}

Только не забудьте добавить геттеры в свой класс PK.

Sonata 02.05.2017 16:21

@Sonata зачем нам геттеры? Я пробовал его без каких-либо геттеров / сеттеров, и он отлично работает

xagaffar 11.02.2019 16:59

Спасибо за пример класса id! Хотя в итоге мне пришлось реализовать и Serializable. Также можно добавить геттеры и сеттеры, особенно если ваша IDE может их автоматически генерировать.

Starwarswii 28.06.2019 16:21

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

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

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

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

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

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

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

С 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

Другие вопросы по теме