2009-01-06 12 views
64

¿Hay alguna forma en JPA de asignar 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?Colección de mapas JPA de Enumeraciones

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

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

Respuesta

86

usando Hibernate que puede hacer

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

En caso de que alguien lea esto ahora ... @CollectionOfElements ahora está en desuso, en su lugar use: @ElementCollection –

+2

Puede encontrar una muestra en el contestador de esta pregunta: http://stackoverflow.com/q/3152787/363573 – Stephan

+0

Dado que usted He mencionado Hibernate, pensé que esta respuesta podría haber sido específica del proveedor, pero no creo que sea así a menos que el uso de JoinTable cause problemas en otras implementaciones. Por lo que he visto, creo que debe usarse CollectionTable en su lugar. Eso es lo que usé en mi respuesta y funciona para mí (aunque sí, también estoy usando Hibernate en este momento). – spaaarky21

0

Las colecciones en JPA se refieren a las 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!).

+7

Esto solo es válido para JPA 1.0. En JPA 2.0 puede usar la anotación @ElementCollection como se muestra arriba. – rustyx

45

El enlace en la respuesta de Andy es un gran punto de partida para las colecciones de mapeo de objetos "no entidad" en APP 2, pero no es del todo completa cuando se trata de mapeo de enums. 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; 
} 
+3

Lamentablemente, un "administrador" decidió eliminar esa respuesta sin dar una razón (sobre el par de el curso aquí). Para la referencia es http://www.datanucleus.org/products/accessplatform_3_0/jpa/orm/one_to_many_collection.html#join_nonpc – DataNucleus

+1

Todo lo que realmente necesita de esto es '@ ElementCollection' y' Collection interests; ' El resto es potencialmente útil pero innecesario. Por ejemplo, '@Enumerated (EnumType.STRING)' pone cadenas legibles en su base de datos. – CorayThan

+2

Tiene razón: en este ejemplo, puede confiar en que se implique el 'nombre 'de' @ Column'. Solo quería aclarar qué está implícito cuando se omite @Column. Y siempre se recomienda @Enumerated, ya que ordinal es algo horrible por defecto. :) – spaaarky21

3

Estoy usando una ligera modificación de java.util.RegularEnumSet para tener una 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 que puedo usar en mi entidad:

@Entity 
public class MyEntity { 
    // ... 
    @Embedded 
    @AttributeOverride(name="elements", [email protected](name="interests")) 
    private InterestsSet interests = new InterestsSet(); 
} 

Ventajas:

  • Trabajar con un seguro tipo y el conjunto de enumeración performant en el código (ver java.util.EnumSet para una descripción)
  • El conjunto es sólo una columna numérica en la base de datos
  • todo es normal APP (sin proveedor específicos tipos personalizados)
  • fácil (y corto) declaración de nuevos campos del mismo tipo, en comparación con las otras soluciones

inconvenientes:

  • la duplicación de código (RegularEnumSet y PersistentEnumSet son casi lo mismo)
    • Usted 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 campo elements
  • Se necesita una clase de conjunto adicional para cada clase enum que debe almacenarse en un conjunto persistente
    • Si su proveedor de persistencia lo apoya ts incrustables sin un constructor público, se podría añadir a @EmbeddablePersistentEnumSet y soltar el clase extra (... interests = new PersistentEnumSet<>(InterestsEnum.class);)
  • Debe utilizar un @AttributeOverride, como se da en mi ejemplo, si usted tiene más de un 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 :
    • Un implemento ación como EnumSet.getUniverse() hace uso de un sun.misc clase
    • Proporcionar la matriz como valores de parámetros tiene el riesgo de que los valores dados no son los correctos
  • Sólo las enumeraciones con un máximo de 64 valores son compatibles (es que realmente una ¿retirarse?)
    • Usted podría utilizar BigInteger lugar
  • No es fácil de utilizar el campo de los elementos en una consulta de criterios o JPQL
    • Se podría utilizar operadores binarios o una columna de máscara de bits con las funciones adecuadas, si su base de datos admite que
1

pude lograr esto de esta manera sencilla:

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

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

+0

funcionó muy bien :) –

Cuestiones relacionadas