具有多个 MappedColumnType 的平滑投影
Slick projection with multiple MappedColumnType
我有以下简化代码来反映我面临的问题。它使用 slick 3.1.1,scala 2.10.4 和 mysql.
我有一个用户 table,其中一列有 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,它工作正常;不幸的是,我的数据模型需要一个是可选的,另一个是必需的。
对这里发生的事情有什么想法吗?谢谢!
看起来真正重要的不是有两个映射列,而是它们除了 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
}
我有以下简化代码来反映我面临的问题。它使用 slick 3.1.1,scala 2.10.4 和 mysql.
我有一个用户 table,其中一列有 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,它工作正常;不幸的是,我的数据模型需要一个是可选的,另一个是必需的。 对这里发生的事情有什么想法吗?谢谢!
看起来真正重要的不是有两个映射列,而是它们除了 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
}