27.Как смаппить составной ключ?
UNKNOWNСоставной первичный ключ, также называемый составным ключом, представляет собой комбинацию из двух или более столбцов для формирования первичного ключа таблицы.
В соответствии с JPA, допустимые типы атрибутов для первичного ключа:
- примитивные типы и их обертки;
- строки;
- BigDecimal и BigInteger;
- java.util.Date и java.sql.Date.
В JPA есть требования к составному ключу:
- составной ключ должен быть представлен классом первичного ключа, при этом используется одна из двух аннотаций: @IdClass и @EmbeddedId;
- класс первичного ключа должен быть публичным и иметь публичный конструктор без аргументов;
- класс первичного ключа должен имплементировать маркерный интерфейс Serializable;
- класс первичного ключа должен иметь методы equals и hashCode;
- атрибуты, представляющие поля составного ключа, могут быть базовыми, составными и @ManyToOne, но не могут быть коллекциями или @OneToOne.
Однако, первое правило имеется только в JPA. Hibernate позволяет определять составные идентификаторы без «класса первичного ключа» с помощью нескольких атрибутов с аннотацией @Id.
@IdClass
Допустим, у нас есть таблица с именем Account, и она имеет два столбца - accountNumber и accountType, которые формируют составной ключ. Чтобы обозначить оба этих поля как части составного ключа мы должны создать класс, например, AccountId с этими полями:
public class AccountId implements Serializable {
private String accountNumber;
private String accountType;
// default constructor
public AccountId(String accountNumber, String accountType) {
this.accountNumber = accountNumber;
this.accountType = accountType;
}
// equals() and hashCode()
}
Затем нам нужно аннотировать сущность Account аннотацией @IdClass.
Мы также должны объявить поля из класса AccountId в сущности Account с такими же именами и аннотировать их с помощью @Id:
@Entity
@IdClass(AccountId.class)
public class Account {
@Id
private String accountNumber;
@Id
private String accountType;
// other fields, getters and setters
}
@EmbeddedId
Является альтернативой аннотации @IdClass.
Рассмотрим другой пример, в котором мы должны сохранить некоторую информацию о книге с заголовком и языком в качестве полей первичного ключа. В этом случае класс первичного ключа, BookId, должен быть аннотирован @Embeddable:
@Embeddable
public class BookId implements Serializable {
private String title;
private String language;
// default constructor
public BookId(String title, String language) {
this.title = title;
this.language = language;
}
// getters, equals() and hashCode() methods
}
Затем нам нужно встроить этот класс в сущность Book, используя @EmbeddedId:
@Entity
public class Book {
@EmbeddedId
private BookId bookId;
// constructors, other fields, getters and setters
}
@IdClass vs @EmbeddedId
- с @IdClass нам пришлось указывать столбцы дважды - в AccountId и в Account. Но с @EmbeddedId мы этого не сделали;
- JPQL-запросы с @IdClass проще. С @EmbeddedId, чтобы получить доступ к полю, нам нужно из сущности обратиться к встраиваемому классу и потом к его полю:
SELECT account.accountNumber FROM Account account // с @IdClass
SELECT book.bookId.title FROM Book book // с @EmbeddedId
- @EmbeddedId более подробна, чем @IdClass, поскольку мы можем получить доступ ко всему объекту первичного ключа, используя метод доступа к полю в классе-сущности. Это также дает четкое представление о полях, которые являются частью составного ключа, поскольку все они агрегированы в классе, который доступен только через метод доступа к полям;
- @IdClass может быть предпочтительным выбором по сравнению с @EmbeddedId в ситуациях, когда класс составного первичного ключа поступает из другого модуля или устаревшего кода, а также когда мы не можем его изменить, например, чтобы установить аннотацию @EmbeddedId. Для таких сценариев, где мы не можем изменить класс составного ключа, аннотация @IdClass является единственным выходом;
- если мы собираемся получить доступ к частям составного ключа по отдельности, мы можем использовать @IdClass, но в тех местах, где мы часто используем полный идентификатор в качестве объекта, @EmbeddedId предпочтительнее.
Предыдущий вопрос: 26. Для чего нужны аннотации @Embedded и @Embeddable?
Следующий вопрос: 28. Для чего нужна аннотация @ID? Какие GeneratedValue вы знаете?
Все вопросы по теме: список
Все темы: список
Вопросы/замечания/предложения/нашли ошибку: напишите мне