如何与 Slick 建立反向关系模型?

How to model an inverse relationship with Slick?

我正在尝试在 Slick 3.1.0-M1 中建立多对多关系模型

这是 Slick 文档的示例

// 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]

我希望能够将 Suppliers 的案例 class 写成这样

case class Supplier(
  id: Int,
  name: String,
  street: String,
  city: String,
  state: String,
  zip: String,
  coffees: List[Coffee]
)

我正在尝试这样做,但目前我无法让它工作。我还希望有一些方法允许以级联模式更新供应商对象和内部的咖啡对象。

答案很简单:你不能SlickFRM(函数关系映射),简单地说 - 它将关系元组映射到 Scala 对象(通常是 tuples / case classes)。你想要实现的东西在标准SQL中不容易表达,因此在Slick中不能直接表达。我在这里特别提到 standard SQL - 因为我知道某些数据库允许您将某些字段分组和聚合到列表或数组中 - 这远远超出了标准 SQL 范围,老实说,我什至不确定你是否能够在一般情况下做到这一点。

您可以通过在两个 table 之间执行 SELECTJOIN 以及稍后的分组结果(这里重要的是 - 结果,我说的不是SQLGROUP BY)获取coffees列表。

这可能是这样的:

首先 - 将您的 table 定义映射到案例 classes - 所以而不是:

class Coffees(tag: Tag) extends Table[(String, Int, Double, Int, Int)]

这样做:

class Coffees(tag: Tag) extends Table[Coffee]

并可能将您现有的 Supplier 案例 class 重命名为 SupplierComposite 或任何其他暗示它不严格映射到 db table 而是一个两个不同 tables.

的组合

Supplier。 这不是严格要求的(你可以使用你的元组)——它只会让事情变得更容易。

然后你会运行你的查询是这样的:

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)
                )
            }
    }
)

不可能无论如何实现这样的事情:

Also I would like to have methods that allow to update the Supplier object and the Coffee object inside in a cascade mode.

这些功能根本不属于Slick试图实现的。这绝对是在 classic ORMs 的范围内 - 就像 Hibernate or (from Scala world) eBean.

但是请注意 - 这个功能(上面提到的)基本上是 ORMs 中固有的问题根源的起点之一 - 即 object relational impedance mismatch - 并且恰恰是Slick 想要避免的事情。