Existe-t-il un moyen dans JPA de mapper une collection d'énumérations dans la classe Entity? Ou la seule solution consiste à encapsuler 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 la mise en œuvre Hibernate JPA, mais je préférerais bien sûr une solution indépendante de la mise en œuvre.
java
jpa
jakarta-ee
Gennady Shumakher
la source
la source
Le lien dans la réponse d'Andy est un excellent point de départ pour mapper des collections d'objets "non-Entity" dans JPA 2, mais n'est pas tout à fait complet lorsqu'il s'agit de mapper des énumérations. Voici ce que j'ai proposé à 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; }
la source
@ElementCollection
etCollection<InterestsEnum> interests;
Le reste est potentiellement utile mais inutile. Par exemple,@Enumerated(EnumType.STRING)
met des chaînes lisibles par l'homme dans votre base de données.@Column
'sname
sont implicites. Je voulais juste clarifier ce qui est impliqué lorsque @Column est omis. Et @Enumerated est toujours recommandé car ordinal est une chose horrible par défaut. :)@CollectionTable(name="person_interest", joinColumns = {@JoinColumn(name="person_id")})
J'ai pu accomplir cela de cette manière simple:
@ElementCollection(fetch = FetchType.EAGER) Collection<InterestsEnum> interests;
Un chargement hâtif est nécessaire afin d'éviter une erreur d'initialisation du chargement paresseux comme expliqué ici .
la source
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 de tous mes ensembles d'énumérations:
@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
etPersistentEnumSet
sont presque les mêmes)EnumSet.noneOf(enumType)
dans votrePersistenEnumSet
, déclarerAccessType.PROPERTY
et fournir deux méthodes d'accès qui utilisent la réflexion pour lire et écrire leelements
champ@Embeddable
àPersistentEnumSet
déposer la classe supplémentaire (... interests = new PersistentEnumSet<>(InterestsEnum.class);
)@AttributeOverride
, comme indiqué dans mon exemple, si vous en avez plus d'unPersistentEnumSet
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 les performances), mais les deux autres options ont aussi leurs inconvénients:EnumSet.getUniverse()
utilise unesun.misc
classela source
tl; dr Une solution courte serait la suivante:
@ElementCollection(targetClass = InterestsEnum.class) @CollectionTable @Enumerated(EnumType.STRING) Collection<InterestsEnum> interests;
La réponse longue est qu'avec ces annotations, JPA créera une table qui contiendra la liste d'InterestsEnum pointant vers l'identificateur de classe principal (Person.class dans ce cas).
@ElementCollections spécifie où JPA peut trouver des informations sur Enum
@CollectionTable crée la table qui contient la relation de Person à IntérêtsEnum
@Enumerated (EnumType.STRING) indique à JPA de conserver Enum en tant que chaîne, pourrait être EnumType.ORDINAL
la source
Les collections dans JPA font référence à des relations un-à-plusieurs ou plusieurs-à-plusieurs et elles ne peuvent contenir que d'autres entités. Désolé, mais vous devez intégrer ces énumérations dans une entité. Si vous y réfléchissez, vous aurez besoin d'une sorte de champ d'identification et de clé étrangère pour stocker ces informations de toute façon. 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 cela!).
la source