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

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

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

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

7 ответов

Решение

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

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

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

Еще одна разница с @IdClass а также @EmbeddedId когда дело доходит до написания HQL:

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

выберите e.name из списка сотрудников e

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

выберите e.employeeId.name из Сотрудника e

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

С 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

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