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

Scala Playframework not all DB queries get executed

我正在通过 HTTP Post 请求 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 操作 returns 200 - OK。但并非所有相关项目都存储到我的数据库中。似乎在 200 - OK 之后一切都停止了,甚至没有抛出错误。 我不想在我的操作中使用 Await.result 或任何阻塞。

提前致谢

您正在通过调用 itemsToDBController.saveItems(items) 开始计算,然后立即 return 结果为 Ok("Success")。因此,如果完成,请求后可能会抛出异常。

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

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