2016-06-12 58 views
0

我为我的Scala使用play-slick Play!虚拟休息API。因此,我不得不从多个表中提取记录。但是,它们是相互依存的,即Scala Play Future Interdependancy

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:我是很新的功能范式和斯卡拉,所以如果可以的话,请原谅我的无知。

+0

试着注释顶部'map':'table1DAO.findById(id).map [结果] {t1 => ...}'以确保给出'Future [Result]'。附:您的代码的可读性将受益于使用理解。 – cchantep

回答

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在一起阶。 Here是一篇关于此主题的精彩而简单的文章(以自定义FutureO monad实现为解决方案)。长话短说,我会用cats库(甚至斯库拉库)和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单子,如果你正在处理OptionFuture不在乎(在斯卡拉的理解仅仅是一个语法糖)。

+0

这是一个很好的答案,尽管对于Scala开始的人来说可能有点高级。如果找不到第二个实体,这不会给你第一个实体。猫还有[关于'OptionT'的教程](http://typelevel.org/cats/tut/optiont.html)。 –

+1

您的更新解决了我的意见。这对初学者更友好一点,并且会返回OP期望的结果,这么好! –

+1

是的,并不是所有的商业逻辑都被保留下来(我只想证明猫库的有用性)。我添加了简单的解决方案,没有外部库的帮助。 – liosedhel

相关问题