如何使用可为空的列编写巧妙的 table 定义?
How do I write slick table definitions with nullable columns?
这个 table 定义一直有效,直到我意识到具有可为空的列意味着我需要使用 Option[String] 而不仅仅是 String。这是我所做的唯一更改,这就是我的代码现在的样子。
class RespondentTableDef(tag: Tag) extends Table[Respondent](tag, "respondent") {
def id = column[Long]("id", O.PrimaryKey)
def uuid = column[String]("uuid")
def version = column[Long]("version")
def task = column[Long]("task")
def firstName = column[Option[String]]("first_name")
def lastName = column[Option[String]]("last_name")
def ageGroup = column[Option[String]]("age_group")
def incomeLevel = column[Option[String]]("income_level")
def employmentStatus = column[Option[String]]("employment_status")
def maritalStatus = column[Option[String]]("marital_status")
def housingStatus = column[Option[String]]("housing_status")
def educationStatus = column[Option[String]]("education_status")
def gender = column[Option[String]]("gender")
override def * =
(id, uuid, version, task, firstName, lastName, ageGroup, incomeLevel, employmentStatus, maritalStatus, housingStatus, educationStatus, gender) <> (Respondent.tupled, Respondent.unapply)
}
我在编译时遇到这个错误。
[error] /Users/roy/adivinate/survey2/app/model/Respondent.scala:45: No matching Shape found.
[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[String], slick.lifted.Rep[Long], slick.lifted.Rep[Long], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[String]])
[error] Unpacked type: (Long, String, Long, Long, String, String, String, String, String, String, String, String, String)
[error] Packed type: Any
[error] (id, uuid, version, task, firstName, lastName, ageGroup, incomeLevel, employmentStatus, maritalStatus, housingStatus, educationStatus, gender) <> (Respondent.tupled, Respondent.unapply)
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 5 s, completed Dec 21, 2016 8:53:17 PM
这基本上是一件简单的事情 - 您的案例 class 需要将这些字段设为可选。例如。而不是(在你的情况下 class): firstName: String
你应该 firstName: Option[String]
.
在模型 class 中将您的字段声明为 Option[T]
而不是 T
以使相应的列可空
让我们用一个例子来理解这一点
case class Foo(name: String, rating: Option[Int])
class Foos(tag: Tag) extends Table[Foo](tag, "foos") {
def name = column[String]("name") //name is not null
def rating = column[Option[Int]]("rating") //rating is nullable
def * = (name, rating) <> (Foo.tupled, Foo.unapply)
}
如果你想让某些东西可以为空,只需将其声明为 Option
字段,它就会理解并生成 sql 并将该特定字段设为可为空。
以上设计与Scala Option设计无缝衔接。意思是Scala中的option直接转换为sql.
中的Nullable
旧版本的 Slick
您必须通过在列声明中显式传递 O.NotNull 来告诉特定列不为空,但在新版本的 slick
中不需要
不要在列的定义中使用选项。如果你有 Option[String] 列应该仍然是 String.
def firstName = column[Option[String]]("first_name") //Bad
def firstName = column[String]("first_name") //Good
定义 select (def *) 时使用 .?定义选项
override def * =
(id, uuid, version, task, firstName.?, lastName.?, ageGroup.?, incomeLevel.?, employmentStatus.?, maritalStatus.?, housingStatus.?, educationStatus.?, gender.?) <> (Respondent.tupled, Respondent.unapply)
此外,如果您要更新(在 TableQuery 中):
def updateExistingName(name : String) : DBIO[Int] =
{
map(_.firstName).update(name)
}
def updateOptionName(nameCanBeNone: Option[String]) : DBIO[Int] =
{
map(_.firstName.?).update(nameCanBeNone)
}
这个 table 定义一直有效,直到我意识到具有可为空的列意味着我需要使用 Option[String] 而不仅仅是 String。这是我所做的唯一更改,这就是我的代码现在的样子。
class RespondentTableDef(tag: Tag) extends Table[Respondent](tag, "respondent") {
def id = column[Long]("id", O.PrimaryKey)
def uuid = column[String]("uuid")
def version = column[Long]("version")
def task = column[Long]("task")
def firstName = column[Option[String]]("first_name")
def lastName = column[Option[String]]("last_name")
def ageGroup = column[Option[String]]("age_group")
def incomeLevel = column[Option[String]]("income_level")
def employmentStatus = column[Option[String]]("employment_status")
def maritalStatus = column[Option[String]]("marital_status")
def housingStatus = column[Option[String]]("housing_status")
def educationStatus = column[Option[String]]("education_status")
def gender = column[Option[String]]("gender")
override def * =
(id, uuid, version, task, firstName, lastName, ageGroup, incomeLevel, employmentStatus, maritalStatus, housingStatus, educationStatus, gender) <> (Respondent.tupled, Respondent.unapply)
}
我在编译时遇到这个错误。
[error] /Users/roy/adivinate/survey2/app/model/Respondent.scala:45: No matching Shape found.
[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[String], slick.lifted.Rep[Long], slick.lifted.Rep[Long], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[String]], slick.lifted.Rep[Option[String]])
[error] Unpacked type: (Long, String, Long, Long, String, String, String, String, String, String, String, String, String)
[error] Packed type: Any
[error] (id, uuid, version, task, firstName, lastName, ageGroup, incomeLevel, employmentStatus, maritalStatus, housingStatus, educationStatus, gender) <> (Respondent.tupled, Respondent.unapply)
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 5 s, completed Dec 21, 2016 8:53:17 PM
这基本上是一件简单的事情 - 您的案例 class 需要将这些字段设为可选。例如。而不是(在你的情况下 class): firstName: String
你应该 firstName: Option[String]
.
在模型 class 中将您的字段声明为 Option[T]
而不是 T
以使相应的列可空
让我们用一个例子来理解这一点
case class Foo(name: String, rating: Option[Int])
class Foos(tag: Tag) extends Table[Foo](tag, "foos") {
def name = column[String]("name") //name is not null
def rating = column[Option[Int]]("rating") //rating is nullable
def * = (name, rating) <> (Foo.tupled, Foo.unapply)
}
如果你想让某些东西可以为空,只需将其声明为 Option
字段,它就会理解并生成 sql 并将该特定字段设为可为空。
以上设计与Scala Option设计无缝衔接。意思是Scala中的option直接转换为sql.
中的Nullable旧版本的 Slick
您必须通过在列声明中显式传递 O.NotNull 来告诉特定列不为空,但在新版本的 slick
中不需要不要在列的定义中使用选项。如果你有 Option[String] 列应该仍然是 String.
def firstName = column[Option[String]]("first_name") //Bad
def firstName = column[String]("first_name") //Good
定义 select (def *) 时使用 .?定义选项
override def * =
(id, uuid, version, task, firstName.?, lastName.?, ageGroup.?, incomeLevel.?, employmentStatus.?, maritalStatus.?, housingStatus.?, educationStatus.?, gender.?) <> (Respondent.tupled, Respondent.unapply)
此外,如果您要更新(在 TableQuery 中):
def updateExistingName(name : String) : DBIO[Int] =
{
map(_.firstName).update(name)
}
def updateOptionName(nameCanBeNone: Option[String]) : DBIO[Int] =
{
map(_.firstName.?).update(nameCanBeNone)
}