Scala Playframework не все запросы к БД выполняются - PullRequest
0 голосов
/ 28 ноября 2018

Я отправляю через HTTP Post Request 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 или что-либо блокирующее в моем действии.

Заранее спасибо

1 Ответ

0 голосов
/ 29 ноября 2018

Вы начинаете вычисления, вызывая itemsToDBController.saveItems(items), а затем немедленно возвращаете результат с помощью Ok("Success").Поэтому после запроса может быть сгенерировано исключение.

Чтобы устранить эту проблему, необходимо преобразовать результат itemsToDBController.saveItems из List[Future[T]] в Future[List[T]] с помощью Future.sequence.Затем вызовите метод 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, проверьте этот официальный документы

...