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
我正在通过 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