Ich suche nach verschiedenen Möglichkeiten, eine Enumeration mit JPA abzubilden. Ich möchte insbesondere den Integer-Wert jedes Enum-Eintrags festlegen und nur den Integer-Wert speichern.
@Entity
@Table(name = "AUTHORITY_")
public class Authority implements Serializable {
public enum Right {
READ(100), WRITE(200), EDITOR (300);
private int value;
Right(int value) { this.value = value; }
public int getValue() { return value; }
};
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "AUTHORITY_ID")
private Long id;
// the enum to map :
private Right right;
}
Eine einfache Lösung besteht darin, die Annotation Enumerated mit EnumType.ORDINAL zu verwenden:
@Column(name = "RIGHT")
@Enumerated(EnumType.ORDINAL)
private Right right;
In diesem Fall bildet JPA jedoch den Enum-Index (0,1,2) und nicht den gewünschten Wert (100.200.300) ab.
Die beiden Lösungen, die ich gefunden habe, scheinen nicht einfach zu sein ...
Erste Lösung
Eine hier vorgeschlagene Lösung verwendet @PrePersist und @PostLoad, um die Aufzählung in ein anderes Feld zu konvertieren und das Aufzählungsfeld als vorübergehend zu markieren:
@Basic
private int intValueForAnEnum;
@PrePersist
void populateDBFields() {
intValueForAnEnum = right.getValue();
}
@PostLoad
void populateTransientFields() {
right = Right.valueOf(intValueForAnEnum);
}
Zweite Lösung
Die zweite hier vorgeschlagene Lösung schlug ein generisches Konvertierungsobjekt vor, scheint jedoch immer noch schwer und auf den Ruhezustand ausgerichtet zu sein (@Type scheint in Java EE nicht zu existieren):
@Type(
type = "org.appfuse.tutorial.commons.hibernate.GenericEnumUserType",
parameters = {
@Parameter(
name = "enumClass",
value = "Authority$Right"),
@Parameter(
name = "identifierMethod",
value = "toInt"),
@Parameter(
name = "valueOfMethod",
value = "fromInt")
}
)
Gibt es noch andere Lösungen?
Ich habe mehrere Ideen, aber ich weiß nicht, ob sie in JPA existieren:
- Verwenden Sie beim Laden und Speichern des Authority-Objekts die Setter- und Getter-Methoden des rechten Mitglieds der Authority-Klasse
- Eine äquivalente Idee wäre, JPA mitzuteilen, welche Methoden Right Enum verwendet, um Enum in Int und Int in Enum umzuwandeln
- Gibt es eine Möglichkeit, JPA anzuweisen, einen bestimmten Konverter (RightEditor) zu verwenden, da ich Spring verwende?
Antworten:
Für Versionen vor JPA 2.1 bietet JPA nur zwei Möglichkeiten, um mit Aufzählungen umzugehen, entweder nach ihren
name
oder nach ihrenordinal
. Und die Standard-JPA unterstützt keine benutzerdefinierten Typen. So:UserType
, EclipseLinkConverter
usw.). (die zweite Lösung). ~ oder ~int
Wert ~ oder ~ und geben Sie ihn zurückIch werde die neueste Option veranschaulichen (dies ist eine grundlegende Implementierung, optimieren Sie sie nach Bedarf):
quelle
Dies ist jetzt mit JPA 2.1 möglich:
Weitere Details:
quelle
@Converter
,enum
sollten aber sofort eleganter gehandhabt werden!AttributeConverter
ABER sprechen, zitiert einen Code, der nichts dergleichen tut und das OP nicht beantwortet.AttributeConverter
@Convert(converter = Converter.class) @Enumerated(EnumType.STRING)
Ab JPA 2.1 können Sie AttributeConverter verwenden .
Erstellen Sie eine Aufzählungsklasse wie folgt:
Und erstellen Sie einen Konverter wie folgt:
Auf der Entität brauchen Sie nur:
Ihr Glück
@Converter(autoApply = true)
kann je nach Container variieren, wurde jedoch getestet, um mit Wildfly 8.1.0 zu arbeiten. Wenn es nicht funktioniert, können Sie@Convert(converter = NodeTypeConverter.class)
die Entitätsklassenspalte hinzufügen .quelle
Der beste Ansatz wäre, jedem Aufzählungstyp eine eindeutige ID zuzuordnen, um die Fallstricke von ORDINAL und STRING zu vermeiden. Siehe diesen Beitrag 5 Möglichkeiten beschrieben, wie Sie eine Aufzählung zuordnen können.
Entnommen aus dem obigen Link:
1 & 2. Verwenden von @Enumerated
Derzeit gibt es zwei Möglichkeiten, wie Sie mithilfe der Annotation @Enumerated Aufzählungen in Ihren JPA-Entitäten zuordnen können. Leider haben sowohl EnumType.STRING als auch EnumType.ORDINAL ihre Grenzen.
Wenn Sie EnumType.String verwenden, führt das Umbenennen eines Ihrer Aufzählungstypen dazu, dass Ihr Aufzählungswert nicht mit den in der Datenbank gespeicherten Werten synchronisiert ist. Wenn Sie EnumType.ORDINAL verwenden, werden beim Löschen oder Neuordnen der Typen in Ihrer Aufzählung die in der Datenbank gespeicherten Werte den falschen Aufzählungstypen zugeordnet.
Beide Optionen sind fragil. Wenn die Aufzählung geändert wird, ohne eine Datenbankmigration durchzuführen, können Sie die Integrität Ihrer Daten gefährden.
3. Lebenszyklus-Rückrufe
Eine mögliche Lösung wäre die Verwendung der Rückrufanmerkungen für den JPA-Lebenszyklus, @PrePersist und @PostLoad. Dies fühlt sich ziemlich hässlich an, da Sie jetzt zwei Variablen in Ihrer Entität haben. Eine Zuordnung des in der Datenbank gespeicherten Werts und die andere der tatsächlichen Aufzählung.
4. Ordnen Sie jedem Aufzählungstyp eine eindeutige ID zu
Die bevorzugte Lösung besteht darin, Ihre Aufzählung einem festen Wert oder einer festen ID zuzuordnen, die in der Aufzählung definiert ist. Durch die Zuordnung zu einem vordefinierten festen Wert wird Ihr Code robuster. Änderungen an der Reihenfolge der Aufzählungstypen oder die Umgestaltung der Namen haben keine nachteiligen Auswirkungen.
5. Verwenden von Java EE7 @Convert
Wenn Sie JPA 2.1 verwenden, haben Sie die Möglichkeit, die neue Annotation @Convert zu verwenden. Dies erfordert die Erstellung einer mit @Converter kommentierten Konverterklasse, in der Sie definieren, welche Werte für jeden Aufzählungstyp in der Datenbank gespeichert werden. Innerhalb Ihrer Entität würden Sie dann Ihre Aufzählung mit @Convert kommentieren.
Meine Präferenz: (Nummer 4)
Der Grund, warum ich es vorziehe, meine IDs innerhalb der Aufzählung zu definieren, anstatt einen Konverter zu verwenden, ist eine gute Kapselung. Nur der Aufzählungstyp sollte seine ID kennen, und nur die Entität sollte wissen, wie sie die Aufzählung der Datenbank zuordnet.
Die Original - Beitrag für das Codebeispiel.
quelle
Das Problem ist, denke ich, dass JPA nie mit der Idee ins Leben gerufen wurde, dass wir bereits ein komplexes, bereits existierendes Schema haben könnten.
Ich denke, dass sich daraus zwei Hauptmängel ergeben, die spezifisch für Enum sind:
Helfen Sie meiner Sache und stimmen Sie über JPA_SPEC-47 ab
Wäre dies nicht eleganter als die Verwendung eines @Converter zur Lösung des Problems?
quelle
Möglicherweise nahe verwandter Code von Pascal
quelle
Ich würde folgendes tun:
Deklarieren Sie die Aufzählung separat in der eigenen Datei:
Deklarieren Sie eine neue JPA-Entität mit dem Namen Right
Sie benötigen auch einen Konverter, um diese Werte zu erhalten (nur JPA 2.1 und es gibt ein Problem, das ich hier nicht mit diesen Aufzählungen diskutieren werde, um direkt mit dem Konverter fortzufahren, es wird also nur eine Einbahnstraße sein).
Die Behörde:
Auf diese Weise können Sie nicht direkt auf das Aufzählungsfeld setzen. Sie können jedoch das Feld Rechts in Autorität mit festlegen
Und wenn Sie vergleichen müssen, können Sie verwenden:
Was ziemlich cool ist, finde ich. Es ist nicht ganz richtig, da der Konverter nicht für die Verwendung mit Aufzählungen vorgesehen ist. In der Dokumentation heißt es, dass Sie die Annotation @Enumerated stattdessen verwenden sollten, um sie niemals für diesen Zweck zu verwenden. Das Problem ist, dass es nur zwei Aufzählungstypen gibt: ORDINAL oder STRING, aber das ORDINAL ist schwierig und nicht sicher.
Wenn es Sie jedoch nicht zufriedenstellt, können Sie etwas Hackigeres und Einfacheres tun (oder auch nicht).
Mal schauen.
The RightEnum:
und die Behörde
In dieser zweiten Idee ist es keine perfekte Situation, da wir das Ordnungsattribut hacken, aber es ist eine viel kleinere Codierung.
Ich denke, dass die JPA-Spezifikation die EnumType.ID enthalten sollte, in der das Enum-Wertfeld mit einer Art @ EnumId-Annotation versehen werden sollte.
quelle
Meine eigene Lösung zur Lösung dieser Art von Enum JPA-Zuordnung ist die folgende.
Schritt 1 - Schreiben Sie die folgende Schnittstelle, die wir für alle Aufzählungen verwenden, die wir einer Datenbankspalte zuordnen möchten:
Schritt 2 - Implementieren Sie einen benutzerdefinierten generischen JPA-Konverter wie folgt:
Diese Klasse konvertiert den Aufzählungswert unter Verwendung der Aufzählung en
E
in ein Datenbankfeld vom TypT
(z. B.String
) und umgekehrt.getDbVal()
E
Schritt 3 - Lassen Sie die ursprüngliche Aufzählung die in Schritt 1 definierte Schnittstelle implementieren:
Schritt 4 - Erweitern Sie den Konverter von Schritt 2 für die
Right
Aufzählung von Schritt 3:Schritt 5 - Der letzte Schritt besteht darin, das Feld in der Entität wie folgt zu kommentieren:
Fazit
IMHO ist dies die sauberste und eleganteste Lösung, wenn Sie viele Aufzählungen zuordnen müssen und ein bestimmtes Feld der Aufzählung selbst als Zuordnungswert verwenden möchten.
Für alle anderen Enums in Ihrem Projekt, die eine ähnliche Zuordnungslogik benötigen, müssen Sie nur die Schritte 3 bis 5 wiederholen, dh:
IDbValue
in Ihrer Enumeration.EnumDbValueConverter
mit nur 3 Codezeilen (Sie können dies auch in Ihrer Entität tun, um das Erstellen einer getrennten Klasse zu vermeiden).@Convert
fromjavax.persistence
package.Hoffe das hilft.
quelle
erster Fall (
EnumType.ORDINAL
)zweiter Fall (
EnumType.STRING
)quelle