¿Hay alguna forma en JPA de mapear una colección de Enums dentro de la clase Entity? ¿O la única solución es envolver 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 una solución independiente de la implementación.
java
jpa
jakarta-ee
Gennady Shumakher
fuente
fuente
El enlace en la respuesta de Andy es un gran punto de partida para mapear colecciones de objetos "que no son de entidad" en JPA 2, pero no es del todo completo cuando se trata de mapear enumeraciones. Esto es lo que se me ocurrió.
@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; }
fuente
@ElementCollection
yCollection<InterestsEnum> interests;
El resto es potencialmente útil pero innecesario. Por ejemplo,@Enumerated(EnumType.STRING)
coloca cadenas legibles por humanos en su base de datos.@Column
'sname
está implícito. Solo quería aclarar lo que está implícito cuando se omite @Column. Y @Enumerated siempre se recomienda ya que ordinal es una cosa horrible para predeterminar. :)@CollectionTable(name="person_interest", joinColumns = {@JoinColumn(name="person_id")})
Pude lograr esto de esta manera simple:
@ElementCollection(fetch = FetchType.EAGER) Collection<InterestsEnum> interests;
Se requiere una carga ansiosa para evitar el error de inicialización de carga diferida, como se explica aquí .
fuente
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 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:
java.util.EnumSet
para obtener una descripción)Inconvenientes:
RegularEnumSet
yPersistentEnumSet
son casi iguales)EnumSet.noneOf(enumType)
en suPersistenEnumSet
, declararAccessType.PROPERTY
y proporcionar dos métodos de acceso que utilizan la reflexión para leer y escribir elelements
campo@Embeddable
aPersistentEnumSet
y soltar la clase extra (... interests = new PersistentEnumSet<>(InterestsEnum.class);
)@AttributeOverride
, como se indica en mi ejemplo, si tiene más de unoPersistentEnumSet
en su entidad (de lo contrario, ambos se almacenarían en la misma columna "elementos")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:EnumSet.getUniverse()
hace uso de unasun.misc
clasefuente
tl; dr Una solución corta sería la siguiente:
@ElementCollection(targetClass = InterestsEnum.class) @CollectionTable @Enumerated(EnumType.STRING) Collection<InterestsEnum> interests;
La respuesta larga es que con estas anotaciones, JPA creará una tabla que contendrá la lista de InterestsEnum apuntando al identificador de clase principal (Person.class en este caso).
@ElementCollections especifica dónde JPA puede encontrar información sobre el Enum
@CollectionTable crea la tabla que mantiene la relación de Person a InterestsEnum
@Enumerated (EnumType.STRING) le dice a JPA que persista el Enum como String, podría ser EnumType.ORDINAL
fuente
Las colecciones en JPA se refieren a relaciones de uno a muchos o de muchos a muchos y solo pueden contener otras entidades. Lo siento, pero tendrías que envolver esas enumeraciones en una entidad. Si lo piensa, necesitará algún tipo de campo de identificación y clave externa para almacenar esta información de todos modos. Eso es a menos que haga algo loco como almacenar una lista separada por comas en una cadena (¡no haga esto!).
fuente