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


假设我有一个引用数据表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创建一个符号
  • 创建一个扩展String并声明vals的新类型RoleId

为了定义角色ID集,以下是我的选项:

  • 使用Enumeration
  • 使用密封特征/密封对象并从中派生case对象

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

对此,最好的解决方案是什么?

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

/** 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表),就这一部分而言,我对另一个问题的回答可能会有所帮助。

最新更新