Czy w JPA istnieje sposób mapowania kolekcji wyliczeń w klasie Entity? A może jedynym rozwiązaniem jest opakowanie Enum z inną klasą domeny i użycie jej do zmapowania kolekcji?
@Entity
public class Person {
public enum InterestsEnum {Books, Sport, etc... }
//@???
Collection<InterestsEnum> interests;
}
Używam implementacji Hibernate JPA, ale oczywiście wolałbym rozwiązanie agnostyczne.
java
jpa
jakarta-ee
Giennadij Szumakher
źródło
źródło
Łącze w odpowiedzi Andy'ego jest świetnym punktem wyjścia do mapowania kolekcji obiektów „nie-Encji” w JPA 2, ale nie jest kompletne, jeśli chodzi o mapowanie wyliczeń. Oto, co wymyśliłem zamiast tego.
@Entity public class Person { @ElementCollection(targetClass=InterestsEnum.class) @Enumerated(EnumType.STRING) // Possibly optional (I'm not sure) but defaults to ORDINAL. @CollectionTable(name="person_interest") @Column(name="interest") // Column name in person_interest Collection<InterestsEnum> interests; }
źródło
@ElementCollection
iCollection<InterestsEnum> interests;
reszta jest potencjalnie przydatna, ale niepotrzebna. Na przykład@Enumerated(EnumType.STRING)
umieszcza w bazie danych ciągi czytelne dla człowieka.@Column
„sname
jest dorozumiany. Chciałem tylko wyjaśnić, co jest implikowane, gdy pominięto @Column. A @Enumerated jest zawsze zalecane, ponieważ liczba porządkowa jest okropną rzeczą do domyślnej. :)@CollectionTable(name="person_interest", joinColumns = {@JoinColumn(name="person_id")})
Udało mi się to osiągnąć w ten prosty sposób:
@ElementCollection(fetch = FetchType.EAGER) Collection<InterestsEnum> interests;
Zachłanne ładowanie jest wymagane, aby uniknąć leniwego błędu inicjalizacji ładowania, jak wyjaśniono tutaj .
źródło
Używam niewielkiej modyfikacji java.util.RegularEnumSet, aby mieć trwały zestaw EnumSet:
@MappedSuperclass @Access(AccessType.FIELD) public class PersistentEnumSet<E extends Enum<E>> extends AbstractSet<E> { private long elements; @Transient private final Class<E> elementType; @Transient private final E[] universe; public PersistentEnumSet(final Class<E> elementType) { this.elementType = elementType; try { this.universe = (E[]) elementType.getMethod("values").invoke(null); } catch (final ReflectiveOperationException e) { throw new IllegalArgumentException("Not an enum type: " + elementType, e); } if (this.universe.length > 64) { throw new IllegalArgumentException("More than 64 enum elements are not allowed"); } } // Copy everything else from java.util.RegularEnumSet // ... }
Ta klasa jest teraz podstawą dla wszystkich moich zestawów wyliczeń:
@Embeddable public class InterestsSet extends PersistentEnumSet<InterestsEnum> { public InterestsSet() { super(InterestsEnum.class); } }
I ten zestaw, którego mogę używać w mojej jednostce:
@Entity public class MyEntity { // ... @Embedded @AttributeOverride(name="elements", column=@Column(name="interests")) private InterestsSet interests = new InterestsSet(); }
Zalety:
java.util.EnumSet
opis)Wady:
RegularEnumSet
iPersistentEnumSet
prawie takie same)EnumSet.noneOf(enumType)
wPersistenEnumSet
, zadeklarowaćAccessType.PROPERTY
i zapewnić dwie metody dostępu, które używają odbicia do odczytu i zapisuelements
pola@Embeddable
doPersistentEnumSet
i upuść dodatkową klasę (... interests = new PersistentEnumSet<>(InterestsEnum.class);
)@AttributeOverride
, jak podano w moim przykładzie, jeśli masz więcej niż jedenPersistentEnumSet
w swojej encji (w przeciwnym razie oba będą przechowywane w tej samej kolumnie „elementy”)values()
z odbiciem w konstruktorze nie jest optymalny (zwłaszcza patrząc na wykonanie), ale dwie inne opcje mają również swoje wady:EnumSet.getUniverse()
wykorzystujesun.misc
klasęźródło
tl; dr Krótkie rozwiązanie byłoby następujące:
@ElementCollection(targetClass = InterestsEnum.class) @CollectionTable @Enumerated(EnumType.STRING) Collection<InterestsEnum> interests;
Długą odpowiedzią jest to, że dzięki tym adnotacjom JPA utworzy jedną tabelę, która będzie zawierała listę InterestsEnum wskazującą na główny identyfikator klasy (w tym przypadku Person.class).
@ElementCollections określ, gdzie JPA może znaleźć informacje o Enum
@CollectionTable utwórz tabelę zawierającą relację od Person do InterestsEnum
@Enumerated (EnumType.STRING) mówi JPA, aby zachować Enum jako ciąg, może to być EnumType.ORDINAL
źródło
Kolekcje w JPA odnoszą się do relacji jeden do wielu lub wiele do wielu i mogą zawierać tylko inne jednostki. Przepraszamy, ale musisz opakować te wyliczenia w encję. Jeśli się nad tym zastanowić, i tak potrzebowałbyś jakiegoś pola ID i klucza obcego do przechowywania tych informacji. Chyba że zrobisz coś szalonego, na przykład przechowywanie listy oddzielonej przecinkami w łańcuchu (nie rób tego!).
źródło