Domanda

Esiste un modo in JPA per mappare una raccolta di Enum all'interno della classe Entity? O l'unica soluzione è avvolgere Enum con un'altra classe di dominio e usarlo per mappare la raccolta?

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

Sto usando l'implementazione di Hibernate JPA, ma ovviamente preferirei una soluzione agnostica di implementazione.

È stato utile?

Soluzione

usando Hibernate puoi farlo

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

Altri suggerimenti

Il link nella risposta di Andy è un ottimo punto di partenza per mappare raccolte di " non Entity " oggetti in JPA 2, ma non è del tutto completo quando si tratta di mappare gli enum. Ecco invece cosa mi è venuto in mente.

@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;
}

Sono stato in grado di farlo in questo modo semplice:

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

È necessario un caricamento ansioso per evitare errori di inizializzazione del caricamento lento, come spiegato qui .

Sto usando una leggera modifica di java.util.RegularEnumSet per avere 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
  // ...
}

Questa classe è ora la base di tutti i miei set di enum:

@Embeddable
public class InterestsSet extends PersistentEnumSet<InterestsEnum> {
  public InterestsSet() {
    super(InterestsEnum.class);
  }
}

E quel set che posso usare nella mia entità:

@Entity
public class MyEntity {
  // ...
  @Embedded
  @AttributeOverride(name="elements", column=@Column(name="interests"))
  private InterestsSet interests = new InterestsSet();
}

Vantaggi:

  • Lavorare con un tipo enum sicuro e performante impostato nel tuo codice (vedi java.util.EnumSet per una descrizione)
  • Il set è solo una colonna numerica nel database
  • tutto è semplice JPA (nessun tipo personalizzato ) specifico del provider
  • facile (e breve) dichiarazione di nuovi campi dello stesso tipo, rispetto alle altre soluzioni

Svantaggi:

  • Duplicazione del codice (RegularEnumSet e PersistentEnumSet sono quasi uguali)
    • Puoi racchiudere il risultato di EnumSet.noneOf(enumType) nel tuo PersistenEnumSet, dichiarare AccessType.PROPERTY e fornire due metodi di accesso che utilizzano la riflessione per leggere e scrivere il campo elements
  • È necessaria una classe set aggiuntiva per ogni classe enum che deve essere archiviata in un set persistente
    • Se il tuo provider di persistenza supporta embeddabili senza un costruttore pubblico, puoi aggiungere @Embeddable a ... interests = new PersistentEnumSet<>(InterestsEnum.class); e rilasciare il classe extra (@AttributeOverride)
  • Devi usare un values(), come indicato nel mio esempio, se ne hai più di uno EnumSet.getUniverse() nella tua entità (altrimenti entrambi sarebbero archiviati nella stessa colonna " elementi ")
  • L'accesso di sun.misc con la riflessione nel costruttore non è ottimale (soprattutto se si guarda alle prestazioni), ma anche le altre due opzioni hanno i loro svantaggi:
    • Un'implementazione come <=> utilizza una <=> classe
    • Fornire l'array dei valori come parametro ha il rischio che i valori indicati non siano quelli corretti
  • Sono supportati solo enum con un massimo di 64 valori (è davvero uno svantaggio?)
    • Invece potresti usare BigInteger
  • Non è facile utilizzare il campo degli elementi in una query di criteri o JPQL
    • È possibile utilizzare operatori binari o una colonna maschera di bit con le funzioni appropriate, se il database lo supporta

Le raccolte in JPA si riferiscono a relazioni uno-a-molti o molti-a-molti e possono contenere solo altre entità. Scusa, ma dovresti avvolgere quelle enumerazioni in un'entità. Se ci pensate, avreste bisogno di una sorta di campo ID e chiave esterna per memorizzare comunque queste informazioni. Questo a meno che tu non faccia qualcosa di folle come memorizzare un elenco separato da virgole in una stringa (non farlo!).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top