Reputation: 747
I have two models (say, Supplier and Coffee) and Coffee model has foreign key reference to Supplier model. During ddl, I want this relationship to exist in table creation. But I also want to be able to refer the Supplier object through Coffee object like coffeeObj.supplier.name
. Below is my dummy code. I am using MappedTable, foreignKey and TypeMapper.
import scala.slick.driver.H2Driver.simple._
import Database.threadLocalSession
object SlickTest {
// Supplier
case class Supplier(id: Option[Int], name: String)
object Suppliers extends Table[Supplier]("supplier") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def * = id.? ~ name <> (Supplier, Supplier.unapply _)
def findOneById(id: Int): Supplier =
this.map { e => e }.where(r => r.id === id).firstOption.get
}
// Implicit Conversion from Supplier to Int
implicit val supTypeMapper = MappedTypeMapper.base[Supplier, Int](
{ s => s.id.get }, { id => Suppliers.findOneById(id) })
// Coffee
case class Coffee(name: Option[String], sup: Supplier, price: Double)
object Coffees extends Table[Coffee]("coffee") {
def name = column[String]("cof_name", O.PrimaryKey)
def sup = column[Supplier]("supplier")
def price = column[Double]("price")
def * = name.? ~ sup ~ price <> (Coffee, Coffee.unapply _)
def supplier = foreignKey("SUP_FK", sup, Suppliers)(_.id)
}
}
The code is failing at the last line for the definition of supplier
. Could anyone shed any light?
Upvotes: 8
Views: 9698
Reputation: 11270
In Slick you don't map to case classes that reference other case classes. To resolve foreign keys you use queries instead, which you can put into methods for re-usability.
Also see my post at: https://groups.google.com/d/msg/scalaquery/esFb2DjoHVE/NtMj7BmpE94J
EDIT: You can't follow a reference in coffeeObj and that is a good thing. Because that would require configuring a loading strategy like in ORMs, which would be more complicated and would make the execution behavior of your code less obvious.
Lazy loading would probably load the coffeeObj in the controller and the supplier in the view, which seems odd, right? You can do this:
Remove .firstOption.get
from your findOneById
method and then:
val supplierQuery = Query(Suppliers).findById(id)
val coffeeQuery = supplierQuery.join(Coffee).on(...).map(_._2)
// here is what you want, a supplier and the corresponding coffee:
val supplierWithCoffee = (supplierQuery.firstOption,coffeeQuery.firstOption)
Put the join into a function to save boiler plate.
Upvotes: 8