SLICK中的伴生对象映射投影

10

我有嵌套类/对象,并希望使用SLICK在数据库中存储(和检索)它们。我知道使用SLICK的映射投影将是关键。此外,我使用伴生对象来映射嵌套对象和平面结构(以存储在DB表中)。我想做类似于这样的事情(简化示例):

case class Foo(id: Int, myBar: Bar)

case class Bar(myInt: Int, myString: String)

object Foo {
  def apply(id: Int, myInt: Int, myString: String): Foo = Foo(id, Bar(myInt, myString))

  override def unapply(f: Foo) = (f.id, f.myBar.myInt, f.myBar.myString)
}

object TTable extends Table[Foo]("FOO") {
    def id = column[Int]("id",  O.PrimaryKey)
    def myInt = column[Int]("myInt", O NotNull)
    def myString = column[String]("myString", O NotNull)

    def * = id ~ myInt ~ myString <> (Foo.apply _, Foo.unapply _)

    def query(db: Database, id: Int): Option[Foo] = db withSession { //s: Session =>
        (for { b <- TTable if b.id is id} yield b).firstOption
    }
}
但编译失败,出现多个错误:"方法unapply定义了两次"、"对重载定义的引用不明确,两个方法apply[...]匹配了期望的类型?"和"具有替代方案的重载方法值<>"。
我在这里找到了关于mapped projection(映射投影)的优秀解释 "scala slick method I can not understand so far" 和 "Mapped projection with <> to a case class with companion object in Slick",但是没有一种建议的解决方案适用于我。
1个回答

20

你可以通过传递执行你想要的操作的lambda函数来代替使用unapplyapply

  def * = id ~ myInt ~ myString <> (
    (id,myInt,myString) => Foo(id, Bar(myInt, myString)),    /* from a row to a Foo */
    (f:Foo) => Some((f.id, f.myBar.myInt, f.myBar.myString)) /* and back */)

这种方式让表行到案例类的映射保持在表定义中,而案例类则保持为简单的案例类,这样也不错。

另一种方法是不使用案例类Foo,而是使用普通类,这样可以在伴生对象中自定义applyunapply的定义:

// untested code
class Foo private (val id: Int, val myBar: Bar) 
case class Bar(myInt: Int, myString: String)
object Foo {
  def apply(id: Int, myInt: Int, myString: String): Foo = new Foo(id, Bar(myInt, myString))
  def unapply(f: Foo) = Some((f.id, f.myBar.myInt, f.myBar.myString))
}

如果你想要执行def * = id ~ myInt ~ myString <> (Foo.apply _, Foo.unapply _)

你将会得到类似于 case class 的用法,但是可能会错过其他好东西,比如像实际 case classes 一样免费获得的 equalstoString。我宁愿保留 case classes(及其默认的applyunapply),这样它们就可以按照通常的约定被视为代数数据类型。

真正的问题在于 case classes 有自己的unapply,因此您不能在 companion class 中拥有一个类似的方法(同名和相同的参数)(就我所知)。您可以简单地使用另一个方法名称。毕竟,您想要做的事情与unapply在语义上并不等价:

object Foo {
  def fromRow(id: Int, myInt: Int, myString: String): Foo = Foo(id, Bar(myInt, myString))
  def toRow(f: Foo) = Some((f.id, f.myBar.myInt, f.myBar.myString))
}

然后在你的表模式中:

def * = id ~ myInt ~ myString <> (Foo.fromRow _, Foo.toRow _)

1
非常感谢。事实上,我更喜欢你的第一种解决方案,它非常有效。但是对于大约十几个参数,整个映射似乎有很多样板文件。我了解SLICK的直接嵌入更紧凑,但还不允许插入。我期待着看到SLICK在这方面的进展。 - jans
1
即使您能够按照您在问题中描述的方式使用applyunapply,您仍然必须处理数十个参数,对吧?顺便说一下,请查看我的最后一次编辑。 - Faiz
1
通过一个 case class 和你最后的编辑,我得到了一个“对方法 'apply' 的多重定义引用不明确”的错误。因此,我不得不将 companion object 中的方法重命名为 'fromRow'。 - jans
5
如何在Slick 2中执行第一项选项? - NightWolf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接