Scala Playframework并非所有数据库查询都得到执行

时间:2018-11-28 14:52:21

标签: scala playframework playframework-2.0 slick

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

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

   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(...):

   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或任何受阻的内容。

预先感谢

1 个答案:

答案 0 :(得分:2)

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

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

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)重写为:

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
  }
}

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

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动作的更多信息,请检查此官方docs