Яку анотацію слід використовувати: @IdClass або @EmbeddedId


128

Специфікація JPA(Java Persistent API) має 2 різні способи визначення сукупних ключів сутності: @IdClassта @EmbeddedId.

Я використовую обидва анотації на своїх картах, але це виявляється великим безладом для людей, які не дуже добре знайомі JPA.

Я хочу прийняти лише один спосіб вказати складові ключі. Який із насправді найкращий? Чому?

Відповіді:


86

Я вважаю, що @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. Але більшість випадків розуміння прямо з запиту про те, що дане поле є частиною складеного ключа, є безцінною допомогою.


10
Хоча я згоден з поясненням, поданим вище, я також хотів би додати один унікальний випадок використання, @IdClassхоча я віддаю перевагу @EmbeddedIdв більшості ситуацій (Маю це знати з сеансу Антоніо Гонкальвеса. Що він запропонував, ми можемо використовувати цей @IdClassвипадок у випадку, якщо композитний Клас клавіш недоступний або надходить з іншого модуля чи застарілого коду, куди ми не можемо додати анотацію. У таких сценаріях @IdClassнам буде надано шлях.
Gaurav Rawat

1
Я думаю, що цілком можливо, що випадки використання @IdClassанотації, поданої @Gaurav, є тією самою причиною, що специфікація JPA перераховує обидва способи створення складеного ключа .. @IdClassі@EmbeddidId
kapad

20

Існує три стратегії використання складного первинного ключа:

  • Позначте його як @Embeddableі додайте до вашого об'єкта класу нормальне властивість для цього, позначене символом @Id.
  • Додайте до класу вашої особи звичайну властивість для цього, позначену символом @EmbeddedId.
  • Додайте властивості до класу вашої сутності для всіх його полів, позначте їх @Idі позначте клас вашого об'єкта @IdClass, поставляючи клас вашого класу основного ключа.

Використання @Idкласу, позначеного як @Embeddableтакий, є найбільш природним підходом. @EmbeddableТег може бути використаний для інших ключів вкладених значень в будь-якому випадку. Це дозволяє розглядати складний первинний ключ як єдину властивість, а також дозволяє повторно використовувати @Embeddableклас в інших таблицях.

Наступним найбільш природним підходом є використання @EmbeddedIdтегів. Тут клас первинного ключа не може бути використаний в інших таблицях, оскільки він не є @Embeddableсутністю, але він дозволяє нам трактувати ключ як єдиний атрибут якогось класу.

Нарешті, використання @IdClassта@Id приміток дозволяє нам зіставити складний клас первинного ключа, використовуючи властивості самої сутності, що відповідають іменам властивостей у класі первинного ключа. Імена повинні відповідати (немає механізму, який би перекрив це), і клас первинного ключа повинен виконувати ті самі зобов'язання, що і для двох інших методик. Єдиною перевагою такого підходу є його здатність «приховувати» використання класу первинного ключа від інтерфейсу сукупності, що додає. В @IdClassанотації використовується параметр значення типу Class, який повинен бути класом, який повинен використовуватися як складений первинний ключ. Поля, які відповідають властивостям класу первинного ключа, який слід використовувати, повинні бути помічені @Id.

Довідка: http://www.apress.com/us/book/9781430228509


17

Я виявив приклад, коли мені довелося використовувати EmbeddedId замість IdClass. У цьому сценарії є таблиця приєднання, у якій визначені додаткові стовпці. Я намагався вирішити цю проблему за допомогою IdClass для представлення ключа об'єкта, який явно представляє рядки в таблиці приєднання. Я не міг змусити це працювати таким чином. На щастя, "Наполегливість Java зі сплячим режимом" має розділ, присвячений цій темі. Одне із запропонованих рішень було дуже схожим на моє, але натомість воно використовувало EmbeddedId. Я моделював свої об’єкти після тих, що в книзі, тепер вона поводиться правильно.


13

Наскільки я знаю, якщо ваш композитний ПК містить FK, його легше та простіше у використанні @IdClass

З @EmbeddedIdви повинні визначити відображення для вашої колонки FK двічі, onece в @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 id

...
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;
    }
}

1
Просто не забудьте додати геттерів до свого класу ПК
Соната

@Sonata навіщо нам потрібні геттери? Я спробував це без будь-яких геттерів / сетерів, і це працює чудово
xagaffar

Дякуємо за приклад класу id! Хоча мені все-таки потрібно було впровадити Serializable. Також можна також додати геттерів та сетерів, особливо якщо ваш IDE може автоматично їх генерувати.
Starwarswii

8

Я думаю, що головна перевага полягає в тому, що ми могли б використовувати @GeneratedValueдля id при використанні @IdClass? Я впевнений , що ми не можемо використовувати @GeneratedValueдля @EmbeddedId.


1
Чи не можливо використовувати @GeneratedValue у Embeddedid ??
Кайсер

1
Я успішно використовував його з EmbeddedId, але, очевидно, його підтримка залежить від БД. Це також стосується використання його з IdClass. Специфікація говорить: "Анотація GeneratedValue може використовуватися портативно лише для простих (тобто некомпозитних) первинних ключів."
BPS

4

При використанні композитного ключа не повинно бути @Idвластивості @EmbeddedId.


1

За допомогою 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.