首页 > 解决方案 > 如何在 Scala 中对基于字符串的静态 ID 的枚举进行建模?

问题描述

假设我有一个参考数据表roles,其中填充了用户可能被授予的所有角色。这些行非常稳定,这意味着很少有人向表中添加新角色。此外还有一个userstable 和一个 join-table 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,我的选项如下:

我将 JOOQ 用于我的持久层,如果我可以在查询中使用类型安全的 RoleId 而无需手动将其转换为 String ,反之亦然。

什么是最好的解决方案?

标签: scalaormenumerationjooq

解决方案


我不太确定我能解决你所有的问题,但这样的解决方案不好吗?

/** 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
  }
}

这是完全类型安全的,如果你需要去/从一个字符串有toStringfromString方法。

这种方法的唯一(大)缺点是很多样板代码很容易搞砸 - 创建一个新RoleId但不将其添加到Set名称或案例中的拼写错误等。
解决此问题的另一种方法是使这个文件由SBT从某种配置中自动生成(如果在构建环境中可以访问,甚至读取 SQL 表),对于那部分,我对另一个问题的这个答案可能会有所帮助。


推荐阅读