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");
}
}
}
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:
- Praca z bezpiecznym i wydajnym zestawem wyliczeń w kodzie (zobacz
java.util.EnumSet
opis)
- Zestaw to tylko jedna kolumna liczbowa w bazie danych
- wszystko jest zwykłym JPA (brak niestandardowych typów specyficznych dla dostawcy )
- łatwe (i krótkie) deklarowanie nowych pól tego samego typu w porównaniu z innymi rozwiązaniami
Wady:
- Powielanie kodu (
RegularEnumSet
i PersistentEnumSet
prawie takie same)
- Możesz zawinąć wynik
EnumSet.noneOf(enumType)
w PersistenEnumSet
, zadeklarować AccessType.PROPERTY
i zapewnić dwie metody dostępu, które używają odbicia do odczytu i zapisu elements
pola
- Dodatkowa klasa zestawu jest wymagana dla każdej klasy wyliczenia, która powinna być przechowywana w zestawie trwałym
- Jeśli dostawca wytrwałość obsługuje embeddables bez konstruktora publicznego, można dodać
@Embeddable
do PersistentEnumSet
i upuść dodatkową klasę ( ... interests = new PersistentEnumSet<>(InterestsEnum.class);
)
- Musisz użyć znaku
@AttributeOverride
, jak podano w moim przykładzie, jeśli masz więcej niż jeden PersistentEnumSet
w swojej encji (w przeciwnym razie oba będą przechowywane w tej samej kolumnie „elementy”)
- Dostęp
values()
z odbiciem w konstruktorze nie jest optymalny (zwłaszcza patrząc na wykonanie), ale dwie inne opcje mają również swoje wady:
- Implementacja taka jak
EnumSet.getUniverse()
wykorzystuje sun.misc
klasę
- Podanie tablicy wartości jako parametru stwarza ryzyko, że podane wartości nie są poprawne
- Obsługiwane są tylko wyliczenia zawierające maksymalnie 64 wartości (czy to naprawdę wada?)
- Zamiast tego możesz użyć BigInteger
- Nie jest łatwo używać pola elementów w zapytaniu kryterialnym lub JPQL
- Możesz użyć operatorów binarnych lub kolumny maski bitowej z odpowiednimi funkcjami, jeśli twoja baza danych to obsługuje