it-swarm.com.de

Möglichkeiten zum Speichern von Aufzählungen in der Datenbank

Was ist der beste Weg, um Aufzählungen in einer Datenbank zu speichern?

Ich weiß, dass Java bietet name() und valueOf() Methoden, um Aufzählungswerte in einen String und zurück zu konvertieren. Aber gibt es andere (flexible) Optionen zum Speichern diese Werte?

Gibt es eine clevere Möglichkeit, eindeutige Zahlen zu erstellen (ordinal() ist nicht sicher zu verwenden)?

pdate:

Vielen Dank für alle tollen und schnellen Antworten! Es war wie ich vermutet habe.

Allerdings ein Hinweis zu "Toolkit"; Das ist eine Möglichkeit. Das Problem ist, dass ich jedem von mir erstellten Aufzählungstyp die gleichen Methoden hinzufügen müsste. Das ist viel duplizierter Code und im Moment unterstützt Java keine Lösungen dafür (a Java enum kann andere Klassen nicht erweitern)).

111
user20298

Wir speichern niemals Aufzählungen mehr als numerische Ordnungswerte; Das macht das Debuggen und den Support viel zu schwierig. Wir speichern den tatsächlichen Aufzählungswert, der in einen String konvertiert wurde:

public enum Suit { Spade, Heart, Diamond, Club }

Suit theSuit = Suit.Heart;

szQuery = "INSERT INTO Customers (Name, Suit) " +
          "VALUES ('Ian Boyd', %s)".format(theSuit.name());

und dann lesen Sie zurück mit:

Suit theSuit = Suit.valueOf(reader["Suit"]);

Das Problem bestand in der Vergangenheit darin, Enterprise Manager anzustarren und zu entschlüsseln:

Name                Suit
==================  ==========
Shelby Jackson      2
Ian Boyd            1

verse

Name                Suit
==================  ==========
Shelby Jackson      Diamond
Ian Boyd            Heart

letzteres ist viel einfacher. Ersteres erforderte das Auffinden des Quellcodes und der numerischen Werte, die den Aufzählungsmitgliedern zugewiesen wurden.

Ja, es nimmt mehr Platz in Anspruch, aber die Namen der Aufzählungsmitglieder sind kurz und die Festplatten sind billig, und es lohnt sich viel mehr, zu helfen, wenn Sie ein Problem haben.

Wenn Sie numerische Werte verwenden, sind Sie außerdem an diese gebunden. Sie können die Elemente nicht ordnungsgemäß einfügen oder neu anordnen, ohne die alten numerischen Werte erzwingen zu müssen. Beispiel: Ändern der Suit-Aufzählung in:

public enum Suit { Unknown, Heart, Club, Diamond, Spade }

müsste werden:

public enum Suit { 
      Unknown = 4,
      Heart = 1,
      Club = 3,
      Diamond = 2,
      Spade = 0 }

um die in der Datenbank gespeicherten alten numerischen Werte beizubehalten.

So sortieren Sie sie in der Datenbank

Die Frage kommt auf: Nehmen wir an, ich wollte die Werte bestellen. Einige Leute möchten sie möglicherweise nach dem Ordnungswert der Aufzählung sortieren. Natürlich ist es bedeutungslos, die Karten nach dem Zahlenwert der Aufzählung zu ordnen:

SELECT Suit FROM Cards
ORDER BY SuitID; --where SuitID is integer value(4,1,3,2,0)

Suit
------
Spade
Heart
Diamond
Club
Unknown

Das ist nicht die Reihenfolge, die wir wollen - wir wollen sie in Aufzählungsreihenfolge:

SELECT Suit FROM Cards
ORDER BY CASE SuitID OF
    WHEN 4 THEN 0 --Unknown first
    WHEN 1 THEN 1 --Heart
    WHEN 3 THEN 2 --Club
    WHEN 2 THEN 3 --Diamond
    WHEN 0 THEN 4 --Spade
    ELSE 999 END

Dieselbe Arbeit, die erforderlich ist, wenn Sie Ganzzahlwerte speichern, ist erforderlich, wenn Sie Zeichenfolgen speichern:

SELECT Suit FROM Cards
ORDER BY Suit; --where Suit is an enum name

Suit
-------
Club
Diamond
Heart
Spade
Unknown

Aber das ist nicht die Reihenfolge, die wir wollen - wir wollen sie in Aufzählungsreihenfolge:

SELECT Suit FROM Cards
ORDER BY CASE Suit OF
    WHEN 'Unknown' THEN 0
    WHEN 'Heart'   THEN 1
    WHEN 'Club'    THEN 2
    WHEN 'Diamond' THEN 3
    WHEN 'Space'   THEN 4
    ELSE 999 END

Meiner Meinung nach gehört ein solches Ranking in die Benutzeroberfläche. Wenn Sie Elemente nach ihrem Aufzählungswert sortieren: Sie machen etwas falsch.

Aber wenn Sie das wirklich tun wollten, würde ich eine Suits -Dimensionstabelle erstellen:

| Suit       | SuitID       | Rank          | Color  |
|------------|--------------|---------------|--------|
| Unknown    | 4            | 0             | NULL   |
| Heart      | 1            | 1             | Red    |
| Club       | 3            | 2             | Black  |
| Diamond    | 2            | 3             | Red    |
| Spade      | 0            | 4             | Black  |

Auf diese Weise können Sie Ihre Karten für die Verwendung von Kissing Kings New Deck Order zu Anzeigezwecken ändern, ohne wirf alle deine Daten weg:

| Suit       | SuitID       | Rank          | Color  | CardOrder |
|------------|--------------|---------------|--------|-----------|
| Unknown    | 4            | 0             | NULL   | NULL      |
| Spade      | 0            | 1             | Black  | 1         |
| Diamond    | 2            | 2             | Red    | 1         |
| Club       | 3            | 3             | Black  | -1        |
| Heart      | 1            | 4             | Red    | -1        |

Jetzt trennen wir ein internes Programmierdetail (Aufzählungsname, Aufzählungswert) von einer Anzeigeeinstellung, die für Benutzer bestimmt ist:

SELECT Cards.Suit 
FROM Cards
   INNER JOIN Suits ON Cards.Suit = Suits.Suit
ORDER BY Suits.Rank, 
   Card.Rank*Suits.CardOrder
153
Ian Boyd

Ich würde empfehlen, für die Aufzählung eine separate Tabelle zu verwenden, es sei denn, Sie haben bestimmte Leistungsgründe, um dies zu vermeiden. Verwenden Sie die Integrität von Fremdschlüsseln, es sei denn, die zusätzliche Suche bringt Sie wirklich um.

Anzüge Tisch:

suit_id suit_name
1       Clubs
2       Hearts
3       Spades
4       Diamonds

Spielertabelle

player_name suit_id
Ian Boyd           4
Shelby Lake        2
  1. Wenn Sie Ihre Aufzählung jemals so umgestalten, dass sie Klassen mit Verhalten (z. B. Priorität) enthält, wird sie von Ihrer Datenbank bereits korrekt modelliert
  2. Ihr DBA ist glücklich, weil Ihr Schema normalisiert ist (Speichern einer einzelnen Ganzzahl pro Player anstelle einer gesamten Zeichenfolge, die Tippfehler enthalten kann oder nicht).
  3. Ihre Datenbankwerte (suit_id) sind unabhängig von Ihrem Aufzählungswert, wodurch Sie auch die Daten aus anderen Sprachen bearbeiten können.
37
Tom

Ich würde argumentieren, dass der einzige sichere Mechanismus hier die Verwendung des Werts String name() ist. Beim Schreiben in die Datenbank können Sie könnte einen Sproc zum Einfügen des Werts und beim Lesen eine View verwenden. Wenn sich die Aufzählungen ändern, gibt es auf diese Weise eine Indirektionsebene im Sproc/in der Ansicht, um die Daten als Aufzählungswert darstellen zu können, ohne dies der Datenbank "aufzuerlegen".

5
oxbow_lakes

Wie Sie sagen, ist Ordinal ein bisschen riskant. Betrachten Sie zum Beispiel:

public enum Boolean {
    TRUE, FALSE
}

public class BooleanTest {
    @Test
    public void testEnum() {
        assertEquals(0, Boolean.TRUE.ordinal());
        assertEquals(1, Boolean.FALSE.ordinal());
    }
}

Wenn Sie dies als Ordnungszahl gespeichert haben, haben Sie möglicherweise folgende Zeilen:

> SELECT STATEMENT, TRUTH FROM CALL_MY_BLUFF

"Alice is a boy"      1
"Graham is a boy"     0

Aber was passiert, wenn Sie Boolean aktualisiert haben?

public enum Boolean {
    TRUE, FILE_NOT_FOUND, FALSE
}

Dies bedeutet, dass all Ihre Lügen als "Datei nicht gefunden" fehlinterpretiert werden.

Verwenden Sie einfach eine Zeichenfolgendarstellung

5
toolkit

Bei einer großen Datenbank möchte ich die Vorteile der numerischen Darstellung hinsichtlich Größe und Geschwindigkeit nur ungern verlieren. Am Ende habe ich oft eine Datenbanktabelle, die die Aufzählung darstellt.

Sie können die Datenbankkonsistenz erzwingen, indem Sie einen Fremdschlüssel deklarieren. In einigen Fällen ist es jedoch besser, diesen nicht als Fremdschlüsseleinschränkung zu deklarieren, da jede Transaktion mit Kosten verbunden ist. Sie können die Konsistenz sicherstellen, indem Sie in regelmäßigen Abständen eine Überprüfung Ihrer Wahl durchführen:

SELECT reftable.* FROM reftable
  LEFT JOIN enumtable ON reftable.enum_ref_id = enumtable.enum_id
WHERE enumtable.enum_id IS NULL;

Die andere Hälfte dieser Lösung besteht darin, einen Testcode zu schreiben, der prüft, ob Java enum und die Datenbank-Enum-Tabelle den gleichen Inhalt haben. Dies ist eine Übung für den Leser.

4
Roger Hayes

Wir speichern nur den Namen der Aufzählung selbst - er ist besser lesbar.

Wir haben herumgespielt, um bestimmte Werte für Aufzählungen zu speichern, bei denen es eine begrenzte Menge von Werten gibt, z. B. diese Aufzählung, die eine begrenzte Menge von Status hat, für die wir ein Zeichen verwenden (aussagekräftiger als ein numerischer Wert):

public enum EmailStatus {
    EMAIL_NEW('N'), EMAIL_SENT('S'), EMAIL_FAILED('F'), EMAIL_SKIPPED('K'), UNDEFINED('-');

    private char dbChar = '-';

    EmailStatus(char statusChar) {
        this.dbChar = statusChar;
    }

    public char statusChar() {
        return dbChar;
    }

    public static EmailStatus getFromStatusChar(char statusChar) {
        switch (statusChar) {
        case 'N':
            return EMAIL_NEW;
        case 'S':
            return EMAIL_SENT;
        case 'F':
            return EMAIL_FAILED;
        case 'K':
            return EMAIL_SKIPPED;
        default:
            return UNDEFINED;
        }
    }
}

und wenn Sie viele Werte haben, müssen Sie eine Map in Ihrer Enumeration haben, um diese getFromXYZ-Methode klein zu halten.

3
JeeBee

Wenn Sie Aufzählungen als Zeichenfolgen in der Datenbank speichern, können Sie Dienstprogrammmethoden erstellen, um eine Aufzählung zu (de) serialisieren:

   public static String getSerializedForm(Enum<?> enumVal) {
        String name = enumVal.name();
        // possibly quote value?
        return name;
    }

    public static <E extends Enum<E>> E deserialize(Class<E> enumType, String dbVal) {
        // possibly handle unknown values, below throws IllegalArgEx
        return Enum.valueOf(enumType, dbVal.trim());
    }

    // Sample use:
    String dbVal = getSerializedForm(Suit.SPADE);
    // save dbVal to db in larger insert/update ...
    Suit suit = deserialize(Suit.class, dbVal);
2
Dov Wasserman

Ich habe das gleiche Problem festgestellt, bei dem es mein Ziel ist, den Wert der Aufzählungszeichenfolge anstelle des Ordnungswerts in der Datenbank zu belassen.

Um dieses Problem zu lösen, habe ich @Enumerated(EnumType.STRING) verwendet und mein Ziel wurde gelöst.

Zum Beispiel haben Sie eine Enum Klasse:

public enum FurthitMethod {

    Apple,
    Orange,
    Lemon
}

Definieren Sie in der Entitätsklasse @Enumerated(EnumType.STRING):

@Enumerated(EnumType.STRING)
@Column(name = "Fruits")
public FurthitMethod getFuritMethod() {
    return fruitMethod;
}

public void setFruitMethod(FurthitMethod authenticationMethod) {
    this.fruitMethod= fruitMethod;
}

Während Sie versuchen, Ihren Wert auf Database zu setzen, wird der String-Wert in Database als "Apple", "ORANGE" oder "LEMON" gespeichert.

2
SaravanaC

All meine Erfahrungen haben mir gezeigt, dass es am sichersten ist, Aufzählungen irgendwo zu speichern, indem man einen zusätzlichen Codewert oder eine ID verwendet (eine Art Weiterentwicklung von @jeebee answer). Dies könnte ein schönes Beispiel für eine Idee sein:

enum Race {
    HUMAN ("human"),
    ELF ("elf"),
    DWARF ("dwarf");

    private final String code;

    private Race(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }
}

Jetzt können Sie mit jeder Persistenz fortfahren, die auf Ihre Enum-Konstanten anhand ihres Codes verweist. Auch wenn Sie sich entscheiden, einige der Konstantennamen zu ändern, können Sie den Codewert (z. B. DWARF("dwarf") bis GNOME("dwarf")) immer speichern.

Ok, tauchen Sie mit dieser Vorstellung etwas tiefer ein. Im Folgenden finden Sie eine Dienstprogrammmethode, mit der Sie einen beliebigen Aufzählungswert ermitteln können. Zunächst können Sie jedoch unseren Ansatz erweitern.

interface CodeValue {
    String getCode();
}

Und lassen Sie es unsere Enumeration implementieren:

enum Race implement CodeValue {...}

Dies ist die Zeit für die magische Suchmethode:

static <T extends Enum & CodeValue> T resolveByCode(Class<T> enumClass, String code) {
    T[] enumConstants = enumClass.getEnumConstants();
    for (T entry : enumConstants) {
        if (entry.getCode().equals(code)) return entry;
    }
    // In case we failed to find it, return null.
    // I'd recommend you make some log record here to get notified about wrong logic, perhaps.
    return null;
}

Und benutze es wie einen Zauber: Race race = resolveByCode(Race.class, "elf")

2
Metaphore

Mehrere Werte mit der Beziehung OR für ein, Aufzählungsfeld. Das Konzept für .NET mit dem Speichern von Aufzählungstypen in einer Datenbank wie einem Byte oder einem Int und dem Verwenden von FlagsAttribute in Ihrem Code.

http://blogs.msdn.com/b/efdesign/archive/2011/06/29/enumeration-support-in-entity-framework.aspx

1
Kryszal