如何一个记录插入到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