使用带有PostgreSQL和油滑自动递增字段(Using Auto Incrementing fi

2019-07-03 12:53发布

如何一个记录插入到PostgreSQL的使用自动递增键用油滑映射表? 如果我在我的情况下使用类和选项的ID,并将其设置为无,那么PostgreSQL将抱怨上插入该字段不能为空。 这适用于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))
  }
}

使用上面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

Answer 1:

这是工作在这里:

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")

这是我的SQL的样子:

CREATE TABLE application
(idlapplication BIGSERIAL PRIMARY KEY,
appName VARCHAR(500));

更新:

关于与用户(如在问题)一个映射表中的特定问题是可以解决的,如下所示:

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

这是从测试用例的油滑的Git仓库 。



Answer 2:

我以不同的方式解决这个问题。 因为我希望我的User对象总是在我的应用程序逻辑的ID,和其中一个不会有它插入到数据库中的唯一一点,我使用辅助NewUser不有一个id案例类。

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"))

同样, User映射1:1到数据库条目/行而NewUser可以通过一个元组所取代,如果你想避免额外的案例类,因为它只是用来作为一个数据容器insert调用。

编辑:如果您想了解更多安全(使用有所增加冗长),你可以使用一个性状为case类,如下所示:

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

在这种情况下,你将适用于第一个特征(包括任何混入您可能需要)模型的变化,还可以选择添加默认值到NewUser

笔者认为:我还是更喜欢无特点的解决方案,因为它是更紧凑,并转移到模型的复制粘贴的问题的User PARAMS然后除去id (自动增量主键),无论是在案件类的声明和在表预测。



Answer 3:

我们使用一个稍微不同的方法。 而不是创建一个进一步的投影,我们请求下一个ID为表,将其复制到case类,并使用默认的投影“*”插入表项。

Postgres的,它看起来是这样的:

让你的表对象实现这一特质

trait TableWithId { this: Table[_] =>
  /**
   * can be overriden if the plural of tablename is irregular
   **/
  val idColName: String = s"${tableName.dropRight(1)}_id"
  def id = column[Int](s"${idColName}", O.PrimaryKey, O.AutoInc)
  def getNextId = (Q[Int] + s"""select nextval('"${tableName}_${idColName}_seq"')""").first
  }

所有你的实体类的情况下需要这样的(应该也是一个特征来定义)的方法:

case class Entity (...) {
  def withId(newId: Id): Entity = this.copy(id = Some(newId)
}

新的实体,现在可以插入这样:

object Entities extends Table[Entity]("entities") with TableWithId {
  override val idColName: String = "entity_id"
  ...
  def save(entity: Entity) = this insert entity.withId(getNextId) 
}

该代码仍然是不会干的,因为你需要定义每个表的withId方法。 此外,您有您插入可能导致性能影响的实体之前请求下一个ID,但除非你一次插入数千个条目的不应该是显着的。

主要优点是,有没有必要进行第二次投影什么使得代码更少出错,尤其是对于有许多列的表。



Answer 4:

另一个窍门是做的情况下类变种的id

case class Entity(var id: Long)

要插入一个实例,创建类似下面Entity(null.asInstanceOf[Long])

我测试过,它的作品。



Answer 5:

我所面临试图从播放油滑3.0使计算机数据库样本时,我改变了分贝的Postgres同样的问题。 有什么解决的问题是改变id列(主键)型在进化文件/conf/evolutions/default/1.sql SERIAL(最初是BIGINT)。 看看https://groups.google.com/forum/?fromgroups=#%21topic/scalaquery/OEOF8HNzn2U
整个讨论。 干杯,夏瑞



文章来源: Using Auto Incrementing fields with PostgreSQL and Slick