Hi all,
picking up this rather old thread.
I have basically the same question, with slick 1.0.1. I have a Product
with (one-to-many) ProductAttributes, the case classes and table mappings
look like this:
case class Product(id: Option[Long], ...)
object Products extends Table[Product]("products") {
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
...
}
case class ProductAttribute(id: Option[Long] = None, productId: Long, name: String, value: String)
object ProductAttributes extends Table[ProductAttribute]("product_attributes") {
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def productId = column[Long]("product_id")
def name = column[String]("name")
def value = column[String]("value")
def product = foreignKey("product_fk", productId, Products)(_.id)
...
}
To load a product with its attributes I came up with this query:
def findById(id: Long)(implicit s:Session): Option[(Product, List[ProductAttribute])] = {
def pa = for {
id <- Parameters[Long]
(p, a) <- Products leftJoin ProductAttributes on (_.id === _.productId)
} yield (p, a)
val emptyMap = Map.empty[Product, List[ProductAttribute]].withDefaultValue(Nil)
val map = pa.foldLeft(id, emptyMap){ (res, entry) =>
res.updated(entry._1, entry._2 :: res(entry._1))
}
map.find{ case (p, as) => p.id.get == id }
}
Are there better ways to do this?
An issue of this solution is, that products that don't have any attributes
are not found, instead an error is thrown ("SlickException: Read NULL value
for column product_attributes.product_id").
To come around this the
yield (p, a)
could be changed to
yield (p, a.id.?, a.name.?, a.value.?)
The disadvantage is that the ProductAttribute has to be constructed manually then,
which is ok for a ProductAttribute because it has only 3 or 4 properties, but for classes
with much more properties it's getting more of a hurdle.
Is it planned to support s.th. like "yield (p, a.?)", or are there other solutions for this? What's the common way to handle one-to-many associations with left/outer joins?
Thanx && cheers,
Martin