Pregunta

¿Hay alguna forma en JPA de asignar una colección de enumeraciones dentro de la clase Entidad?¿O la única solución es empaquetar 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.

¿Fue útil?

Solución

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;

Otros consejos

El enlace en la respuesta de Andy es un excelente punto de partida para mapear colecciones de " non-Entity " objetos en JPA 2, pero no está completamente completo cuando se trata de enumeraciones de mapeo. 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;
}

Pude lograr esto de esta manera simple:

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

Se requiere una carga ansiosa para evitar un error de inicialización de carga diferida como se explica aquí .

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 enumeraciones:

@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 una enumeración de tipo segura y de alto rendimiento establecida en su código (consulte java.util.EnumSet para una descripción)
  • El conjunto es solo una columna numérica en la base de datos.
  • todo es JPA simple (sin proveedor específico tipos personalizados)
  • declaración fácil (y breve) de nuevos campos del mismo tipo, en comparación con las otras soluciones

Desventajas:

  • Duplicación de código (RegularEnumSet y PersistentEnumSet son casi iguales)
    • Podrías envolver el resultado de EnumSet.noneOf(enumType) en tus PersistenEnumSet, declarar AccessType.PROPERTY y proporciona 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 admite integrables sin un constructor público, puede agregar @Embeddable a PersistentEnumSet y deja caer la clase extra (... interests = new PersistentEnumSet<>(InterestsEnum.class);)
  • Debes utilizar un @AttributeOverride, como se muestra en mi ejemplo, si tienes 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 reflexión en el constructor no es óptimo (especialmente cuando se observa el rendimiento), pero las otras dos opciones también tienen sus inconvenientes:
    • Una implementación como EnumSet.getUniverse() hace uso de un sun.misc clase
    • Proporcionar la matriz de valores como parámetro tiene el riesgo de que los valores dados no sean los correctos.
  • Sólo se admiten enumeraciones con hasta 64 valores (¿es eso realmente un inconveniente?)
    • Podrías usar BigInteger en su lugar
  • No es fácil utilizar el campo elementos en una consulta de criterios o JPQL
    • Puede utilizar 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 sentimos, pero necesitarías incluir esas enumeraciones en una entidad. Si lo piensa, necesitaría 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!).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top