【问题标题】:Scala Play Future InterdependancyScala Play 未来的相互依赖
【发布时间】:2016-10-12 19:18:50
【问题描述】:

我正在为我的 scala Play 使用 play-slick!虚拟休息 API。

所以,我必须从多个表中获取记录。但是,它们是相互依赖的,即

Table_1   Table_2
id1       id2
id2

要从 Table_2 中获取记录,我必须从 Table_1 中获取记录,然后使用 id2 从 Table_2 中获取。

我的控制器:

def getEntity(id : Long) = Action.async {
  table1DAO.findById(id) map { t1 =>
    t1 map { t1Entity =>
      table2DAO.findById(t1Entity.id2) map { t2 =>
        t2 map { t2Entity =>
          Ok(Json.toJson(CombiningClass(t1Entity, t2Entity)))
        } getOrElse { Ok(Json.toJson(t1Entity)) }
      }
    } getOrElse { NoContent }
  }
}

编译后得到:

[error] DummyController.scala:17: overloaded method value async with alternatives:
[error]   [A](bodyParser: play.api.mvc.BodyParser[A])(block: play.api.mvc.Request[A] => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[A] <and>
[error]   (block: play.api.mvc.Request[play.api.mvc.AnyContent] => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[play.api.mvc.AnyContent] <and>
[error]   (block: => scala.concurrent.Future[play.api.mvc.Result])play.api.mvc.Action[play.api.mvc.AnyContent]
[error]  cannot be applied to (scala.concurrent.Future[Object])
[error]   def getEntity(id : Long) = Action.async {
[error]                                ^
[error] one error found

这是我的 DAO 方法:

def findById(id : Long): Future[Option[A]] = {
  db.run(tableQ.filter(_.id === id).result.headOption)
}

PS:我对函数式范式和 Scala 还很陌生,所以如果可以,请原谅我的无知。

【问题讨论】:

  • 尝试注释顶部maptable1DAO.findById(id).map[Result] { t1 =&gt; ... },以确保您提供Future[Result]。附言使用 for-comprehension 可以提高代码的可读性。

标签: scala playframework-2.0 dao future data-access-object


【解决方案1】:

简单地解决您的问题:

def getEntity(id : Long) = Action.async {
  findById(id) flatMap {
    case Some(t1Entity) =>
      findById(t1Entity.id2) map { t2Opt =>
        t2Opt map { t2Entity =>
          Ok(Json.toJson(t1Entity, t2Entity))
        } getOrElse { Ok(Json.toJson(t1Entity)) }
      }
    case None => Future.successful(NoContent)
  }
}

这里的问题是你不能flatMapOptionFuture在scala中一起使用。 Here 是一篇关于这个主题的精彩而简单的文章(使用自定义的 FutureO monad 实现作为解决方案)。长话短说,我会使用 cats 库(甚至 scalaz 库)和 OptionT 功能。我稍微简化了你的代码。

def getEntity(id : Long) = Action.async {
  (for {
    t1 <- daoFindById(id)
    t2 <- daoFindById(t1.id2)
  } yield (t1, t2)).map{
    result => Ok(Json.toJson(result))
  }.getOrElse(NoContent)
}

case class T(id2: Long)
def daoFindById(id : Long): OptionT[Future, T] = {
  OptionT[Future, T](db.run(tableQ.filter(_.id === id).result.headOption))
}

您现在可以通过 OptionT monad 轻松地使用 flatMap,并且不在乎您是在处理 Option 还是 Future(因为 scala 中的理解只是语法糖)。

【讨论】:

  • 这是一个很好的答案,尽管对于刚开始使用 Scala 的人来说可能有点高级。如果找不到第二个实体,这不会给你第一个实体。猫也有a tutorial about OptionT
  • 您的更新解决了我的两个 cmets。它对初学者更友好一点,并且会返回 OP 期望的结果,干得好!
  • 是的,并不是所有的业务逻辑都被保留了(我只是想证明 cat 库的用处)。我添加了在没有外部库帮助的情况下编写的简单解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-18
  • 1970-01-01
  • 1970-01-01
  • 2019-08-16
  • 2013-04-06
相关资源
最近更新 更多