Której adnotacji powinienem użyć: @IdClass czy @EmbeddedId

128

Specyfikacja JPA(Java Persistence API) ma 2 różne sposoby określania kluczy złożonych encji: @IdClassi @EmbeddedId.

Używam obu adnotacji na moich mapowanych obiektach, ale okazuje się, że jest to duży bałagan dla osób, które nie są zbyt dobrze zaznajomione JPA.

Chcę przyjąć tylko jeden sposób określania kluczy złożonych. Który z nich jest naprawdę najlepszy? Czemu?

sakana
źródło

Odpowiedzi:

86

Uważam, że @EmbeddedIdjest to prawdopodobnie bardziej szczegółowe, ponieważ @IdClassnie możesz uzyskać dostępu do całego obiektu klucza podstawowego za pomocą dowolnego operatora dostępu do pola. Używając @EmbeddedIdmożesz zrobić w ten sposób:

@Embeddable class EmployeeId { name, dataOfBirth }
@Entity class Employee {
  @EmbeddedId EmployeeId employeeId;
  ...
}

Daje to jasne pojęcie o polach, które tworzą klucz złożony, ponieważ wszystkie są zagregowane w klasie, do której dostęp uzyskuje się za pośrednictwem operatora dostępu do pola.

Kolejna różnica między @IdClassi w @EmbeddedIdprzypadku pisania HQL:

Z @IdClasstobą piszesz:

wybierz e.name z Employee e

a z @EmbeddedIdtobą trzeba napisać:

wybierz e.employeeId.name z Employee e

Musisz napisać więcej tekstu dla tego samego zapytania. Niektórzy mogą twierdzić, że różni się to od bardziej naturalnego języka, takiego jak promowany przez IdClass. Jednak w większości przypadków zrozumienie bezpośrednio z zapytania, że ​​dane pole jest częścią złożonego klucza, jest nieocenioną pomocą.

Jorge Ferreira
źródło
10
Choć zgadzam się z wyjaśnieniem podanym powyżej chciałbym również dodać jeden unikalny przypadek użycia dla @IdClasschociaż wolę @EmbeddedIdw większości sytuacji (poznali ten z sesji Antonio Goncalves .Co zasugerował to możemy użyć @IdClassw przypadku kompozytu Klasa klucza jest niedostępna lub pochodzi z innego modułu lub starszego kodu, w którym nie możemy dodać adnotacji. W tych scenariuszach @IdClassda nam sposób nasz.
Gaurav Rawat
1
Myślę, że to możliwe, że przypadki użycia @IdClassadnotacji podanej przez @Gaurav są tym samym powodem, dla którego specyfikacja JPA wymienia obie metody tworzenia klucza złożonego .. @IdClassi@EmbeddidId
kapad
20

Istnieją trzy strategie używania złożonego klucza podstawowego:

  • Oznacz to jako @Embeddablei dodaj do swojej klasy jednostki normalną właściwość oznaczoną znakiem @Id.
  • Dodaj do swojej klasy jednostki normalną właściwość oznaczoną @EmbeddedId.
  • Dodaj właściwości do swojej klasy jednostki dla wszystkich jej pól, oznacz je @Idi oznacz klasę jednostki za pomocą @IdClass, podając klasę klasy klucza podstawowego.

Użycie @Idz klasą oznaczoną jako @Embeddablejest najbardziej naturalnym podejściem. @EmbeddableTag może być używany do kluczowych niż podstawowe wartości niezabudowany tak. Umożliwia traktowanie złożonego klucza podstawowego jako pojedynczej właściwości i umożliwia ponowne użycie @Embeddableklasy w innych tabelach.

Kolejnym najbardziej naturalnym podejściem jest użycie @EmbeddedIdtagu. W tym przypadku klasa klucza podstawowego nie może być używana w innych tabelach, ponieważ nie jest ona @Embeddablejednostką, ale pozwala nam traktować klucz jako pojedynczy atrybut jakiejś klasy.

Wreszcie, użycie adnotacji @IdClassi @Idumożliwia nam mapowanie złożonej klasy klucza podstawowego przy użyciu właściwości samej jednostki odpowiadającej nazwom właściwości w klasie klucza podstawowego. Nazwy muszą się zgadzać (nie ma mechanizmu nadpisywania tego), a klasa klucza podstawowego musi honorować te same obowiązki, co w przypadku pozostałych dwóch technik. Jedyną zaletą tego podejścia jest możliwość „ukrycia” użycia klasy klucza podstawowego przed interfejsem encji otaczającej. @IdClassAdnotacja przyjmuje parametr wartość typu Class, która musi być klasa być stosowany jako związek klucz podstawowy. Wszystkie pola, które odpowiadają właściwościom używanej klasy klucza podstawowego, muszą być opatrzone adnotacją @Id.

Źródła: http://www.apress.com/us/book/9781430228509

Adelin
źródło
17

Odkryłem wystąpienie, w którym musiałem użyć EmbeddedId zamiast IdClass. W tym scenariuszu istnieje tabela łączenia, która ma zdefiniowane dodatkowe kolumny. Próbowałem rozwiązać ten problem, używając IdClass do reprezentowania klucza jednostki, która jawnie reprezentuje wiersze w tabeli łączenia. Nie mogłem sprawić, żeby to działało w ten sposób. Na szczęście „Java Persistence With Hibernate” zawiera sekcję poświęconą temu tematowi. Jedno z proponowanych rozwiązań było bardzo podobne do mojego, ale zamiast tego korzystało z EmbeddedId. Wymodelowałem swoje obiekty na podstawie tych z książki, teraz zachowuje się poprawnie.

laz
źródło
13

O ile wiem, jeśli twój złożony PK zawiera FK, jest łatwiejszy i prostszy w użyciu @IdClass

Z tym, @EmbeddedIdże musisz dwukrotnie zdefiniować mapowanie dla swojej kolumny FK, raz w środku @Embeddedablei raz na przykład, @ManyToOnegdzie @ManyToOnema być tylko do odczytu ( @PrimaryKeyJoinColumn), ponieważ nie możesz ustawić jednej kolumny w dwóch zmiennych (możliwe konflikty).
Musisz więc ustawić swój FK za pomocą prostego wpisywania @Embeddedable.

W innej witrynie korzystanie z @IdClasstej sytuacji może być znacznie łatwiejsze, jak pokazano w Kluczach podstawowych przez relacje OneToOne i ManyToOne :

Przykład adnotacji identyfikacyjnej JPA 2.0 ManyToOne

...
@Entity
@IdClass(PhonePK.class)
public class Phone {

    @Id
    private String type;

    @ManyToOne
    @Id
    @JoinColumn(name="OWNER_ID", referencedColumnName="EMP_ID")
    private Employee owner;
    ...
}

Przykład klasy ID 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;
    }
}
Ondrej Bozek
źródło
1
Tylko nie zapomnij dodać getterów do swojej klasy PK
Sonata
@Sonata, dlaczego potrzebujemy programów pobierających? Wypróbowałem to bez żadnych getterów / seterów i działa dobrze
xagaffar
Dzięki za przykład klasy id! Chociaż ostatecznie musiałem również zaimplementować Serializable. Równie dobrze można też dodawać metody pobierające i ustawiające, zwłaszcza jeśli IDE może je automatycznie generować.
Starwarswii
8

Myślę, że główną zaletą jest to, że możemy użyć @GeneratedValueidentyfikatora podczas korzystania z @IdClass? Jestem pewien, że nie możemy użyć @GeneratedValuedo @EmbeddedId.

bertie
źródło
1
Czy nie można użyć @GeneratedValue w Embeddedid?
Kayser
1
Użyłem go z powodzeniem z EmbeddedId, ale najwyraźniej jego obsługa różni się w zależności od DB. Dotyczy to również używania go z IdClass. Specyfikacja mówi: „Adnotacja GeneratedValue może być używana przenośnie tylko dla prostych (tj. Niezłożonych) kluczy podstawowych”.
BPS
4

Klucz złożony nie może mieć @Idwłaściwości, gdy @EmbeddedIdjest używany.

BK
źródło
1

Dzięki EmbeddedId możesz użyć klauzuli IN w HQL, na przykład: FROM Entity WHERE id IN :idsgdzie id to EmbeddedId, podczas gdy osiągnięcie tego samego wyniku z IdClass jest trudne, będziesz chciał zrobić coś takiegoFROM Entity WHERE idPartA = :idPartA0 AND idPartB = :idPartB0 .... OR idPartA = :idPartAN AND idPartB = :idPartBN

Aleks Ben Maza
źródło