我正在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()
}
}
答案 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
}
}
请注意,我没有编译代码。有很多空间来改进代码。