Способы сохранить перечисления в базе данных

115
задан pants 9 January 2018 в 09:14
поделиться

7 ответов

Мы никогда перечисления хранилища как числовые порядковые номера больше; это делает отладку и поддержку слишком трудными. Мы храним фактическую перечислимую величину, преобразованную в строку:

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

Suit theSuit = Suit.Heart;

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

и затем читают назад с:

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

проблема была в прошлом, уставившись на Руководителя предприятия и пытаясь дешифровать:

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

стихи

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

последний намного легче. Первый потребовал достигания исходного кода и нахождения численных значений, которые были присвоены участникам перечисления.

Да это занимает больше места, но имена элемента перечисления коротки, и жесткие диски являются дешевыми, и это намного больше стоит того для помощи, когда у Вас есть проблема.

Кроме того, при использовании численных значений Вы связываетесь с ними. Вы не можете приятно ввести или перестроить участников, не имея необходимость вызывать старые численные значения. Например, изменяя перечисление Иска на:

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

должен был бы стать:

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

для поддержания численных значений прежней версии, сохраненных в базе данных.

, Как отсортировать их в базе данных

, вопрос подходит: позволяет говорят, что я хотел заказать значения. Некоторые люди могут хотеть отсортировать их по порядковому номеру перечисления. Конечно, упорядочивание карт численным значением перечисления бессмысленно:

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

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

Это не порядок, который мы хотим - мы хотим их в порядке перечисления:

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

та же работа, которая требуется при сохранении целочисленных значений требуется при сохранении строк:

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

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

, Но это не порядок, который мы хотим - мы хотим их в порядке перечисления:

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

Мое мнение - то, что этот вид рейтинга принадлежит пользовательского интерфейса. Если Вы - элементы сортировки на основе их перечислимой величины: Вы делаете что-то не так.

, Но если бы Вы хотели действительно сделать это, я создал бы Suits таблица измерений:

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

Таким образом, когда Вы хотите изменить свои карты для использования Целующиеся Короли Новый Порядок Деки, можно изменить его в целях дисплея, не выбрасывая все данные:

| 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        |

Теперь мы разделяем внутреннюю деталь программирования (имя перечисления, перечислимая величина) с установкой дисплея, предназначенной для пользователей:

SELECT Cards.Suit 
FROM Cards
   INNER JOIN Suits ON Cards.Suit = Suits.Suit
ORDER BY Suits.Rank, 
   Card.Rank*Suits.CardOrder
156
ответ дан Ian Boyd 24 November 2019 в 02:25
поделиться

Если у Вас нет причин реального исполнения избежать его, я рекомендовал бы использовать отдельную таблицу для перечисления. Используйте целостность внешнего ключа, если дополнительный поиск действительно не уничтожает Вас.

таблица Suits:

suit_id suit_name
1       Clubs
2       Hearts
3       Spades
4       Diamonds

таблица

player_name suit_id
Ian Boyd           4
Shelby Lake        2
  1. Players, Если Вы когда-либо осуществляете рефакторинг свое перечисление, чтобы быть классами с поведением (такими как приоритет), Ваша база данных уже, моделирует его правильно
  2. , Ваш DBA счастлив, потому что Ваша схема нормализована (хранение единственного целого числа на плеер, вместо всей строки, которая может или не может иметь опечаток).
  3. Ваши значения базы данных (suit_id) независимы от Вашей перечислимой величины, которая помогает Вам работать над данными из других языков также.
39
ответ дан Ian Boyd 24 November 2019 в 02:25
поделиться

Я утверждал бы, что единственный безопасный механизм здесь должен использовать Строку name() значение. При записи в DB Вы могли использовать sproc, чтобы вставить значение и при чтении, использовать Представление. Этим способом, если перечисления изменяются, существует уровень абстракции в sproc/view, чтобы быть в состоянии представить данные как перечисление значений, не "налагая" это на DB.

5
ответ дан oxbow_lakes 24 November 2019 в 02:25
поделиться

Как Вы говорите, порядковый немного опасно. Рассмотрите, например:

public enum Boolean {
    TRUE, FALSE
}

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

при хранении этого как ординалов у Вас могли бы быть строки как:

> SELECT STATEMENT, TRUTH FROM CALL_MY_BLUFF

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

, Но что происходит, если Вы обновили булевскую переменную?

public enum Boolean {
    TRUE, FILE_NOT_FOUND, FALSE
}

Это означает, что вся Ваша ложь станет неправильно истолкованной как 'file-not-found'

Лучше, чтобы просто использовать строковое представление

5
ответ дан toolkit 24 November 2019 в 02:25
поделиться

Мы просто храним само перечислимое имя - это более читаемо.

Мы действительно бездельничали с хранением определенных значений для перечислений, где существует ограниченный набор значений, например, это перечисление, которое имеет ограниченный набор состояний, которые мы используем символ для представления (более значимый, чем числовое значение):

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

и когда у Вас есть много значений, у Вас должна быть Карта в Вашем перечислении для хранения этого getFromXYZ методом маленький.

3
ответ дан JeeBee 24 November 2019 в 02:25
поделиться

Для большой базы данных я отказываюсь потерять размер и преимущества скорости числового представления. Я часто заканчиваю с таблицей базы данных, представляющей Перечисление.

можно осуществить непротиворечивость базы данных путем объявления внешнего ключа - хотя в некоторых случаях могло бы быть лучше не объявить, что как ограничение внешнего ключа, которое налагает стоимость на каждую транзакцию. Можно гарантировать непротиворечивость путем периодического выполнения проверки, во времена выбора, с:

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

другая половина этого решения должна записать некоторый тестовый код, который проверяет, что перечисление Java и таблица перечисления базы данных имеют то же содержание. Это оставляют как осуществление для читателя.

4
ответ дан naXa 24 November 2019 в 02:25
поделиться

При сохранении перечислений как строк в базе данных можно создать служебные методы для (de), сериализируют любое перечисление:

   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 24 November 2019 в 02:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: