首页 > 解决方案 > 从 Squeryl 迁移时使用 Slick 3.0 事务

问题描述

我正在将一些 Scala 代码从 Squeryl 迁移到 Slick。一切都很顺利,直到我碰到交易的话题。Squeryl 使处理事务变得非常简单:您只需将代码(无论是否与 DB 相关)包装在一个transaction块中即可。

从我可以在网上收集的示例来看,似乎人们希望重新构建整个项目,使其能够很好地使用它的一元方法和理解,这是我真的想避免的事情。

考虑任意一段代码,例如:

def f(): Unit = {
    UserRepository.getUser()
    ... some imperative code
    ServiceRepository.getServer()
    ...
    ServerRepository.updateServer(...)
    ...
    UserRepository.insertNewUser(...)
}

有没有一种方法可以轻松地将其包装在某种事务块中,而无需更改方法的内部逻辑?

标签: scalaslick

解决方案


有没有一种方法可以轻松地将其包装在某种事务块中,而无需更改方法的内部逻辑?

简短的回答是否定的,但这取决于那些调用UserRepository和其他方法返回的内容:

  • 如果它们是Future[T](或者如果您已阻止从未来获取值),则您已经运行了查询,所以这是一个否,因为您在 Slick 层之外。

  • 如果它们是动作 ( DBIO) 甚至是查询,那么它可能不会改变内部逻辑:您必须将动作组合在一起,但可以通过混合任意代码来实现。例如,flatMap(或理解)将允许这样做。

作为混合逻辑和组合动作的示例,您可以编写以下代码:

val action = for {
  user <- UserRepository.getUser // a DBIO[User]
  result <- if user.notPermitted() {
      DBIO.failed(new IOException("Not allowed")
  } else {
      DBIO.successful(user)
  }  
} yield result

DBIO介绍在(可能使用successfuland )方面起作用的方法和值failed可能会使这种转换对您来说不那么痛苦。有一个答案可能会为混合逻辑和操作提供更多想法:https ://stackoverflow.com/a/31490020/154248

您必须执行最终的组合操作,.transcationally以确保它们的所有操作都在事务中运行。这在:https ://scala-slick.org/doc/3.3.2/dbio.html#transactions

想到了另外两种可能性:

  • 可以想象,您可以为 Slick 实现自己的后端。但这似乎需要做很多工作,而且你会一直与 Slick 其余部分的想法作斗争。

  • 虽然我通常不建议这样做,但旧版本的 Slick(Slick 2)似乎更接近 SQueryl:https ://scala-slick.org/doc/2.1.0/connection.html#session-handling ...和这可能会为您提供通往 Slick 的垫脚石,然后作为单独的工作线进入 Slick 3。


推荐阅读