scalaplayframeworkslick-3.0

Reusable Table classes in Scala Slick


In a Play application, I have a few tables and classes that share the same structure. I want to keep them different to maintain application semantics (yay static typing!). However, I'd like to avoid duplicating boilerplate code.

For example, here's class Region.

case class Region(id: Int, name:String)

and here is its Slick's table query class:

class RegionsTable(tag:Tag) extends Table[Region](tag, "regions") {
  def id = column[Int]("id", O.AutoInc, O.PrimaryKey)
  def name = column[String]("name", O.Unique)
  
  def * = (id, name) <> (Region.tupled, Region.unapply)
}

How can I avoid duplicating the table query class for each of the other classes that share Region's structure?


Solution

  • Perhaps not very Slick native solution, but refactoring might look like:

    import slick.jdbc.H2Profile.api._
    import scala.reflect.ClassTag
    
    case class Region(id: Int, name: String)
    case class Country(id: Int, name: String)
    
    class IdNameTable[T](tag: Tag, tableName: String, apply: (Int, String) => T, unapply: T => Option[(Int, String)])
                        (implicit classTag: ClassTag[T]) extends Table[T](tag, tableName) {
      def id = column[Int]("id", O.AutoInc, O.PrimaryKey)
      def name = column[String]("name", O.Unique)
      def * = (id, name) <> (apply.tupled, unapply)
    }
    
    class RegionsTable(tag: Tag) extends IdNameTable[Region](tag, "regions", Region, Region.unapply)
    
    class CountryTable(tag: Tag) extends IdNameTable[Country](tag, "country", Country, Country.unapply)
    

    Working Scatie example: https://scastie.scala-lang.org/fR7BzS1jSKSXptE9CTxXyA