let's say I have a reference data table roles
filled with all roles that a user might be granted. The rows are quite stable, meaning that it's uncommon that someone adds a new role to the table. Additionally there's a users
table and a join-table users_roles
. In fact, the roles
table is just required to grant a user some predefined roles by adding a record to users_roles
.
The roles
table is quite simple:
CREATE TABLE IF NOT EXISTS admin.roles (
id CHAR(16) PRIMARY KEY,
description VARCHAR(256) NOT NULL
);
The following example describes a role:
INSERT INTO admin.roles VALUES('CS_AGENT', 'A customer service agent');
Obviously, I need the possible id
values somewhere in my code. This is a set of Strings, but I'd like to prevent magic Strings and make this more type safe.
As far as I understand, there are several options:
RoleId
that extends String
and declare valsIn order to define the set of role ids, these are my options:
Enumeration
I'm using JOOQ for my persistence layer, and it would be nice if I could use the type safe RoleId in my queries without manually converting it to String and vice versa.
What would be the best solution for this ?
I am not quite sure I get all of your problem, but would not something like this be the solution?
/** 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
}
}
This is completely typesafe and if you need to go to/from a String there are the toString
and fromString
methods.
The only (big) downside of this approach is a lot of boilerplate code which is easy to screw up - creating a new RoleId
but not adding it to the Set
, typo in the name or in the case, etc.
An alternative to fixing this is to make this file autogenerated by SBT from some kind of config (even reading the SQL table if reachable in the build environment), for that part this answer of mine to another question may help.