Defining projection to map to nested case classes
Asked Answered
R

1

7

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.

Rollin answered 29/11, 2014 at 13:51 Comment(0)
P
3

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

Presnell answered 10/12, 2014 at 5:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.