有没有办法可以在 Slick 中巧妙地进行更新插入操作?以下工作但太晦涩/冗长,我需要明确说明应该更新的字段:
val id = 1
val now = new Timestamp(System.currentTimeMillis)
val q = for { u <- Users if u.id === id } yield u.lastSeen
q.update(now) match {
case 0 => Users.insert((id, now, now))
case _ => Unit
}
最佳答案
更新了 Slick 2.1 中的原生 upsert/merge 支持
注意力
您必须在您的数据库 native 中使用纯 SQL 嵌入 MERGE陈述。模拟此语句的所有试验很可能会导致不正确的结果。
背景:
当您模拟 upsert/merge 语句时,Slick 将不得不使用多个语句来达到该目标(例如,先进行选择,然后是插入或更新语句)。在一个 SQL 事务中运行多条语句时,它们通常没有与单个语句相同的隔离级别。不同的隔离级别,你会在海量并发的情况下体验到奇怪的效果。所以在测试期间一切都会正常工作,但在生产中会因奇怪的效果而失败。
在同一事务中的两条语句之间运行一条语句时,数据库通常具有更强的隔离级别。而一个正在运行的语句不会受到其他并行运行的语句的影响。数据库要么锁定语句涉及的所有内容,要么检测正在运行的语句之间的干扰,并在必要时自动重新启动有问题的语句。当执行同一事务中的下一条语句时,此级别的保护不成立。
所以以下场景可能(并且将会!)发生:
user.firstOption
后面的 select 语句找不到当前用户的数据库行。 公平地说,隔离级别 "serializable" 不会发生这种情况。 .但是这种隔离级别会带来巨大的性能损失,很少在生产中使用。另外可序列化将需要您的应用程序的一些帮助:数据库管理系统通常不会真正可序列化所有事务。但它会检测违反可序列化要求的行为,并中止遇到麻烦的事务。因此,您的应用程序必须准备好重新运行被 DBMS(随机)中止的事务。
如果您依赖于发生违反约束的情况,请设计您的应用程序,使其自动重新运行有问题的事务,而不会打扰用户。这类似于隔离级别“可序列化”中的要求。
结论
在这种情况下使用纯 SQL 或为生产中的不愉快惊喜做好准备。考虑并发可能存在的问题。
2014 年 8 月 5 日更新:Slick 2.1.0 现在具有 native MERGE 支持
在 Slick 2.1.0 中,现在对 MERGE 语句提供 native 支持(请参阅 release notes:“插入或更新支持,在可能的情况下使用 native 数据库功能”)。
代码如下所示(取自 Slick test cases ):
def testInsertOrUpdatePlain {
class T(tag: Tag) extends Table[(Int, String)](tag, "t_merge") {
def id = column[Int]("id", O.PrimaryKey)
def name = column[String]("name")
def * = (id, name)
def ins = (id, name)
}
val ts = TableQuery[T]
ts.ddl.create
ts ++= Seq((1, "a"), (2, "b")) // Inserts (1,a) and (2,b)
assertEquals(1, ts.insertOrUpdate((3, "c"))) // Inserts (3,c)
assertEquals(1, ts.insertOrUpdate((1, "d"))) // Updates (1,a) to (1,d)
assertEquals(Seq((1, "d"), (2, "b"), (3, "c")), ts.sortBy(_.id).run)
}
关于scala - 在 Slick 中插入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17895476/