Estoy usando una ligera modificación de java.util.RegularEnumSet para tener 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");
}
}
}
Esta clase es ahora la base para todos mis conjuntos de enumeración:
@Embeddable
public class InterestsSet extends PersistentEnumSet<InterestsEnum> {
public InterestsSet() {
super(InterestsEnum.class);
}
}
Y ese conjunto lo puedo usar en mi entidad:
@Entity
public class MyEntity {
@Embedded
@AttributeOverride(name="elements", column=@Column(name="interests"))
private InterestsSet interests = new InterestsSet();
}
Ventajas:
- Trabajar con un conjunto de enumeración seguro y eficaz de tipos en su código (consulte
java.util.EnumSet
para obtener una descripción)
- El conjunto es solo una columna numérica en la base de datos
- todo es JPA simple (sin tipos personalizados específicos del proveedor )
- declaración fácil (y breve) de nuevos campos del mismo tipo, en comparación con las otras soluciones
Inconvenientes:
- Duplicación de código (
RegularEnumSet
y PersistentEnumSet
son casi iguales)
- Puede envolver el resultado de
EnumSet.noneOf(enumType)
en su PersistenEnumSet
, declarar AccessType.PROPERTY
y proporcionar dos métodos de acceso que utilizan la reflexión para leer y escribir el elements
campo
- Se necesita una clase de conjunto adicional para cada clase de enumeración que debe almacenarse en un conjunto persistente
- Si su proveedor de persistencia apoya incrustables sin un constructor público, se podría añadir
@Embeddable
a PersistentEnumSet
y soltar la clase extra ( ... interests = new PersistentEnumSet<>(InterestsEnum.class);
)
- Debe usar un
@AttributeOverride
, como se indica en mi ejemplo, si tiene más de uno PersistentEnumSet
en su entidad (de lo contrario, ambos se almacenarían en la misma columna "elementos")
- El acceso de
values()
con reflejo en el constructor no es óptimo (especialmente cuando se mira el rendimiento), pero las otras dos opciones también tienen sus inconvenientes:
- Una implementación como
EnumSet.getUniverse()
hace uso de una sun.misc
clase
- Proporcionar la matriz de valores como parámetro tiene el riesgo de que los valores dados no sean los correctos
- Solo se admiten enumeraciones con hasta 64 valores (¿es eso realmente un inconveniente?)
- Podrías usar BigInteger en su lugar
- No es fácil usar el campo de elementos en una consulta de criterios o JPQL
- Puede usar operadores binarios o una columna de máscara de bits con las funciones apropiadas, si su base de datos lo admite.