1
votes

I have some columns all my tables share, so I'd like to provide the default columns for all the tables. Following is what I have tried so far. I am using Slick 3.0.

// created_at and updated_at are the columns every table share
abstract class BaseTable[T](tag: Tag, tableName: String) 
     extends Table[T](tag, tableName) {

  def currentWhenInserting = new Timestamp((new Date).getTime)

  def createdAt = column[Timestamp]("created_at", O.Default(currentWhenInserting))

  def updatedAt = column[Timestamp]("updated_at", O.Default(currentWhenInserting))

}

And the simple way to implement this seems like the following.

case class Student(
  name: String, age: Int, 
  created_at: Timestamp, updated_at: Timestamp
)

class Students(tag: Tag) extends BaseTable[Student](tag, "students"){

  def name = column[String]("name")
  def age = column[Int]("age")

  override def * : ProvenShape[Student] = 
        (name, age, createdAt, updatedAt).shaped <> 
        (Student.tupled, Student.unapply _)
}

But it's not desirable.

First, force every table row case class to incoporate created_at and updated_at. If I have more fields that would be totally unacceptable from the API design angle.

Second, write the two createdAt, updatedAt in (name, age, createdAt, updatedAt) explicitly. This is not what I expect about Default row.

My ideal way of solving this would be like the following:

case class Student(name: String, age: Int)

class Students(tag: Tag) extends BaseTable[Student](tag, "students"){    

  def name = column[String]("name")
  def age = column[Int]("age")

  override def * : ProvenShape[Student] = 
        (name, age).shaped <> 
        (Student.tupled, Student.unapply _)
}

That is, write some method in BaseTable or Define BaseCaseClass to avoid explicitly writing the extra two fields in table definition like Students and row case class definition Student.

However, after a painful struggle, still can get it done. Any help would be greatly appreciated.

1
A start may be to make Student extend a base object which has createdAt and updatedAt. Not sure if you can use that somehow to make * less verbose. Only throwing ideas - not a Scala/Slick expert. – User

1 Answers

1
votes

I'm using the following pattern:

case class Common(arg0: String, arg1: String)

trait Commons { this: Table[_] =>
  def arg0 = column[String]("ARG0", O.Length(123))
  def arg1 = column[String]("ARG1", O.Length(123))

  def common = (arg0, arg1).<> [Meta, (String, String)](
    r => {
      val (arg0, arg1) = r
      Meta(arg0, arg1)
    },
    o => Some(o.arg0, o.arg1)
  )
}

case class MyRecord(a: String, b: String, common: Common)

class MyRecords(tag: Tag) extends Table[MyRecord](tag, "MY_RECORDS") with Commons {
  def a = column[String]("A", O.PrimaryKey, O.Length(123))
  def b = column[String]("B", O.Length(123))

  def * = (a, b, common) <> (MyRecord.tupled, MyRecord.unapply)
}

It's not perfect but it helps avoid duplication without it being to difficult to understand.