如何在Scala中为基于字符串的静态ID枚举建模?

时间:2019-01-07 20:57:23

标签: scala orm enumeration jooq

假设我有一个参考数据表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并声明val的新类型String

为了定义一组角色ID,这些是我的选择:

  • 使用Enumeration
  • 使用密封的特征/密封对象并从中导出案例对象

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

什么是最好的解决方案?

1 个答案:

答案 0 :(得分:1)

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

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

这完全是 typesafe ,并且,如果您需要使用字符串,则可以使用toStringfromString方法。

此方法唯一的(big)缺点是很多易于理解的样板代码-创建新的RoleId而不是将其添加到{{1} },名称或大小写的错别字等。
解决此问题的另一种方法是使 SBT 通过某种配置(即使在构建环境中可以访问SQL表)对该文件自动生成文件,对于该部分{我的{3}}对另一个问题可能会有帮助。