如何转换Slick 2.1.0 MappedProjection,使其所有列都是选项

时间:2016-05-13 18:15:27

标签: scala slick-2.0

假设我有一个名为Blarg的案例类,它由具有基本类型的属性组成:

case class Blarg(
  x: String,
  y: Int
)

Blarg用于各种类,有时用作Option[Blarg],有时用作List[Blarg],并且这些类是持久的。我们还有一个名为OptionalComplexThing的案例类,其中包含Option[Blarg]类型的属性:

case class OptionalComplexThing(
  one: String,
  maybeInt: Option[Int],
  maybeBlarg: Option[Blarg],
  id: Option[Long] = None
)

问题是如何创建包含相应子投影的默认MappedProjection。请注意下面的blarg MappedProjection。我希望能够转换blarg MappedProjection,以使其Column的所有Option都为class _OptionalComplexThings(tag: Tag) extends Table[OptionalComplexThing](tag, "optional_complex_thing") { def one = column[String]("one") def maybeInt = column[Option[Int]]("maybe_int") // specifying O.Nullable makes no difference def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc) // Blarg fields need to have their lifted types remapped according to MappedProjection usage. // Notice OptionalComplexThing's maybeBlarg property is an Option[Blarg]. def x = column[String]("x") // want to map to column[Option[String]]("x") def y = column[Int]("y") // want to map to column[Option[String]]("y") def blarg = (x, y) <> (Blarg.tupled, Blarg.unapply) def * = (one, maybeInt, Option(blarg), id) <> (OptionalComplexThing.tupled, OptionalComplexThing.unapply) /* Error:(23, 46) No matching Shape found. Slick does not know how to map the given types. Possible causes: T in Table[T] does not match your * projection. Or you use an unsupported type in a Query (e.g. scala List). Required level: scala.slick.lifted.FlatShapeLevel Source type: (scala.slick.lifted.Column[String], scala.slick.lifted.Column[Option[Int]], Option[scala.slick.lifted.MappedProjection[Blarg,(String, Int)]], scala.slick.lifted.Column[Option[Long]]) Unpacked type: (String, Option[Int], Option[Blarg], Option[Long]) Packed type: Any def * = (one, maybeInt, Option(blarg), id) <> (OptionalComplexThing.tupled, OptionalComplexThing.unapply) ^ */ } 。我不想对这个例子进行硬编码 - 使用组合器应该有一种通用的方法。

System.out

Here is a a GitHub project证明了这个问题。

1 个答案:

答案 0 :(得分:0)

您可以编写自定义版本的tupled / unapply方法:

case object Blarg {
  def fromOptions(maybeX: Option[String], maybeY: Option[Int]): Option[Blarg] = {
    (maybeX, maybeY) match {
      case (Some(x), Some(y)) => Some(Blarg(x, y))
      case _ => None
    }
  }

  def customTupled = (fromOptions _).tupled
  def customUnapply(arg: Option[Blarg]): Option[(Option[String], Option[Int])] = arg.map { blarg =>
    (Some(blarg.x), Some(blarg.y))
  }
}

class _OptionalComplexThings(tag: Tag) extends Table[OptionalComplexThing](tag, "optional_complex_thing") {
  def one      = column[String]("one")
  def maybeInt = column[Option[Int]]("maybe_int")
  def id       = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
  def maybeX   = column[Option[String]]("x")
  def maybeY   = column[Option[Int]]("y")

  def blarg = (maybeX, maybeY) <> (Blarg.customTupled, Blarg.customUnapply)
  def * = (one, maybeInt, blarg, id) <> (OptionalComplexThing.tupled, OptionalComplexThing.unapply)
}

这可能不是最短的解决方案,但应该有效。