persistir mapear enumerated enum annotation java jpa java-ee

java - enumerated - mapear enum jpa



Colección de mapas de JPA de Enums (5)

El enlace en la respuesta de Andy es un gran punto de partida para mapear colecciones de objetos "no-Entidad" en JPA 2, pero no está del todo completo cuando se trata de mapear enumeraciones. Esto es lo que se me ocurrió en su lugar.

@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; }

¿Hay alguna manera en JPA de mapear una colección de Enumerados dentro de la clase Entity? ¿O la única solución es envolver a Enum con otra clase de dominio y usarla para mapear la colección?

@Entity public class Person { public enum InterestsEnum {Books, Sport, etc... } //@??? Collection<InterestsEnum> interests; }

Estoy usando la implementación de Hibernate JPA, pero por supuesto preferiría la solución agnóstica de implementación.


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"); } } // Copy everything else from java.util.RegularEnumSet // ... }

Esta clase ahora es la base de todos mis conjuntos de enumeración:

@Embeddable public class InterestsSet extends PersistentEnumSet<InterestsEnum> { public InterestsSet() { super(InterestsEnum.class); } }

Y ese conjunto que 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 una enumeración segura y de rendimiento configurada 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 (no hay 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 ajustar el resultado de EnumSet.noneOf(enumType) en su PersistenEnumSet , declarar AccessType.PROPERTY y proporcionar dos métodos de acceso que utilizan el reflejo para leer y escribir el campo de elements
  • Se necesita una clase set adicional para cada clase enum que debe almacenarse en un conjunto persistente
    • Si su proveedor de persistencia admite incrustables sin un constructor público, puede agregar @Embeddable a PersistentEnumSet y descartar la clase extra ( ... interests = new PersistentEnumSet<>(InterestsEnum.class); )
  • Debe usar @AttributeOverride , como se indica en mi ejemplo, si tiene más de un PersistentEnumSet en su entidad (de lo contrario, ambos se almacenarían en la misma columna "elements")
  • 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 clase sun.misc
    • Proporcionar la matriz de valores como parámetro tiene el riesgo de que los valores dados no sean los correctos
  • Solo se admiten las enumeraciones con hasta 64 valores (¿eso es realmente un inconveniente?)
    • Podría 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

Las colecciones en JPA se refieren a relaciones uno a muchos o muchos a muchos y solo pueden contener otras entidades. Lo siento, pero tendrías que ajustar esas enumeraciones en una entidad. Si lo piensas bien, necesitarías algún tipo de campo de ID y clave foránea para almacenar esta información de todos modos. Eso es a menos que hagas algo loco como almacenar una lista separada por comas en una cadena (¡no hagas esto!).


Pude lograr esto de esta manera simple:

@ElementCollection(fetch = FetchType.EAGER) Collection<InterestsEnum> interests;

Se requiere una carga ansiosa para evitar el error de inizialización de la carga diferida como se explica here .


usando Hibernate puedes hacer

@CollectionOfElements(targetElement = InterestsEnum.class) @JoinTable(name = "tblInterests", joinColumns = @JoinColumn(name = "personID")) @Column(name = "interest", nullable = false) @Enumerated(EnumType.STRING) Collection<InterestsEnum> interests;