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




Я считаю, что @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, предоставленные @Gaurav, являются той самой причиной, по которой в спецификации JPA перечислены оба метода создания составного ключа. @IdClass и @EmbeddidId
Я обнаружил случай, когда мне пришлось использовать EmbeddedId вместо IdClass. В этом сценарии есть объединенная таблица, в которой определены дополнительные столбцы. Я попытался решить эту проблему, используя IdClass для представления ключа объекта, который явно представляет строки в объединенной таблице. Я не мог заставить это работать таким образом. К счастью, в «Java Persistence With Hibernate» есть раздел, посвященный этой теме. Одно из предложенных решений было очень похоже на мое, но вместо него использовалось EmbeddedId. Я смоделировал свои объекты по образцу тех, что есть в книге, теперь они ведут себя правильно.
Я думаю, что главное преимущество в том, что мы могли бы использовать @GeneratedValue в качестве идентификатора при использовании @IdClass? Я уверен, что мы не можем использовать @GeneratedValue для @EmbeddedId.
Разве нельзя использовать @GeneratedValue в Embeddedid ??
Я успешно использовал его с EmbeddedId, но, очевидно, его поддержка зависит от БД. Это также верно в отношении использования его с IdClass. В спецификации сказано: «Аннотацию GeneratedValue можно переносить только для простых (то есть несоставных) первичных ключей».
Насколько я знаю, если ваш составной ПК содержит 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 зачем нам геттеры? Я пробовал его без каких-либо геттеров / сеттеров, и он отлично работает
Спасибо за пример класса id! Хотя в итоге мне пришлось реализовать и Serializable. Также можно добавить геттеры и сеттеры, особенно если ваша IDE может их автоматически генерировать.
При использовании @Id составной ключ не должен иметь свойства @EmbeddedId.
Есть три стратегии использования составного первичного ключа:
@Embeddable и добавьте в свой класс сущности обычное свойство для него, помеченное @Id.@EmbeddedId.@Id и пометьте свой класс сущности с помощью @IdClass, предоставив класс вашего класса первичного ключа.Использование @Id с классом, обозначенным как @Embeddable, является наиболее естественным подходом. Тег @Embeddable в любом случае можно использовать для встраиваемых значений, отличных от первичного ключа. Он позволяет рассматривать составной первичный ключ как отдельное свойство и позволяет повторно использовать класс @Embeddable в других таблицах.
Следующим наиболее естественным подходом является использование тега @EmbeddedId. Здесь класс первичного ключа не может использоваться в других таблицах, поскольку он не является объектом @Embeddable, но он позволяет нам рассматривать ключ как
единственный атрибут некоторого класса.
Наконец, использование аннотаций @IdClass и @Id позволяет нам отображать составной класс первичного ключа, используя свойства самой сущности, соответствующие именам свойств в классе первичного ключа. Имена должны соответствовать (нет механизма для переопределения этого), и класс первичного ключа должен соблюдать те же обязательства, что и с двумя другими методами. Единственное преимущество этого подхода - его способность «скрыть» использование класса первичного ключа от интерфейса включающей сущности. Аннотация @IdClass принимает параметр значения типа Class, который должен быть классом, который будет использоваться в качестве составного первичного ключа. Поля, которые соответствуют свойствам используемого класса первичного ключа, должны быть аннотированы @Id.
С 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
Хотя я согласен с приведенным выше объяснением, я также хотел бы добавить один уникальный вариант использования
@IdClass, хотя я предпочитаю@EmbeddedIdв большинстве ситуаций (я должен знать это из сеанса Антонио Гонсалвеса. Он предложил использовать@IdClassв в случае, если класс составного ключа недоступен или поступает из другого модуля или устаревшего кода, где мы не можем добавить аннотацию. В этих сценариях@IdClassпредоставит нам способ.