问题
I am trying to model a relationship many-to-many in Slick 3.1.0-M1
This is the example of the Slick documentation
// Definition of the SUPPLIERS table
class Suppliers(tag: Tag) extends Table[(Int, String, String, String, String, String)](tag, "SUPPLIERS") {
def id = column[Int]("SUP_ID", O.PrimaryKey) // This is the primary key column
def name = column[String]("SUP_NAME")
def street = column[String]("STREET")
def city = column[String]("CITY")
def state = column[String]("STATE")
def zip = column[String]("ZIP")
// Every table needs a * projection with the same type as the table's type parameter
def * = (id, name, street, city, state, zip)
}
val suppliers = TableQuery[Suppliers]
// Definition of the COFFEES table
class Coffees(tag: Tag) extends Table[(String, Int, Double, Int, Int)](tag, "COFFEES") {
def name = column[String]("COF_NAME", O.PrimaryKey)
def supID = column[Int]("SUP_ID")
def price = column[Double]("PRICE")
def sales = column[Int]("SALES")
def total = column[Int]("TOTAL")
def * = (name, supID, price, sales, total)
// A reified foreign key relation that can be navigated to create a join
def supplier = foreignKey("SUP_FK", supID, suppliers)(_.id)
}
val coffees = TableQuery[Coffees]
I would like to be able to write the case class of Suppliers as this
case class Supplier(
id: Int,
name: String,
street: String,
city: String,
state: String,
zip: String,
coffees: List[Coffee]
)
I am trying to do it, but at the moment I can not make it work. Also I would like to have methods that allow to update the Supplier object and the Coffee object inside in a cascade mode.
回答1:
The answer is simply: you cannot. Slick
is FRM
(functional relational mapping) and to put it simply - it maps relational tuples onto Scala objects (usually tuples
/ case classes
). What you want to achieve is not expressed easily in standard SQL, therefore it is not expressible directly in Slick
. I am specifically mentioning standard SQL here - as I am aware that some databases allow you to group and aggregate certain fields into lists or arrays - this is however far outside of standard SQL scope and honestly I am not even sure you would be able to do it in general case.
You can to some extent mimic above in queries by doing SELECT
with JOIN
between two tables and later grouping results (important thing here is - results, I am not talking about SQL GROUP BY
) to obtain coffees
list.
This could be something like this:
First - map your table definitions to cases classes - so instead of:
class Coffees(tag: Tag) extends Table[(String, Int, Double, Int, Int)]
do this:
class Coffees(tag: Tag) extends Table[Coffee]
and perhaps rename your existing Supplier
case class to SupplierComposite
or whatever else that implies that it doesn't map strictly to db table but is rather a composition of two different tables.
Same with Supplier
.
It is not strictly required (you would be fine with your tuples) - it will only make things easier.
Then you would run your query like this:
db.run(
(
suppliers
join coffees on (_.id === _.supID)
)
.result
.map { results: Seq[(Supplier, Coffee)] =>
results
.groupBy(_._1)
.map { case (supp, groupped) =>
SupplierComposite(
id = supp.id,
name = supp.name,
...
coffees = groupped.map(_._2)
)
}
}
)
It is not possible whatsoever to achieve things like this:
Also I would like to have methods that allow to update the Supplier object and the Coffee object inside in a cascade mode.
Such functions simply don't belong to what Slick
tries to achieve. It is definitely though within reach of classic ORM
s - like Hibernate or (from Scala world) eBean.
Be warned however - this feature (mentioned above) is basically one of the starting points of the root of the problem that is inherent in ORM
s - namely object relational impedance mismatch - and is precisely the thing that Slick
wanted to avoid.
来源:https://stackoverflow.com/questions/42211504/how-to-model-an-inverse-relationship-with-slick