Raccolta di mappe JPA di Enums


92

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

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

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

Risposte:


112

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;

141
Nel caso qualcuno lo legga ora ... @CollectionOfElements è ora deprecato, usa invece: @ElementCollection

2
Puoi trovare un esempio nella risposta a questa domanda: stackoverflow.com/q/3152787/363573
Stephan

1
Dato che hai menzionato Hibernate, ho pensato che questa risposta potesse essere specifica del fornitore, ma non credo che lo sia a meno che l'uso di JoinTable non causi problemi in altre implementazioni. Da quello che ho visto, credo che dovrebbe essere usato CollectionTable. Questo è quello che ho usato nella mia risposta e funziona per me (anche se sì, sto usando anche Hibernate in questo momento.)
spaaarky21

So che questo è un vecchio thread, ma stiamo implementando lo stesso tipo di cose usando javax.persistence. Quando aggiungiamo: @ElementCollection (targetClass = Roles.class) @CollectionTable (name = "USER_ROLES", joinColumns = @ JoinColumn (name = "USER_ID")) @Column (name = "ROLE", nullable = false) @Enumerated ( EnumType.STRING) private Set <Roles> ruoli; alla nostra tabella Utente, le cose si sfaldano nell'intero pacchetto del modello. Nel nostro oggetto User, anche un errore sul generatore @Id ... @ GeneratedValue della chiave primaria e il primo @OneToMany generano errori sciocchi durante la compilazione.
LinuxLars

Per quello che vale - gli errori che vedo sono un bug - issues.jboss.org/browse/JBIDE-16016
LinuxLars

65

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

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

4
Purtroppo alcuni "admin" hanno deciso di cancellare quella risposta senza dare una ragione (circa il valore del corso qui). Per riferimento è datanucleus.org/products/accessplatform_3_0/jpa/orm/…
DataNucleus

2
Tutto ciò di cui hai effettivamente bisogno è @ElementCollectione Collection<InterestsEnum> interests; Il resto è potenzialmente utile ma non necessario. Ad esempio, @Enumerated(EnumType.STRING)inserisce stringhe leggibili dall'uomo nel database.
Coray,

2
Hai ragione: in questo esempio, puoi fare affidamento sul fatto @Columnche namesia implicito. Volevo solo chiarire cosa è implicito quando @Column viene omesso. E @Enumerated è sempre consigliato poiché ordinale è una cosa orribile per impostazione predefinita. :)
spaaarky21

Penso che valga la pena ricordare che in realtà hai bisogno della tabella
person_interest

Ho dovuto aggiungere il parametro joinColumn per farlo funzionare@CollectionTable(name="person_interest", joinColumns = {@JoinColumn(name="person_id")})
Tiago

8

Sono stato in grado di farlo in questo modo semplice:

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

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


5

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 per tutti i miei set di enumerazioni:

@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'enumerazione sicura e performante impostata nel codice (vedere java.util.EnumSetper 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 ( RegularEnumSete PersistentEnumSetsono quasi la stessa cosa)
    • Puoi racchiudere il risultato di EnumSet.noneOf(enumType)nel tuo PersistenEnumSet, dichiarare AccessType.PROPERTYe fornire due metodi di accesso che usano la riflessione per leggere e scrivere il elementscampo
  • È necessaria una classe set aggiuntiva per ogni classe enum che deve essere memorizzata in un set persistente
    • Se il vostro provider di persistenza supporta embeddables senza un costruttore pubblico, si potrebbe aggiungere @Embeddablealla PersistentEnumSete rilasciare la classe in più ( ... interests = new PersistentEnumSet<>(InterestsEnum.class);)
  • Devi usare un @AttributeOverride, come indicato nel mio esempio, se ne hai più di uno PersistentEnumSetnella tua entità (altrimenti entrambi verrebbero memorizzati nella stessa colonna "elementi")
  • L'accesso a values()con riflessione nel costruttore non è ottimale (soprattutto quando si guarda alle prestazioni), ma anche le altre due opzioni hanno i loro svantaggi:
    • Un'implementazione come EnumSet.getUniverse()fa uso di una sun.miscclasse
    • Fornire la matrice dei valori come parametro ha il rischio che i valori forniti non siano quelli corretti
  • Sono supportate solo le enumerazioni con un massimo di 64 valori (è davvero uno svantaggio?)
    • Puoi invece 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

4

tl; dr Una breve soluzione sarebbe la seguente:

@ElementCollection(targetClass = InterestsEnum.class)
@CollectionTable
@Enumerated(EnumType.STRING)
Collection<InterestsEnum> interests;

La risposta lunga è che con queste annotazioni JPA creerà una tabella che conterrà l'elenco di InterestsEnum che punta all'identificatore della classe principale (Person.class in questo caso).

@ElementCollections specifica dove JPA può trovare informazioni su Enum

@CollectionTable crea la tabella che contiene la relazione da Person a InterestsEnum

@Enumerated (EnumType.STRING) dice a JPA di rendere persistente l'Enum come stringa, potrebbe essere EnumType.ORDINAL


In questo caso non posso modificare questa raccolta perché salva come PersistenceSet che è immutabile.
Nicolazz92

Errore mio. Possiamo cambiare questo set con un setter.
Nicolazz92

0

Le raccolte in JPA si riferiscono a relazioni uno-a-molti o molti-a-molti e possono contenere solo altre entità. Spiacenti, ma avresti bisogno di racchiudere quelle enumerazioni in un'entità. Se ci pensi, avresti 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!).


8
Questo è valido solo per JPA 1.0. In JPA 2.0 è possibile utilizzare l'annotazione @ElementCollection come mostrato sopra.
rustyx
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.