JPA existe-t-il un moyen de mapper une collection d'énums au sein de la classe Entity? Ou la seule solution consiste à envelopper Enum avec une autre classe de domaine et à l'utiliser pour mapper la collection?
@Entity
public class Person {
public enum InterestsEnum {Books, Sport, etc... }
//@???
Collection<InterestsEnum> interests;
}
J'utilise l'implémentation Hibernate JPA, mais je préférerais bien sûr une solution sans implémentation.
en utilisant Hibernate vous pouvez faire
@CollectionOfElements(targetElement = InterestsEnum.class)
@JoinTable(name = "tblInterests", joinColumns = @JoinColumn(name = "personID"))
@Column(name = "interest", nullable = false)
@Enumerated(EnumType.STRING)
Collection<InterestsEnum> interests;
Le lien indiqué par Andy est un excellent point de départ pour mapper des collections d’objets "non-Entity" dans JPA 2, mais n’est pas assez complet en matière de mappage. Voici ce que je suis venu à la place.
@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;
}
J'ai pu accomplir cela de cette manière simple:
@ElementCollection(fetch = FetchType.EAGER)
Collection<InterestsEnum> interests;
Un chargement rapide est nécessaire pour éviter les erreurs d’initialisation du chargement paresseux, comme expliqué ici .
J'utilise une légère modification de Java.util.RegularEnumSet pour avoir un EnumSet persistant:
@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
// ...
}
Cette classe est maintenant la base pour tous mes ensembles enum:
@Embeddable
public class InterestsSet extends PersistentEnumSet<InterestsEnum> {
public InterestsSet() {
super(InterestsEnum.class);
}
}
Et cet ensemble que je peux utiliser dans mon entité:
@Entity
public class MyEntity {
// ...
@Embedded
@AttributeOverride(name="elements", column=@Column(name="interests"))
private InterestsSet interests = new InterestsSet();
}
Avantages:
Java.util.EnumSet
pour une description)Désavantages:
RegularEnumSet
et PersistentEnumSet
sont presque les mêmes) EnumSet.noneOf(enumType)
dans votre PersistenEnumSet
, déclarer AccessType.PROPERTY
et fournir deux méthodes d'accès utilisant la réflexion pour lire et écrire le champ elements
.@Embeddable
à PersistentEnumSet
et supprimer la classe Extra (... interests = new PersistentEnumSet<>(InterestsEnum.class);
).@AttributeOverride
, comme indiqué dans mon exemple, si vous avez plus d'une PersistentEnumSet
dans votre entité (sinon les deux seraient stockés dans la même colonne "éléments")values()
avec réflexion dans le constructeur n’est pas optimal (surtout lorsqu’on regarde la performance), mais les deux autres options ont aussi leurs inconvénients: EnumSet.getUniverse()
utilise une classe Sun.misc
Les collections dans JPA font référence à des relations un à plusieurs ou plusieurs à plusieurs et ne peuvent contenir que d'autres entités. Désolé, mais vous devez envelopper ces enums dans une entité. Si vous y réfléchissez, vous aurez de toute façon besoin d’un champ d’identification et d’une clé étrangère pour stocker ces informations. C’est à moins que vous ne fassiez quelque chose de fou comme stocker une liste séparée par des virgules dans une chaîne (ne faites pas ceci!).