如何从Play控制器异步查询OrientDB?

时间:2014-03-03 23:11:08

标签: scala asynchronous playframework playframework-2.2 orientdb

我正在Scala中编写一个Play(2.2)控制器,它应该返回针对OrientDB的查询结果。现在,我已成功编写了所述控制器的同步版本,但我想重新编写它以异步工作。

我的问题是;给出下面的代码(只是为了演示目的而放在一起),如何重新编写我的控制器以与OrientDB异步交互(连接和查询)?

import play.api.mvc.{Action, Controller}
import play.api.libs.json._
import com.orientechnologies.orient.`object`.db.OObjectDatabasePool
import java.util
import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery
import scala.collection.JavaConverters._

object Packages extends Controller {
  def packages() = Action { implicit request  =>
    val db = OObjectDatabasePool.global().acquire("http://localhost:2480", "reader", "reader")
    try {
      db.getEntityManager().registerEntityClass(classOf[models.Package])
      val packages = db.query[util.List[models.Package]](new OSQLSynchQuery[models.Package]("select from Package")).asScala.toSeq
      Ok(Json.obj(
        "packages" -> Json.toJson(packages)
      ))
    }
    finally {
      db.close()
    }
  }
}

修改

具体来说,我希望使用OrientDB的异步API。我知道API支持异步查询,但我不确定你是否也可以异步连接。

尝试解决方案

根据Jean的回答,我尝试了以下异步实现,但由于编译错误value execute is not a member of Nothing possible cause: maybe a semicolon is missing before 'value execute'?而失败:

def getPackages(): Future[Seq[models.Package]] = {
    val db = openDb
    try {
      val p = promise[Seq[models.Package]]
      val f = p.future
      db.command(
        new OSQLAsynchQuery[ODocument]("select from Package",
          new OCommandResultListener() {
            var acc = List[ODocument]()

            @Override
            def result(iRecord: Any): Boolean = {
              val doc = iRecord.asInstanceOf[ODocument]
              acc = doc :: acc
              true
            }

            @Override
            def end() {
              // This is just a dummy
              p.success(Seq[models.Package]())
            }
          // Fails
          })).execute()
      f
    }
    finally {
      db.close()
    }
}

2 个答案:

答案 0 :(得分:2)

一种方法可能是启动一个承诺,返回表示该承诺结果的未来,在本地累积结果并完成de promise(从而解决未来)当orient db通知您该命令已完成时。

def executeAsync(osql: String, params: Map[String, String] = Map()): Future[List[ODocument]] = {
    import scala.concurrent._
    val p = promise[List[ODocument]]
    val f =p.future
    val req: OCommandRequest = database.command(
      new OSQLAsynchQuery[ODocument]("select * from animal where name = 'Gipsy'",
        new OCommandResultListener() {
          var acc = List[ODocument]()
          @Override
          def result(iRecord:Any):Boolean= {
            val doc = iRecord.asInstanceOf[ODocument]
            acc=doc::acc
            true
          }

          @Override
          def end() {
            p.success(acc)
          }
        }))
    req.execute()
    f
  }

但要小心,要启用图形导航和字段延迟加载,使用orientdb对象来保存对它们从中加载的数据库实例的内部引用(或依赖于threadlocal数据库连接的实例),以便从数据库中延迟加载元素。异步操作这些对象可能会导致加载错误。我没有检查1.6的变化,但这似乎深深地嵌入了设计中。

答案 1 :(得分:-1)

就像在Future中包装阻塞调用一样简单。

import play.api.libs.concurrent.Execution.Implicits.defaultContext
import scala.concurrent.Future

object Packages extends Controller {
  def packages = Action.async { implicit request  =>
    val db = OObjectDatabasePool.global().acquire("http://localhost:2480", "reader", "reader")
    db.getEntityManager().registerEntityClass(classOf[models.Package])

    val futureResult: Future[Result] = Future(
      db.query[util.List[models.Package]](new OSQLSynchQuery[models.Package]("select from Package")).asScala.toSeq
    ).map(
      queryResult => Ok(Json.obj("packages" -> Json.toJson(packages)))
    ).recover { 
      // Handle each of the exception cases legitimately
      case e: UnsupportedOperationException => UnsupportedMediaType(e.getMessage)
      case e: MappingException              => BadRequest(e.getMessage)
      case e: MyServiceException            => ServiceUnavailable(e.toString)
      case e: Throwable                     => InternalServerError(e.toString + "\n" + e.getStackTraceString) 
    }

    futureResult.onComplete { case _ => 
      db.close()
    }

    futureResult
  }
}

请注意,我没有编译代码。有很多空间来改进代码。