Scala Playframework并非所有数据库查询都得到执行 [英] Scala Playframework not all DB queries get executed

查看:116
本文介绍了Scala Playframework并非所有数据库查询都得到执行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在通过HTTP发送请求Json到我的Playframework后端.

I'm sending via HTTP Post Request a Json to my Playframework backend.

在后端,我将Json验证为模型.之后,我想将模型中的条目保存到数据库中.

In my backend I validate the Json to a Model. After that, I want to save the entries in my Model to my DB.

   def parseJSON: Action[AnyContent] = Action.async {
       request =>
         Future {
           request.body.asJson.map(_.validate[MyModel] match {
             case JsSuccess(items, _) =>
               itemsToDBController.saveItems(items)
               Ok("Success")
             case JsError(err) =>
               println(err)
               BadRequest("Json Parse Error")
           }).getOrElse(BadRequest("Error"))
         }
     }

一个项目由多个对象组成.要将所有对象保存到数据库,我需要获取一些值.因此,我正在使用for(..)yield(...):

One Item consists out of several objects. To save all objects to my DB, I need to get some values. Therefore I'm using a for(..) yield(...):

   def saveItems(items: MyModel) = {
       items.SomeObject.map(obj => {
         if (obj.value1.isDefined &&
           obj.value2.isDefined ) {
           val result = for (
             value1Exists <- value1DTO.checkExists(obj.value1.name);
             value1Entry <- getOrCreateValue1(value1Exists, obj);
             value2Exists <- value2DTO.checkExists(obj.value2.name);
             value2Entry <- getOrCreateValue2(value1Exists, obj)
            ) yield(value1Entry, value2Entry)

           result.map({
             case (value1Entry, value2Entry) => {
               insertAllValue3(value1Entry, value2Entry)
               Future.successful()
             }
             case _ => Future.failed(new Exception("Not all entries defined"))
           })
         }
         else {
             Future.successful("Not all objects defined - skipping")
         }
       })
     }

我的问题是,在所有result.map({...})启动之后,我的parseJSON Action returns 200 - OK.但是,并非所有相关项目都存储到我的数据库中.在200 - OK之后,一切似乎都停止了,甚至没有引发错误. 我不想在Action中使用Await.result或任何受阻的内容.

My problem is, after all result.map({...}) have started, my parseJSON Action returns 200 - OK. But not all relevant items get stored to my DB. It seems like after the 200 - OK everything is stopped and it doesn't even throw an error. I don't want to use Await.result or anything blocking in my Action.

预先感谢

推荐答案

您正在通过调用itemsToDBController.saveItems(items)开始计算,然后立即使用Ok("Success")返回结果.因此,请求完成后可能会引发异常.

You are starting computations by calling itemsToDBController.saveItems(items) and then immediately return result with Ok("Success"). So exception may be thrown after request if completed.

要解决此问题,您需要在Future.sequence的帮助下将itemsToDBController.saveItems的结果从List[Future[T]]转换为Future[List[T]].然后在返回的Future上调用map方法.在此Future上调用recover以查找引发哪个错误:

To fix this issue you need to transform result of itemsToDBController.saveItems from List[Future[T]] to Future[List[T]] with help of Future.sequence. Then call map method on returned future. Call recover on this Future to find which error is thrown:

def parseJSON: Action[AnyContent] = Action.async { request =>
  request.body.asJson
    .map(_.validate[MyModel] match {
      case JsSuccess(items, _) =>
        Future
          .sequence(itemsToDBController.saveItems(items))
          .map(_ => Ok("Success"))
          .recover {
            case e: Exception => BadRequest(e.getMessage())
          }

      case JsError(err) =>
        println(err)
        Future.successful(BadRequest("Json Parse Error"))
    })
    .getOrElse(Future.successful(BadRequest("Error")))
}

更新

要在一个事务中运行所有插入,应结合使用DBIOAction而不是Future.例如,您将checkExists(name)重写为:

For running all inserts in one transaction you should combine DBIOAction instead of Future. For example you rewrite checkExists(name) as:

def checkExists(name: String): DBIO[Boolean] = {
  Objects.filter(obj => obj.name === name).exists
}

getOrCreateValue(exists, obj)为:

def getOrCreateValue(exists: boolean, obj: Object): DBIO[Object] = {
  if (exists) {
    Objects.filter(o => o.name === name).result.head
  } else {
    (Objects returning Objects.map(_.id) into ((o, id) => o.copy(id = Some(id)))) += obj
  }
}

现在,您可以通过以下方式在单个事务中运行它:

Now you can run it in single transaction in the following way:

def saveItems(items: MyModel) = {
  val insertActions = items.SomeObject.map(obj => {
    if (obj.value1.isDefined && obj.value2.isDefined) {
      val result = for {
        value1Exists <- value1DTO.checkExists(obj.value1.name);
        value1Entry <- getOrCreateValue1(value1Exists, obj);
        value2Exists <- value2DTO.checkExists(obj.value2.name);
        value2Entry <- getOrCreateValue2(value1Exists, obj)
      } yield (value1Entry, value2Entry)

      result.flatMap({
        case (value1Entry, value2Entry) => {
          insertAllValue3(value1Entry, value2Entry) // This also returns instance of `DBIOAction`
        }
        case _ =>
          DBIO.failed(new Exception("Not all entries defined"))
      })
    } else {
      DBIO.successful("Not all objects defined - skipping")
    }
  })
  db.run(DBIO.sequence(inserActions).transactionally)
}

有关如何使用DBIO操作的更多信息,请检查此官方

For mo info how to work with DBIO actions check this official docs

这篇关于Scala Playframework并非所有数据库查询都得到执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆