Какую аннотацию мне следует использовать: @IdClass или @EmbeddedId - PullRequest
114 голосов
/ 17 октября 2008

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

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

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

Ответы [ 7 ]

79 голосов
/ 17 октября 2008

Я считаю, что @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. Но в большинстве случаев правильное понимание из запроса того, что данное поле является частью составного ключа, имеет неоценимую помощь.

17 голосов
/ 17 октября 2008

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

14 голосов
/ 14 февраля 2017

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

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

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

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

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

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

11 голосов
/ 19 сентября 2013

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

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

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

Пример JPA 2.0: аннотация id ManyToOne

...
@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;
    }
}
7 голосов
/ 12 января 2011

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

4 голосов
/ 30 августа 2014

Составной ключ не должен иметь свойства @Id, если используется @EmbeddedId.

1 голос
/ 02 апреля 2018

С 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...