Scala Playframework并非所有数据库查询都得到执行 [英] Scala Playframework not all DB queries get executed
问题描述
我正在通过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)
}
For mo info how to work with DBIO actions check this official docs
这篇关于Scala Playframework并非所有数据库查询都得到执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!