Как смоделировать перечисление статических идентификаторов на основе строк в Scala? - PullRequest
0 голосов
/ 07 января 2019

допустим, у меня есть справочная таблица данных roles, заполненная всеми ролями, которые может быть предоставлен пользователю. Строки достаточно стабильны, это означает, что редко кто-то добавляет новую роль в таблицу. Дополнительно есть таблица users и таблица соединений users_roles. Фактически, таблица roles просто необходима, чтобы предоставить пользователю некоторые предопределенные роли, добавив запись в users_roles.

Таблица roles довольно проста:

CREATE TABLE IF NOT EXISTS admin.roles (
  id          CHAR(16) PRIMARY KEY,
  description VARCHAR(256) NOT NULL
);

В следующем примере описана роль:

INSERT INTO admin.roles VALUES('CS_AGENT', 'A customer service agent');

Очевидно, мне нужны возможные значения id где-нибудь в моем коде. Это набор строк, но я бы хотел предотвратить магические строки и сделать этот тип более безопасным.

Насколько я понимаю, есть несколько вариантов:

  • создать символ для каждой роли id
  • создать новый тип RoleId, который расширяет String и объявить значения

Чтобы определить набор идентификаторов ролей, вот мои варианты:

  • используйте Enumeration
  • использовать запечатанный признак / запечатанный объект и извлекать из него объекты падежа

Я использую JOOQ для своего слоя персистентности, и было бы неплохо, если бы я мог использовать безопасный тип RoleId в своих запросах без ручного преобразования его в String и наоборот.

Что было бы лучшим решением для этого?

1 Ответ

0 голосов
/ 08 января 2019

Я не совсем уверен, что получу всю вашу проблему, но разве что-то подобное не будет решением?

/** Represents a RoleId from the roles table. */
sealed trait RoleId {
  def name: String
  def description: String
  override final def toString: String = name
}

object RoleId {
  case object CS_AGENT extends RoleId {
    override val name = "CS_AGENT"
    override val description = "A customer service agent"
  }
  // Define all other roles...

  /** All roles */
  val allRoles: Set[RoleId] = Set(
    CS_AGENT,
    // All other roles...
  )

  /** Returns an RoleId given its name, if the name is not found this will return a None. */
  def fromString(name: String): Option[RoleId] = name.toUpperCase match {
    case "CS_AGENT" => Some(CS_AGENT)
    // All other cases..
    case _ => None
  }
}

Это полностью типов безопасности , и если вам нужно перейти к / из строки, есть методы toString и fromString.

Единственный (большой) недостаток этого подхода - много шаблонного кода, который легко испортить - создав новый RoleId, но не добавив его в опечатку Set в имя или в кейсе и т. д.
Альтернативой исправлению этого является автоматическое создание этого файла с помощью SBT из некоторого вида конфигурации (даже чтение таблицы SQL, если она доступна в среде сборки) , для этой части этот ответ мой на другой вопрос может помочь.

...