具有多个MappedColumnType

时间:2018-01-23 23:53:08

标签: scala slick slick-3.0

我有以下简化代码来反映我面临的问题。它使用了sclick 3.1.1,scala 2.10.4和mysql。

我有一个User表,其中一列有Option [Seq [String]],另一列是Seq [String]。

有2个MappedColumnType;将Seq [String]转换为String,将Option [Seq [String]]转换为String

以下是简化代码:

package models

import slick.driver.MySQLDriver.api._

case class User(id: Long,
                name: Option[String],
                cities: Option[Seq[String]] = None,
                countries: Seq[String])

class UserMapping(tag: Tag) extends Table[User](tag, "USERS") {

  implicit val stringListMapper = MappedColumnType.base[Seq[String], String](
    list => list.mkString(","),
    string => string.split(',').toSeq
  )

  implicit val stringOptionalListMapper = MappedColumnType.base[Option[Seq[String]], String](
    list => list.get.mkString(","),
    string => Some(string.split(',').toSeq)
  )

  def id: Rep[Long] = column[Long]("ID", O.PrimaryKey, O.AutoInc)
  def name: Rep[Option[String]] = column[Option[String]]("NAME")
  def cities: Rep[Option[Seq[String]]] = column[Option[Seq[String]]]("CITIES")(stringOptionalListMapper)
  def countries: Rep[Seq[String]] = column[Seq[String]]("COUNTRIES")(stringListMapper)

  // scalastyle:off method.name public.methods.have.type
  def * = (id, name, cities, countries) <> (User.tupled, User.unapply)
  // scalastyle:on method.name public.methods.have.type

}

编译器在投影中失败:

[error] Slick does not know how to map the given types.
[error] Possible causes: T in Table[T] does not match your *     projection. Or you use an unsupported type in a Query (e.g. scala List).
[error]   Required level: slick.lifted.FlatShapeLevel
[error]      Source type: (slick.lifted.Rep[Long], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[Seq[String]]], slick.lifted.Rep[Seq[String]])
[error]    Unpacked type: (Long, Option[String], Option[Seq[String]], Seq[String])
[error]      Packed type: Any
[error]   def * = (id, name, cities, countries) <> (User.tupled, User.unapply)
[error]                                         ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 2 s, completed Jan 23, 2018 3:24:23 PM

如果我在这里只定义了一个MappedColumnType,它可以正常工作;遗憾的是,我的数据模型需要一个是可选的,另一个是必需的。 关于这里发生了什么的任何想法?谢谢!

1 个答案:

答案 0 :(得分:1)

看起来真正重要的不是有两个映射列,而是除了Option之外它们具有相同的形状。这很糟糕,因为它会让您为映射引入两个implicit val,这使得它们在(id, name, cities, countries)转换为ProvenShape

时不明确

如果这种形状的逻辑实际上与你的例子中的相同,那么Slick似乎能够自己添加Option包装器,所以你可以只使用一个(非Option )隐含的如:

class UserMapping(tag: Tag) extends Table[User](tag, "USERS") {

  implicit val stringListMapper = MappedColumnType.base[Seq[String], String](
    list => list.mkString(","),
    string => string.split(',').toSeq
  )

  def id: Rep[Long] = column[Long]("ID", O.PrimaryKey, O.AutoInc)

  def name: Rep[Option[String]] = column[Option[String]]("NAME")

  def cities: Rep[Option[Seq[String]]] = column[Option[Seq[String]]]("CITIES")   // share stringListMapper

  def countries: Rep[Seq[String]] = column[Seq[String]]("COUNTRIES")        // share stringListMapper

  // scalastyle:off method.name public.methods.have.type
  def * = (id, name, cities, countries) <> (User.tupled, User.unapply)
  // scalastyle:on method.name public.methods.have.type

}

但是如果你运气不好并且相同形状的映射实际上是不同的,那么你需要将它们显式地传递给column(例如,如果分隔符字符不同),那么你必须明确地提供一个implicit适当Shape的证据,例如:

class UserMapping(tag: Tag) extends Table[User](tag, "USERS") {

  val stringListMapper = MappedColumnType.base[Seq[String], String](
    list => list.mkString(","),
    string => string.split(',').toSeq
  )

  val stringOptionalListMapper = MappedColumnType.base[Option[Seq[String]], String](
    list => list.get.mkString(","),
    string => Some(string.split(',').toSeq)
  )

  def id: Rep[Long] = column[Long]("ID", O.PrimaryKey, O.AutoInc)

  def name: Rep[Option[String]] = column[Option[String]]("NAME")

  def cities: Rep[Option[Seq[String]]] = column[Option[Seq[String]]]("CITIES")(stringOptionalListMapper)

  def countries: Rep[Seq[String]] = column[Seq[String]]("COUNTRIES")(stringListMapper)


  // explicitly provide proper Shape evidence
  import slick.lifted.Shape
  implicit val shape = Shape.tuple4Shape(
    Shape.repColumnShape(longColumnType),
    Shape.optionShape(Shape.repColumnShape(stringColumnType)),
    Shape.repColumnShape(stringOptionalListMapper),
    Shape.repColumnShape(stringListMapper))

  // scalastyle:off method.name public.methods.have.type
  def * = (id, name, cities, countries) <> (User.tupled, User.unapply)
  // scalastyle:on method.name public.methods.have.type

}