Сохранение данных, подходящих для перечислений

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

public enum Status {
    ACTIVE(10, "Active");
    EXPIRED(11, "Expired");
    /* other statuses... */

    /* constructors, getters, etc. */
}

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

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

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

Есть ли здесь соглашение, которое использует большинство людей? Каков опыт людей с каждым из них и есть ли другие альтернативы?

Изменить:

Поразмыслив над этим некоторое время, моя настоящая борьба за постоянство приходит с обработкой значений id, которые привязаны к статусам в базе данных. Эти значения будут вставлены как данные по умолчанию при установке приложения. На этом этапе у них будут идентификаторы, которые можно использовать в качестве внешних ключей в других таблицах. Я чувствую, что мой код должен знать об этих идентификаторах, чтобы я мог легко извлекать объекты состояния и назначать их другим объектам. Что мне с этим делать? Я мог бы добавить еще одно поле, например «код», чтобы искать информацию, или просто искать статусы по имени, что неприглядно.


person Rob Hruska    schedule 29.01.2009    source источник
comment
Спасибо за все ответы - было представлено много разных решений, и все они приносят пользу. Я собираюсь позволить голосам за награду определять награду, так как мне нужно мнение сообщества, а не мои собственные предпочтения.   -  person Rob Hruska    schedule 06.04.2009


Ответы (8)


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

Если вы ожидаете большого количества значений перечисления, вы можете создать статическое сопоставление HashMap<String,MyEnum> для быстрого преобразования.

Не храните фактическое имя перечисления (например, «АКТИВНЫЙ» в вашем примере), потому что разработчики легко реорганизовывают его.

person Jason Cohen    schedule 29.01.2009
comment
Хотелось бы знать, какое явное строковое или символьное значение, кроме АКТИВНОГО, вы храните в базе данных. - person Elise van Looij; 30.05.2017

Я использую смесь трех подходов, которые вы задокументировали ...

Используйте базу данных в качестве авторитетного источника значений Enum. Сохраните значения в какой-то «кодовой» таблице. Каждый раз при сборке создавайте файл класса для Enum, который будет включен в ваш проект.

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

person Jeff Fritz    schedule 31.03.2009
comment
Мое решение по-прежнему работает с вашими новыми условиями: если значения идентификаторов для ваших перечислений изменяются в базе данных, просто повторно сгенерируйте сценарии перечисления и перекомпилируйте. Если вы правильно используете перечисления в коде, у вас не должно возникнуть проблем с несовпадающими ключами вне базы данных. - person Jeff Fritz; 06.04.2009
comment
Это применимо только к пользовательскому приложению, находящемуся в полной собственности. Однако это не будет иметь отношения ни к продукту OTS, ни даже к пользовательскому приложению, созданному разработчиками, не имеющими доступа к производственной системе. - person AviD; 03.06.2009
comment
@JeffFritz ... Пожалуйста, дайте мне знать, как вам это удалось? У нас аналогичное требование .. - person Krithika Vittal; 03.02.2014
comment
@KrithikaVittal Мы написали собственный шаблон T4, который подключался к базе данных, извлекал значения из нашей таблицы «кодов» и записывал синтаксис C # для перечисления. - person Jeff Fritz; 11.02.2014
comment
@JeffFritz Это не лучшее решение. Что, если значения базы данных изменились, и вы больше не компилировали? Было бы лучше, если бы он делал то же самое на производстве во время развертывания. - person Jatin; 23.06.2014
comment
@Jatin - Легко решается: мы защитили эти таблицы в средах, отличных от DEV, чтобы никакие значения не могли быть изменены без развертывания. Таким образом, только администратор базы данных может изменить перечисление в производственной среде, и это произойдет только во время производственного развертывания. - person Jeff Fritz; 30.06.2014

Джошуа Блох дает прекрасное объяснение перечислений и того, как их использовать в своей книге "Эффективная Java, Издание второе "(стр.147)

Там вы можете найти всевозможные уловки, как определять свои перечисления, сохранять их и как быстро отображать их между базой данных и вашим кодом (стр. 154).

Во время выступления на Jazoon 2007 Блох привел следующие причины использования дополнительного атрибута для сопоставления перечислений с полями БД и обратно: перечисление является константой, а код - нет. Чтобы убедиться, что разработчик, редактирующий источник, не может случайно нарушить сопоставление БД, переупорядочив перечисления или переименовав их, вы должны добавить в перечисление определенный атрибут (например, «dbName») и использовать его для сопоставления.

Перечисления имеют внутренний идентификатор (который используется в операторе switch ()), но этот идентификатор изменяется, когда вы меняете порядок элементов (например, путем их сортировки или добавления элементов посередине).

Итак, лучшее решение - добавить методы toDB () и fromDB () и дополнительное поле. Я предлагаю использовать короткие читаемые строки для этого нового поля, чтобы вы могли декодировать дамп базы данных, не просматривая перечисления.

person Aaron Digulla    schedule 29.01.2009
comment
На самом деле я не особо много нашел в этой главе о настойчивости, которая является моей главной заботой. Я просто не могу найти способ сделать это без жесткого кодирования чего-то вроде идентификатора, строки или чего-то еще, чтобы сопоставить мои объекты в моем коде / перечислении с строками базы данных. - person Rob Hruska; 29.01.2009

Хотя я не знаком с идеей «атрибутов» в Java (и я не знаю, какой язык вы используете), я обычно использовал идею кодовой таблицы (или таблиц, специфичных для предметной области), и я присвоил моим значениям перечисления более конкретные данные, такие как строки, читаемые человеком (например, если мое значение перечисления - NewStudent, я бы присвоил ему значение «New Student» в качестве отображаемого значения). Затем я использую Reflection для проверки данных в базе данных и вставки или обновления записей, чтобы привести их в соответствие с моим кодом, используя фактическое значение перечисления в качестве идентификатора ключа.

person Adam Robinson    schedule 02.04.2009
comment
Я использую Groovy, но смотрю на него с точки зрения Groovy и / или Java. Спасибо за ответ. - person Rob Hruska; 03.04.2009

То, что я использовал в нескольких случаях, - это определить перечисление в коде и представление хранилища на уровне сохраняемости (БД, файл и т. Д.), А затем иметь методы преобразования, чтобы сопоставить их друг с другом. Эти методы преобразования необходимо использовать только при чтении или записи в постоянное хранилище, и приложение может везде использовать перечисления, безопасные для типов. В методах преобразования я использовал операторы switch для сопоставления. Это также позволяет генерировать исключение, если необходимо преобразовать новое или неизвестное состояние (обычно потому, что либо приложение, либо данные новее, чем другое, и были объявлены новые или дополнительные состояния).

person lothar    schedule 03.04.2009

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

person Dmitry Ornatsky    schedule 06.04.2009

Что ж, у нас нет администратора базы данных, которому нужно отвечать, поэтому мы предпочитаем вариант 2).

Мы просто сохраняем значение Enum в базе данных, и когда мы загружаем данные из базы данных в наши объекты домена, мы просто приводим целочисленное значение к типу enum. Это позволяет избежать проблем с синхронизацией с вариантами 1) и 3). Список определяется один раз - в коде.

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

person Community    schedule 06.04.2009
comment
А что, если кто-то добавит в код новый член перечисления не внизу, а вверху или где-то посередине? - person Elise van Looij; 30.05.2017

В вашей базе данных первичный ключ этой "доменной" таблицы не обязательно должен быть числом. Просто используйте varchar pk и столбец описания (для целей, связанных с вашей dba). Если вам нужно гарантировать порядок значений, не полагаясь на алфавитный указатель, просто добавьте числовой столбец с именем «порядок или последовательность».

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

Если вы делаете это слишком часто, создайте сценарий для генерации кода установки / объявления.

person user16120    schedule 06.04.2009
comment
Но тогда вам нужно убедиться, что числовые значения в порядке или последовательности уникальны, вроде как первичный ключ ... - person Elise van Looij; 30.05.2017