Anorm Scala вставляет список объектов с вложенным списком

Мне нужно вставить последовательность элементов с последовательностью вложенных элементов в базу данных PostgreSQL, предпочтительно с помощью одного оператора, потому что я возвращаю Future. Я использую Scala Play с Anorm.

Мои данные выглядят примерно так, как показано ниже.

case class Question(id: Long, titel: String)
case class Answer(questionId: Long, text: String)

В БД это выглядит так:

CREATE TABLE questions (
  question_id SERIAL PRIMARY KEY NOT NULL,
  titel TEXT NOT NULL,
);

CREATE TABLE answers (
  answer_id SERIAL PRIMARY KEY NOT NULL,
  question_id INT NOT NULL,
  text TEXT NOT NULL,
  FOREIGN KEY (question_id) REFERENCES questions(question_id) ON DELETE CASCADE
);

Моя функция будет выглядеть примерно так:

def saveFormQuestions(questions: Seq[Question], answers: Seq[Answer]): Future[Long] = {
  Future {
    db.withConnection{ implicit c =>
      SQL(
        // sql
      ).executeInsert()
    }
  }
}

Каким-то образом в Anorm, SQL или в обоих я должен сделать следующее, желательно в одной транзакции:

  • foreach question in questions
    • insert question into questions
    • foreach answer in answers, where answer.questionId == old question.id
      • insert answer into answers with new question id gained from question insert

Я новичок в Scala Play, поэтому, возможно, сделал некоторые предположения, которые не должен был делать. Любые идеи, которые помогут мне начать, будут оценены.


person MyrionSC2    schedule 27.08.2017    source источник
comment
Сначала попробуйте закодировать в Scala итерацию по экземплярам ваших классов case, даже используя только println с соответствующим ожидаемым оператором.   -  person cchantep    schedule 28.08.2017


Ответы (2)


Я решил это с помощью логики внутри блока db.withConnection. Почему-то я предположил, что внутри db.withConnection должен быть один оператор SQL, но это оказалось не так. Ну вот так:

val idMap = scala.collection.mutable.Map[Long,Long]() // structure to hold map of old ids to new
db.withConnection { implicit conn =>
  // save all questions and gather map of the new ids to the old
  for (q <- questions) {
    val id: Long = SQL("INSERT INTO questions (titel) VALUES ({titel})")
      .on('titel -> q.titel)
      .executeInsert(scalar[Long].single)
    idMap(q.id) = id
  }

  // save answers with new question ids
  if (answers.nonEmpty) {
    for (a <- answers ) {
      SQL("INSERT INTO answers (question_id, text) VALUES ({qid}, {text});")
        .on('qid -> idMap(a.questionId), 'text -> a.text).execute()
    }
  }
}
person MyrionSC2    schedule 29.08.2017
comment
Вместо этого используйте интерполяцию Anorm для параметров. - person cchantep; 30.08.2017

Как видно из названия, Anorm не является ORM и не будет генерировать отчет за вас.

Вам нужно будет определить операторы, соответствующие представлению данных и отношений (например, мой учебник Аколита).

Что касается транзакций, Anorm представляет собой тонкую/умную оболочку вокруг JDBC, поэтому семантика транзакций JDBC сохраняется. BTW Play предоставляет .withTransaction в своей утилите разрешения DB.

person cchantep    schedule 27.08.2017