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