27.Как смаппить составной ключ?

27.Как смаппить составной ключ?

UNKNOWN

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

В соответствии с JPA, допустимые типы атрибутов для первичного ключа:

  1. примитивные типы и их обертки;
  2. строки;
  3. BigDecimal и BigInteger;
  4. java.util.Date и java.sql.Date.

В JPA есть требования к составному ключу:

  1. составной ключ должен быть представлен классом первичного ключа, при этом используется одна из двух аннотаций: @IdClass и @EmbeddedId;
  2. класс первичного ключа должен быть публичным и иметь публичный конструктор без аргументов;
  3. класс первичного  ключа  должен  имплементировать  маркерный  интерфейс Serializable;
  4. класс первичного ключа должен иметь методы equals и hashCode;
  5. атрибуты, представляющие  поля  составного  ключа,  могут  быть  базовыми, составными и @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 вы знаете?

Все вопросы по теме: список

Все темы: список

Вопросы/замечания/предложения/нашли ошибку: напишите мне

Report Page