播放Scala和Slick3,在“NOT NULL DEFAULT CURRENT_TIMESTAMP”的列中出现“列xx不能为空”错误

问题描述 投票:1回答:4

我正在使用Play Framework 2.6和光滑的3.2.1创建一个Web应用程序。当我尝试将记录插入“USER”表时,其中created_at列中包含“NOT NULL DEFAULT CURRENT_TIMESTAMP”(我正在使用MySQL),数据库会抛出错误“Column'creved_at'不能为null”。

我知道光滑生成的SQL是错误的。该语句试图将null插入到created_at列中。让光滑生成不包含created_at列的SQL的正确方法是什么?

scala代码的摘录。

import org.joda.time.DateTime

case class User(
    id: Option[Long],
    accountId: Long,
    name: String,
    description: Option[String] = None,
    createdAt: Option[DateTime: = None,
)

class UserTable(tag: Tag) extends Table[User](tag, "user") {
  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def accountId = column[Long]("account_id")
  def name = column[String]("name")
  def description = column[Option[String]]("description")
  def createdAt = column[Option[DateTime]]("created_at")
  def * = (id.?, accountId, name, description, createdAt) <> (User.tupled, User.unapply)
}

object Users extends TableQuery(new UserTable(_)) {
}

val user = User(None, accountId, name, description)
val insert = for {
  newId <- (Users returning Users.map(_.id)) += user
} yield newId

db.run(insert)

生成SQL

[debug] s.j.J.statement - Preparing insert statement (returning: id): insert into `user` (`account_id`,`name`,`description`,`created_at`)  values (?,?,?,?)

[debug] s.j.J.parameter - /------+--------+---------+-----------\
[debug] s.j.J.parameter - | 1    | 2      | 3       | 4         |
[debug] s.j.J.parameter - | Long | String | VARCHAR | TIMESTAMP |
[debug] s.j.J.parameter - |------+--------+---------+-----------|
[debug] s.j.J.parameter - | 1    | user01 | NULL    | NULL      |
[debug] s.j.J.parameter - \------+--------+---------+-----------/
scala playframework slick slick-3.0
4个回答
1
投票

重新思考你的榜样。您的表格不接受null作为列created_at的值。但是你的域模型允许这个字段可以是None,它不能用null以任何其他方式在数据库中表示。所以,如果你想要光滑生成正确的查询,你必须将created_at的类型更改为DateTime


0
投票

createdAt不能是Option,你可以放在那里DateTime.now,以避免使用Option。还需要DateTime列映射器:

import slick.lifted.MappedTypeMapper
import java.sql.Date
import org.joda.time.DateTime
import slick.lifted.TypeMapper.DateTypeMapper

object DateTimeMapper {
  implicit def date2dt = MappedTypeMapper.base[DateTime, Date] (
    dt => new Date(dt.getMillis),
    date => new DateTime(date)
  )
}

或者你很可能需要它为Timestamp,而不是Date

 implicit def dateTime  =
      MappedColumnType.base[DateTime, Timestamp](
        dt => new Timestamp(dt.getMillis),
        ts => new DateTime(ts.getTime)
  )

如果你不想处理DateTime.now,你可能会这样做:

def created = column[DateTime]("createdAt", SqlType("timestamp not null default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP"))

其他信息:http://queirozf.com/entries/scala-slick-dealing-with-datetime-timestamp-attributes


0
投票

如果你看你的投影,你必须举起created_at,就像你举起你的身份。

所以你的预测会变成:

def * = (id.?, accountId, name, description, createdAt.?) <> (User.tupled, User.unapply)

注意创建后的.?


0
投票

我找到了两种解决这个问题的方法(使用Slick 3.2.3)。幸运的是,对于我的用例,我没有使用Slick来创建表或生成表类,所以我并不关心它将生成的模式是否有效。

假设我们有一个简单的用户模型/表:

case class User(id: Long, name: String, createdAt: Timestamp, updatedAt: Timestamp)

class UsersDAO(tag: Tag) extends Table[User](tag, "users") {
  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name")
  def createdAt = column[Timestamp]("created_at", O.AutoInc)
  def updatedAt = column[Timestamp]("updated_at", O.AutoInc)

  override def * = (id, name, createdAt, updatedAt) <>
    (User.tupled, User.unapply)
}

选项1:使用自定义插入方法

object UsersDAO extends TableQuery(new UsersDAO(_)) {
  def create(u: User) = this map { c =>
    (c.name) // only include columns you want to send
  } returning this.map(_.id) += (u.name)
}

选项2:将字段标记为O.AutoInc

如果你用createdAt标记updatedAtO.AutoInc列(如上所述),你可以简单地使用+=语法:

object UsersDAO extends TableQuery(new UsersDAO(_)) {
  def create(u: User) = this returning this.map(_.id) += user
}

目前似乎有几个与此相关的问题。希望一旦他们得到解决,就会有更好的方法。 https://github.com/slick/slick/issues/1448 https://github.com/slick/slick/pull/1533

© www.soinside.com 2019 - 2024. All rights reserved.