【问题标题】:The "right" way to use write Slick 3.0 Scala queries in Play Framework在 Play Framework 中使用编写 Slick 3.0 Scala 查询的“正确”方式
【发布时间】:2015-05-20 03:34:02
【问题描述】:

我使用的是 Slick 3.0,而且(当然)几乎所有的示例都涵盖了 Slick 2.x。事情发生了变化,坦率地说,看起来更加复杂,而不是更少。

这是一个例子:我想通过 id 获取一个对象(一个 GPPerson)。这就是我现在所拥有的,它似乎非常冗长......比 Slick 2.x 更详细:

def get(id: GPID): Option[GPPerson] = Await.result(
    db.run(
        people.filter(_.id === id).result), Duration.Inf
    ).headOption

在 Slick 2.x 中,由于隐式等原因,事情变得更容易了。不过以上似乎是我想出的最简洁的表达方式了。

它也没有真正解决我需要添加的异常处理。

【问题讨论】:

  • 为了清楚起见,您正在使用Await.result 阻止您的线程。您可能知道,但在代码中看到它只是吓人。
  • 我知道——Typesafe 中的所有 Slick 3.0 示例都是这样做的......

标签: scala playframework slick


【解决方案1】:

几个月前我开始在一个新项目中使用 Slick 3.0,我也有同样的问题。这是我的理解:

Slick 3.0 专为非阻塞异步(反应式)应用程序而设计。显然,它现在意味着 Akka + Play / Spray。在这个世界中,您主要与 Futures 交互,这就是 Slick 的 db.run 返回 Future 的原因。使用 Await.result 没有意义 - 如果您需要阻塞调用,最好返回到 2.x。

但是,如果您使用反应式堆栈,您将立即获得好处。例如,Spray 是一个完全非阻塞的库,可以很好地使用 onComplete 指令与 Futures 一起工作。您可以调用一个方法,该方法在 Spray 路由中使用来自 Slick 的结果返回 Future,然后将该结果与 onComplete 一起使用。在这种情况下,整个响应-回复管道是非阻塞的。

您还提到了异常处理,所以这正是您的做法 - 使用 Futures。

所以根据我的经验,我会按照以下方式编写您的方法:

def get(id: GPID): Future[Option[GPPerson]] = db.run(
  people.filter(_.id === id).result.map(_.headOption)
)

然后使用 Future。

【讨论】:

  • Future 是否直接在 Action 中返回?例如,现在我有一些使用 Await 来完成查询的操作。例如,我将在 Action 中执行 val r = db.run(glimpleAction); Await.result(r, Duration.Inf)... 我会直接取出 Await 并且 Play 足够聪明来管理其后果吗? (但不可能这么简单,因为我可以通过Await.result(r, Duration.Inf).toSeq 来获取序列。如何在 Web 服务 Action 中返回 Future?)
  • 你绝对不需要 Await.result。为我在 Slick 中编写查询的标准方法:db.run(some_query.result),这将返回类型 Future[Seq[SomeQueryType]]。关于 Play - 您需要使用 Action.async 才能在您的操作中使用 Futures,更多详细信息:playframework.com/documentation/2.4.x/ScalaAsync
【解决方案2】:

你可以这样做。

def syncResult[R](action:slick.dbio.DBIOAction[R, slick.dbio.NoStream, scala.Nothing]):R = {
    import scala.concurrent.duration.Duration

    val db = Database.forConfig("db")
    try {
      Await.result(db.run(action), Duration.Inf)
    } finally db.close
  }

def get(id: GPID): Option[GPPerson] = syncResult { people.filter(_.id === id).result.headOption }

【讨论】:

  • 好吧,这确实让它更干净一点......但正如上面所指出的,这是一个阻塞调用的事实呢?我看过的所有 Slick 3.0 示例似乎都使用了这种模式,但是......它是阻塞的。这似乎是非常错误的。此外,Slick 3.0 似乎走错了方向,使事情更难调用而不是更容易......但也许 API 中有一些我还没有发现的变化。善良地说,文档是“模糊且不完整的......”
猜你喜欢
  • 2023-04-02
  • 1970-01-01
  • 2021-03-08
  • 2013-10-29
  • 1970-01-01
  • 2020-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多