Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defining projection to map to nested case classes

I have these case classes:

case class PolicyHolder(id : String, firstName : String, lastName : String)
case class Policy(address : Future[Address], policyHolder : Future[PolicyHolder], created : RichDateTime, duration : RichDuration )

I then have a slick schema defined for Policy

class PolicyDAO(tag: Tag) extends Table[Policy](tag, "POLICIES") with DbConfig {
  def address = column[String]("ADDRESS", O.PrimaryKey)
  def policyHolder = foreignKey("POLICY_HOLDER_FK", address, TableQuery[PolicyHolderDAO])(_.id)

  def created = column[RichDateTime]("CREATED")
  def duration = column[String]("DURATION")

  def * = (address, policyHolder, created, duration) <> (Policy.apply, Policy.unapply)
}

What is the best way for me to define this projection correctly to map the policyHolder field inside of my Policy case class from the foreign key value to an actual instance of the PolicyHolder case class.

like image 808
Chris Stewart Avatar asked Nov 29 '14 13:11

Chris Stewart


1 Answers

Our solution to this problem is to place the foreign key id in the case class, and to then use a lazy val or a def (the latter possibly being backed by a cache) to retrieve the record using the key. This is assuming that your PolicyHolders are stored in a separate table - if they're denormalized but you want to treat them as separate case classes then you can have the lazy val / def in Policy construct a new case class instead of retrieving the record using the foreign key.

class PolicyDAO(tag: Tag) extends Table[Policy](tag, "POLICIES") with DbConfig {
  def address = column[String]("ADDRESS", O.PrimaryKey)
  def policyHolderId = column[String]("POLICY_HOLDER_ID")

  def created = column[RichDateTime]("CREATED")
  def duration = column[String]("DURATION")

  def * = (address, policyHolderId, created, duration) <> (Policy.apply, Policy.unapply)
}

case class Policy(address : Future[Address], policyHolderId : Future[String], created : RichDateTime, duration : RichDuration ) {
  lazy val policyHolder = policyHolderId.map(id => PolicyHolderDAO.get(id))
}

We also used a common set of create/update/delete methods to account for the nesting, so that when a Policy is committed its inner PolicyHolder will also be committed; we used a CommonDAO class that extended Table and had the prototypes for the create/update/delete methods, and then all DAOs extended CommonDAO instead of Table and overrode create/update/delete as necessary.


Edit: To cut down on errors and reduce the amount of boilerplate we had to write, we used Slick's code generation tool - this way the CRUD operations could be automatically generated from the schema

like image 55
Zim-Zam O'Pootertoot Avatar answered Oct 19 '22 09:10

Zim-Zam O'Pootertoot