Scala 玩未来的相互依赖
Scala Play Future Interdependancy
我正在为我的 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 很陌生,所以如果可以的话,请原谅我的无知。
简单解决您的问题:
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)
}
}
这里的问题是你不能在 scala 中一起 flatMap
Option
和 Future
。 Here is a fantastic and simple article concerning this topic (with the custom FutureO
monad implementation as a solution). Long story short, I would use the 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))
}
您现在可以轻松地 flatMap
处理这个 OptionT
monad,并且不关心您是在处理 Option
还是 Future
(对于 scala 的理解是只是一个语法糖)。
我正在为我的 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 很陌生,所以如果可以的话,请原谅我的无知。
简单解决您的问题:
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)
}
}
这里的问题是你不能在 scala 中一起 flatMap
Option
和 Future
。 Here is a fantastic and simple article concerning this topic (with the custom FutureO
monad implementation as a solution). Long story short, I would use the 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))
}
您现在可以轻松地 flatMap
处理这个 OptionT
monad,并且不关心您是在处理 Option
还是 Future
(对于 scala 的理解是只是一个语法糖)。