Перейти к основному содержимому

Составные первичные ключи в JPA

· 3 мин. чтения

1. Введение

В этом руководстве мы узнаем о составных первичных ключах и соответствующих аннотациях в JPA.

2. Составные первичные ключи

Составной первичный ключ, также называемый составным ключом, представляет собой комбинацию двух или более столбцов для формирования первичного ключа для таблицы.

В JPA у нас есть два варианта определения составных ключей: аннотации @IdClass и @EmbeddedId .

Чтобы определить составные первичные ключи, мы должны следовать некоторым правилам:

  • Составной класс первичного ключа должен быть общедоступным.
  • Он должен иметь конструктор без аргументов.
  • Он должен определить методы equals() и hashCode() .
  • Он должен быть сериализуемым .

3. Аннотация IdClass

Допустим, у нас есть таблица Account с двумя столбцами, accountNumber и accountType, которые образуют составной ключ. Теперь нам нужно отобразить его в JPA.

В соответствии со спецификацией JPA создадим класс 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()
}

Далее давайте свяжем класс AccountId с сущностью 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
}

4. Аннотация EmbeddedId

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

5. @IdClass против @EmbeddedId

Как мы видим, внешнее различие между ними заключается в том, что с @IdClass нам пришлось указывать столбцы дважды, один раз в AccountId и еще раз в Account; однако с @EmbeddedId мы этого не сделали.

Однако есть и другие компромиссы.

Например, эти различные структуры влияют на запросы JPQL, которые мы пишем.

С @IdClass запрос немного проще:

SELECT account.accountNumber FROM Account account

С @EmbeddedId нам нужно сделать один дополнительный обход:

SELECT book.bookId.title FROM Book book

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

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

6. Заключение

В этой краткой статье мы рассмотрели составные первичные ключи в JPA.

Как всегда, полный код для этой статьи можно найти на Github .