Question

Existe-t-il un moyen dans JPA de mapper une collection d’énums au sein de la classe Entity? Ou bien 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 d'Hibernate JPA, mais je préférerais bien sûr une solution indépendante de l'implémentation.

Était-ce utile?

La solution

vous pouvez utiliser Hibernate

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

Autres conseils

Le lien dans la réponse de Andy est un excellent point de départ pour mapper des collections de " non-Entity " dans JPA 2, mais n’est pas assez complet en matière de cartographie d’énums. Voici ce que j’ai trouvé à 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 une erreur de chargement paresseux, comme expliqué ici .

J'utilise une légère modification de java.util.RegularEnumSet pour créer 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'enchères:

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

  • Utilisation d'un enum sécuritaire et performant défini dans votre code (voir java.util.EnumSet pour une description)
  • L'ensemble n'est constitué que d'une colonne numérique dans la base de données
  • tout est JPA simple (aucun type personnalisé spécifique au fournisseur)
  • déclaration facile (et brève) de nouveaux champs du même type, par rapport aux autres solutions

Inconvénients:

  • La duplication de code (RegularEnumSet et PersistentEnumSet sont presque identiques)
    • Vous pouvez insérer le résultat de 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
  • Une classe de jeu supplémentaire est nécessaire pour chaque classe enum devant être stockée dans un jeu persistant
    • Si votre fournisseur de persistance prend en charge les éléments incorporables sans constructeur public, vous pouvez ajouter @Embeddable à ... interests = new PersistentEnumSet<>(InterestsEnum.class); et supprimer le classe supplémentaire (@AttributeOverride)
  • Vous devez utiliser un values(), comme indiqué dans mon exemple, si vous avez plusieurs EnumSet.getUniverse() dans votre entité (sinon les deux seraient stockés dans la même colonne " éléments ")
  • L'accès de sun.misc avec réflexion dans le constructeur n'est pas optimal (surtout lorsque vous regardez la performance), mais les deux autres options ont aussi leurs inconvénients:
    • Une implémentation telle que <=> utilise une <=> classe
    • Fournir le tableau de valeurs en tant que paramètre présente le risque que les valeurs données ne soient pas correctes
  • Seules les énumérations contenant jusqu'à 64 valeurs sont prises en charge (s'agit-il vraiment d'un inconvénient?)
    • Vous pouvez utiliser BigInteger à la place
  • Il n'est pas facile d'utiliser le champ des éléments dans une requête de critère ou dans JPQL
    • Vous pouvez utiliser des opérateurs binaires ou une colonne de masque binaire avec les fonctions appropriées, si votre base de données le prend en charge

Les collections dans JPA font référence à des relations un à plusieurs ou à 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 le faites pas!).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top