Sto usando una leggera modifica di java.util.RegularEnumSet per avere un EnumSet persistente:
@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");
}
}
}
Questa classe è ora la base per tutti i miei set di enumerazioni:
@Embeddable
public class InterestsSet extends PersistentEnumSet<InterestsEnum> {
public InterestsSet() {
super(InterestsEnum.class);
}
}
E quel set che posso usare nella mia entità:
@Entity
public class MyEntity {
@Embedded
@AttributeOverride(name="elements", column=@Column(name="interests"))
private InterestsSet interests = new InterestsSet();
}
Vantaggi:
- Lavorare con un'enumerazione sicura e performante impostata nel codice (vedere
java.util.EnumSet
per una descrizione)
- Il set è solo una colonna numerica nel database
- tutto è semplice JPA (nessun tipo personalizzato specifico del provider )
- facile (e breve) dichiarazione di nuovi campi dello stesso tipo, rispetto alle altre soluzioni
Svantaggi:
- Duplicazione del codice (
RegularEnumSet
e PersistentEnumSet
sono quasi la stessa cosa)
- Puoi racchiudere il risultato di
EnumSet.noneOf(enumType)
nel tuo PersistenEnumSet
, dichiarare AccessType.PROPERTY
e fornire due metodi di accesso che usano la riflessione per leggere e scrivere il elements
campo
- È necessaria una classe set aggiuntiva per ogni classe enum che deve essere memorizzata in un set persistente
- Se il vostro provider di persistenza supporta embeddables senza un costruttore pubblico, si potrebbe aggiungere
@Embeddable
alla PersistentEnumSet
e rilasciare la classe in più ( ... interests = new PersistentEnumSet<>(InterestsEnum.class);
)
- Devi usare un
@AttributeOverride
, come indicato nel mio esempio, se ne hai più di uno PersistentEnumSet
nella tua entità (altrimenti entrambi verrebbero memorizzati nella stessa colonna "elementi")
- L'accesso a
values()
con riflessione nel costruttore non è ottimale (soprattutto quando si guarda alle prestazioni), ma anche le altre due opzioni hanno i loro svantaggi:
- Un'implementazione come
EnumSet.getUniverse()
fa uso di una sun.misc
classe
- Fornire la matrice dei valori come parametro ha il rischio che i valori forniti non siano quelli corretti
- Sono supportate solo le enumerazioni con un massimo di 64 valori (è davvero uno svantaggio?)
- Puoi invece usare BigInteger
- Non è facile utilizzare il campo degli elementi in una query di criteri o JPQL
- È possibile utilizzare operatori binari o una colonna maschera di bit con le funzioni appropriate, se il database lo supporta