Die Ordnungszahl einer Enum wird zusammen mit JPA verwendet, um den Datenbankwert eines Feldes vom Typ Enum einer Entität festzulegen. Da ich die Verwendung der Ordnungszahl im Falle zukünftiger Änderungen für gefährlich halte, suchte ich nach einer alternativen Möglichkeit, mein Datenbankfeld zu füllen und gleichzeitig die Enum in meinem Anwendungscode zu verwenden.
Die erste offensichtliche Lösung könnte die Verwendung der Option EnumType.STRING der @Enumerated-Annotation sein. Meiner Meinung nach könnte dies nützlich sein, wenn die Datenbank so umgestaltet werden kann, dass sie diese String-Darstellungen aufnehmen kann. Der Fall, auf den ich gestoßen bin, war ein definiertes Datenbankfeld NUMBER namens status mit den möglichen Werten 0, 100 und 200, was INITIAL, ACTIVE und INACTIVE bedeutet. Wenn Sie die Standardkonvertierung mit der unten erwähnten Aufzählung verwenden, werden die Werte 0, 1 und 2 zum Einfügen in die Datenbank verwendet.
public enum Status {
INITIAL, AKTIV, INAKTIV;
}
und die Verwendung des Enums in der Entitätsklasse
privat Status Status
Und was ist, wenn jemand beschließt, einen zusätzlichen Status namens GEPRÜFT mit einem Wert von 10 hinzuzufügen? Sollte ich eine Enum wie diese erstellen, um die Standardeinstellung verwenden zu können?
public enum Status {
INITIAL, UNUSED_1, ..., UNUSED_9, CHECKED, UNUSED_11 .. UNUSED_99, ACTIVE, DO_YOU_GET_THE_FEELING?;
}
Also habe ich weiter gesucht und herausgefunden, dass es mit EclipseLink (dem JPA-Anbieter, den wir verwenden) möglich ist, einen Konverter zu definieren und diesen in Ihren Entitäten zu verwenden. Versuchen wir es also. Zunächst benötigen wir eine Enum, die den richtigen Wert enthält: (Siehe auch einen früheren Blog über Enums)
public enum Status {
INITIAL(0), CHECKED(10), ACTIVE(100), INACTIVE(200);
private final byte status;
private Status(int Wert) {
Status = (Byte) Wert;
}
public int getValue() {
Status zurückgeben;
}
}
Als nächstes brauchen wir einen Konverter:
public class StatusEnumConverter implements Konverter {
public Object convertDataValueToObjectValue(Object data, Session session) {
//TODO implementiert die Umwandlung von Datenbankwerten in Enum
null zurückgeben;
}
public Object convertObjectValueToDataValue(Object data, Session session) {
if (data instanceof Status) {
return BigDecimal.valueOf(((Status) data).getValue());
}
null zurückgeben;
}
public void initialize(DatabaseMapping dbMap, Session session) {
// Keine Notwendigkeit für Datenbankzuordnung
}
public boolean isMutable() {
return false;
}
}
Das ist jetzt eine Einbahnstraße. Wie kommen wir von einem Wert zum richtigen Element der Enum? Bei der Suche im Internet fand ich das Problem der Enum-Inversion, das mir eine Lösung für dieses Problem lieferte. Außerdem wollte ich, dass der StatusEnumConverter generischer ist, damit der Konverter leicht wiederverwendet werden kann. Ich habe eine ReverseEnumMap-Klasse erstellt, um das Problem der Ermittlung des richtigen Enum-Werts zu lösen, und eine konvertierbare Schnittstelle, um den Konverter zu generieren (ist das ein Wort?). Das Endergebnis sieht wie folgt aus:
public class ReverseEnumMap<v erweitert Enum & Convertable> {
private Map map = new HashMap();
public ReverseEnumMap(Klasse valueType) {
for (V v : valueType.getEnumConstants()) {
map.put(v.convert(), v);
}
}
public V get(Object obj) {
return map.get(obj);
}
}
public Schnittstelle Convertable<e erweitert Enum & Convertable> {
Object convert();
E getFromValue(Object value);
}
public enum Status implements Convertable {
INITIAL(0), CHECKED(10), ACTIVE(100), INACTIVE(200);
private static ReverseEnumMap map = new ReverseEnumMap(Status.class);
private final byte status;
private Status(int Wert) {
Status = (Byte) Wert;
}
public Object convert() {
return BigDecimal.valueOf(status);
}
public Status getFromValue(Object obj) {
return map.get(obj);
}
}
public abstract class AbstractConverter implementiert Konverter {
public abstract Convertable getConvertableEnum();
public Object convertDataValueToObjectValue(Object data, Session session) {
wenn (Daten == null) {
return getConvertableEnum();
}
Convertable convertableEnum = getConvertableEnum().getFromValue(data);
if (convertableEnum == null) {
throw new IllegalArgumentException(
"Daten nicht mit einem passenden Wert erhalten [" + data.getClass() +" : "+data
+ "] erwartete einen gültigen Wert von ["
+ getConvertableEnum().getClass() + "]");
} sonst {
return convertableEnum;
}
}
public Object convertObjectValueToDataValue(Object data, Session session) {
wenn (Daten == null) {
return getConvertableEnum().convert();
}
if (data instanceof Convertable) {
return ((Convertable) data).convert();
}
throw new IllegalArgumentException("Data not of correct type got ["
+ data.getClass() + "] erwartet [Convertable]");
}
//die anderen Methoden aus Gründen der Lesbarkeit weggelassen
}
public class StatusConverter extends AbstractConverter {
private static final long serialVersionUID = 6209909216228257358L;
@Override
public Convertable getConvertableEnum() {
return Status.INITIAL;
}
}
Und so werden Sie den Konverter in Ihrer Entitätsklasse verwenden. Annotation des Statusfeldes. Beachten Sie, dass Sie die @Converter-Anmerkung nur einmal definieren müssen, um den @Convert auch in anderen Entitäten verwenden zu können!
@Converter(name="status", converterClass=com.xebia.enum.converters.StatusConverter.class)
@Convert("Status")
private Status Status;
Ich denke, damit haben wir eine ziemlich generische Lösung, um die Verwendung von Ordinal mit Enums und JPA zu vermeiden.
Verfasst von
Kris Geusebroek
Unsere Ideen
Weitere Blogs
Contact


