scala - 在 Slick 中插入

标签 scala slick

有没有办法可以在 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 语句找不到当前用户的数据库行。
  • 并行的第二个事务为该用户插入一行
  • 第一个事务为该用户插入第二行(类似于 phantom read )
  • 您要么以同一用户的两行结尾,要么第一个事务失败并违反约束,尽管其检查有效(运行时)

  • 公平地说,隔离级别 "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/

    相关文章:

    scala - 在 Spark Scala 中读取二进制文件

    Scala 密封特征 - 复制枚举 `withName` 方法

    scala - Slick - 使用动态 sortBy 编译

    scala - 在 Play Slick 中分配动态注入(inject)的数据库名称

    scala - 添加reactivemongo后出现错误 "play-iteratees_2.10 not found"

    Scala 转换为泛型类型(用于泛型数值函数)

    scala - 如何在 Scala 中根据元素索引获取列表中的一组元素?

    scala - 创建通用更新计数器方法

    playframework - 使用注入(inject)在 PlaySlick 中配置两个数据库

    scala - 无法使用Slick更新记录