25

I have the follow enum:

object LoginStatus extends Enumeration() with BitmaskedEnumeration {
  type LoginStatus = Value
  val Active = Value("A")
  val Inactive = Value("I")
}

I need to persist the value of the enum "A", but when the sql is generated the result is 0. this is the table mapping:

object LoginTable extends Table[Login]("login") {
  def idLogin = column[Int]("idlogin", O.PrimaryKey, O.AutoInc)
  def cdLogin = column[String]("cdlogin", O.NotNull)
  def cdPass = column[String]("cdPass", O.NotNull)
  def stLogin = column[LoginStatus]("stlogin", O.NotNull, O.DBType("character(1)"))
}

how to persiste the enum value?

I implemented

implicit val charMapper = MappedTypeMapper.base[Char, String](
    b => b.toString(),
    i => i.charAt(0))

  implicit def enum2StringMapper(enum: Enumeration) = MappedTypeMapper.base[enum.Value, Char](
    b => b.toString.charAt(0),
    i => enum.withName(i.toString))

  implicit val LoginStatusMapper = enum2StringMapper(LoginStatus)

but result in:

[error] c.Login - Invalid value for type int : A
Diogo
  • 543
  • 6
  • 12
  • this is somewhat related http://stackoverflow.com/questions/19030875/how-can-i-create-a-custom-column-type-with-typesafe-slick-in-scala/19040124#19040124 – cvogt Oct 12 '13 at 02:31
  • This is also discussed on the Slick mailing list https://groups.google.com/d/msg/scalaquery/Cd5iG-tJchM/fEIhq8IPVJQJ – cvogt Oct 12 '13 at 23:42
  • also related to http://stackoverflow.com/questions/18752929/how-to-use-enums-in-scala-slick – cvogt Oct 12 '13 at 23:45

4 Answers4

19

I would personally suggest making your own class inherit from Scala's Enumeration class because then you don't have to create mappers for every single enum you end up using:

Here is the slick 2.0 code that I am currently using:

abstract class DBEnum extends Enumeration {

  import slick.jdbc.MappedJdbcType
  import slick.driver.JdbcDriver.simple._

  implicit val enumMapper = MappedJdbcType.base[Value, Int](_.id, this.apply)
}

This should also work in slick 1.0 ( I have not tested it ):

abstract class DBEnum extends Enumeration {
  implicit val enumMapper = MappedTypeMapper.base[Value, Int](_.id, this.apply)
}

Now all you need for your enums is just to inherit from DBEnum and it should cut down on a lot of boiler plate.

Edit the code accordingly if you want to use string values instead of Ints.

prakhunov
  • 253
  • 1
  • 7
  • tks @prakhunov, I edited your answer to add the code that I changed to work with strings. – Diogo Oct 17 '13 at 19:05
  • 5
    Note this code doesn't seem to work with the current release, due to https://github.com/slick/slick/issues/540 . You get the error `JdbcProfile has no TypeInfo for type scala.slick.driver.JdbcTypesComponent$MappedJdbcType ...` To get around this, change the imports to your particular db driver, instead of 'jdbc'. e.g. `import scala.slick.driver.HsqldbDriver.simple._` and `import scala.slick.driver.HsqldbDriver.MappedJdbcType` – Luciano Feb 04 '14 at 08:21
  • You are right I noticed that when I updated my project to the final slick 2.0 release. We originally had plans to support multiple databases which sadly will not be possible now. – prakhunov Feb 26 '14 at 21:16
  • 2
    If you prefer traits: trait EnumMapper { this: Enumeration => implicit val enumMapper = MappedColumnType.base[Value, Int](_.id, this.apply) } – OlivierBlanvillain Mar 14 '14 at 18:24
  • i can't try for now since I'm not in a pc for now, but is this answer still valid to slick 3.1.0 ? – pedrorijo91 Nov 15 '15 at 23:54
4

Maybe you could create a TypeMapper for your enum type:

implicit val LoginStatusTypeMapper = MappedTypeMapper.base[LoginStatus.Value, Int](  
  // conversion from LoginStatus to int
  {
    status => status.id
  },
  // conversion back from int to enum
  {
    id => LoginStatus(id)
  }
 )

then you need to reference your column as:

columnLoginStatus.Value

This way it will be transformed back to your enum, when you load data from the database. If you insist on storing your values as characters in the DB, you just need to create a mapper that maps to a char and define your

Daniel B.
  • 929
  • 4
  • 8
2

After some help I found the solution, the enum:

object LoginStatus extends Enumeration {

  def enum2StringMapper(enum: Enumeration) = MappedTypeMapper.base[enum.Value, String](
    b => b.toString,
    i => enum.withName(i))

  implicit val LoginStatusMapper = enum2StringMapper(LoginStatus)

  type LoginStatus = Value
  val Active = Value("A")
  val Inactive = Value("I")
}

and the table mapping:

    import constants.LoginStatus._
...
    def stLogin = column[LoginStatus]("stlogin", O.NotNull, O.DBType("character(1)"))
Diogo
  • 543
  • 6
  • 12
0

@prakhunov's answer is very good and also works for Slick 3.x (tested on 3.3.3). Here is a variation of his answer which stores the enum as a string in the database:

import slick.jdbc.H2Profile.api._

// DatabaseEnumeration.scala
abstract class DatabaseEnumeration extends Enumeration {
  implicit val enumerationMapper = MappedColumnType.base[Value, String](_.toString, this.withName)
}

// ColourType.scala
object ColourType extends DatabaseEnumeration {
  type ColourType = Value
  val Red, Blue = Value
}

// NumberType.scala
object NumberType extends DatabaseEnumeration {
  type NumberType = Value
  val One, Two = Value
}

// MyTable.scala
class MyTable(tag: Tag) extends Table[(Int, ColourType, NumberType)](tag, "MYTABLE") {
  def identifier: Rep[Int] = column[Int]("IDENTIFIER", O.PrimaryKey, O.AutoInc)
  def colour: Rep[ColourType] = column[ColourType]("COLOUR")
  def number: Rep[NumberType] = column[NumberType]("NUMBER")
  def * = (identifier, colour, number)
}

To store the enum as an integers instead of string, as shown by @prakhunov, use the following:

abstract class DatabaseEnumeration extends Enumeration {
  implicit val enumerationMapper = MappedColumnType.base[Value, Int](_.id, this.apply)
}
Daniel
  • 8,655
  • 5
  • 60
  • 87