我正在通过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或任何受阻的内容。
预先感谢
答案 0 :(得分:2)
您通过调用itemsToDBController.saveItems(items)
开始计算,然后立即使用Ok("Success")
返回结果。因此,请求完成后可能会引发异常。
要解决此问题,您需要借助itemsToDBController.saveItems
将List[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