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

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

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

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

Ответ 1

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

Ответ 2

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

Ответ 3

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

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

На другом сайте с использованием @IdClass эту ситуацию можно обрабатывать гораздо проще, как показано в Первичные ключи через отношения OneToOne и 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 id class

...
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;
    }
}

Ответ 4

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

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

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

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

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

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

Ответ 5

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

Ответ 6

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