使用PostgreSQL和Slick的自动递增字段

Jac*_*ack 21 scala typesafe-stack slick

如何使用带有Slick映射表的AutoInc键将记录插入PostgreSQL?如果我在我的case类中使用和选择id并将其设置为None,那么PostgreSQL会在插入时抱怨该字段不能为null.这适用于H2,但不适用于PostgreSQL:

//import scala.slick.driver.H2Driver.simple._
//import scala.slick.driver.BasicProfile.SimpleQL.Table
import scala.slick.driver.PostgresDriver.simple._
import Database.threadLocalSession

object TestMappedTable extends App{

    case class User(id: Option[Int], first: String, last: String)

    object Users extends Table[User]("users") {
        def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
        def first = column[String]("first")
        def last = column[String]("last")
        def * = id.? ~ first ~ last <> (User, User.unapply _)
        def ins1 = first ~ last returning id
        val findByID = createFinderBy(_.id)
        def autoInc = id.? ~ first ~ last <> (User, User.unapply _) returning id
    }

 // implicit val session = Database.forURL("jdbc:h2:mem:test1", driver = "org.h2.Driver").createSession()
    implicit val session = Database.forURL("jdbc:postgresql:test:slicktest",
                           driver="org.postgresql.Driver",
                           user="postgres",
                           password="xxx")

  session.withTransaction{
    Users.ddl.create

    // insert data
    print(Users.insert(User(None, "Jack", "Green" )))
    print(Users.insert(User(None, "Joe", "Blue" )))
    print(Users.insert(User(None, "John", "Purple" )))
    val u = Users.insert(User(None, "Jim", "Yellow" ))
  //  println(u.id.get)
    print(Users.autoInc.insert(User(None, "Johnathan", "Seagul" )))
  }
  session.withTransaction{
    val queryUsers = for {
    user <- Users
  } yield (user.id, user.first)
  println(queryUsers.list)

  Users.where(_.id between(1, 2)).foreach(println)
  println("ID 3 -> " + Users.findByID.first(3))
  }
}
Run Code Online (Sandbox Code Playgroud)

使用上面的H2成功,但如果我发表评论并改为PostgreSQL,那么我得到:

[error] (run-main) org.postgresql.util.PSQLException: ERROR: null value in column "id" violates not-null constraint
org.postgresql.util.PSQLException: ERROR: null value in column "id" violates not-null constraint
Run Code Online (Sandbox Code Playgroud)

小智 15

这是在这里工作:

object Application extends Table[(Long, String)]("application") {   
    def idlApplication = column[Long]("idlapplication", O.PrimaryKey, O.AutoInc)
    def appName = column[String]("appname")
    def * = idlApplication ~ appName
    def autoInc = appName returning idlApplication
}

var id = Application.autoInc.insert("App1")
Run Code Online (Sandbox Code Playgroud)

这是我的SQL看起来的样子:

CREATE TABLE application
(idlapplication BIGSERIAL PRIMARY KEY,
appName VARCHAR(500));
Run Code Online (Sandbox Code Playgroud)

更新:

关于具有User的映射表(如问题中)的具体问题可以解决如下:

  def forInsert = first ~ last <>
    ({ (f, l) => User(None, f, l) }, { u:User => Some((u.first, u.last)) })
Run Code Online (Sandbox Code Playgroud)

这是来自Slick git存储库中的测试用例.

  • 当表格更大时可以做些什么?可能是15-20列......`forInsert`方法变成了怪物! (2认同)

Leo*_*s A 11

我以不同的方式解决了这个问题.因为我希望我的User对象在我的应用程序逻辑中始终具有id,并且在插入数据库期间唯一没有它的地方,我使用了一个NewUser没有id 的辅助case类.

case class User(id: Int, first: String, last: String)
case class NewUser(first: String, last: String)

object Users extends Table[User]("users") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def first = column[String]("first")
  def last = column[String]("last")

  def * = id ~ first ~ last <> (User, User.unapply _)
  def autoInc = first ~ last <> (NewUser, NewUser.unapply _) returning id
}

val id = Users.autoInc.insert(NewUser("John", "Doe"))
Run Code Online (Sandbox Code Playgroud)

同样,如果您想避免使用额外的case类,User则NewUser可以将1:1映射到数据库条目/行,同时可以替换为元组,因为它仅用作insert调用的数据容器.

编辑: 如果你想要更安全(稍微增加冗长度),你可以使用案例类的特征,如下所示:

trait UserT {
  def first: String
  def last: String
}
case class User(id: Int, first: String, last: String) extends UserT
case class NewUser(first: String, last: String) extends UserT
// ... the rest remains intact
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您首先将模型更改应用于特征(包括您可能需要的任何mixin),并可选择将默认值添加到特征NewUser.

作者的观点:我仍然更喜欢无特性解决方案,因为它更紧凑,对模型的更改是复制粘贴User参数,然后删除id(auto-inc主键),在案例类声明和表中预测.