J'utilise une légère modification de java.util.RegularEnumSet pour avoir un EnumSet persistant:
@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");
}
}
}
Cette classe est maintenant la base de tous mes ensembles d'énumérations:
@Embeddable
public class InterestsSet extends PersistentEnumSet<InterestsEnum> {
public InterestsSet() {
super(InterestsEnum.class);
}
}
Et cet ensemble que je peux utiliser dans mon entité:
@Entity
public class MyEntity {
@Embedded
@AttributeOverride(name="elements", column=@Column(name="interests"))
private InterestsSet interests = new InterestsSet();
}
Avantages:
- Travailler avec une énumération de type sûre et performante dans votre code (voir
java.util.EnumSet
pour une description)
- L'ensemble est juste une colonne numérique dans la base de données
- tout est JPA simple (aucun type personnalisé spécifique au fournisseur )
- déclaration simple (et courte) de nouveaux champs du même type, par rapport aux autres solutions
Désavantages:
- Duplication de code (
RegularEnumSet
et PersistentEnumSet
sont presque les mêmes)
- Vous pouvez envelopper le résultat de
EnumSet.noneOf(enumType)
dans votre PersistenEnumSet
, déclarer AccessType.PROPERTY
et fournir deux méthodes d'accès qui utilisent la réflexion pour lire et écrire le elements
champ
- Une classe d'ensemble supplémentaire est nécessaire pour chaque classe d'énumération qui doit être stockée dans un ensemble persistant
- Si votre fournisseur de persistance prend en charge intégrables sans constructeur public, vous pouvez ajouter
@Embeddable
à PersistentEnumSet
déposer la classe supplémentaire ( ... interests = new PersistentEnumSet<>(InterestsEnum.class);
)
- Vous devez utiliser un
@AttributeOverride
, comme indiqué dans mon exemple, si vous en avez plus d'unPersistentEnumSet
dans votre entité (sinon les deux seraient stockés dans la même colonne "éléments")
- L'accès à
values()
avec réflexion dans le constructeur n'est pas optimal (surtout lorsqu'on regarde les performances), mais les deux autres options ont aussi leurs inconvénients:
- Une implémentation comme
EnumSet.getUniverse()
utilise une sun.misc
classe
- Fournir le tableau de valeurs en tant que paramètre présente le risque que les valeurs données ne soient pas les bonnes
- Seules les énumérations avec jusqu'à 64 valeurs sont prises en charge (est-ce vraiment un inconvénient?)
- Vous pouvez utiliser BigInteger à la place
- Il n'est pas facile d'utiliser le champ des éléments dans une requête de critères ou JPQL
- Vous pouvez utiliser des opérateurs binaires ou une colonne de masque de bits avec les fonctions appropriées, si votre base de données prend en charge cela