Failing to use transactions in Quill to INSERT one-to-many relational objects

ぐ巨炮叔叔 提交于 2019-12-12 21:15:02

问题


I have a person table and animal table and in the animal table there is FK to personId since there is one-to-many relation between them.

I just want to create a person and create its animals using a transaction cause I want the process to be atomic (there is no use of person in the db if I could not create its animals)

This is the model of how I accept a person creation request:

case class PersonCreateRequest(name: String, age: Int, animals: Seq[AnimalCreateRequest])

This is how the DB knows a Person:

case class Person(personId: Long, name, age: Int)

// this is just a companion object to help me take a PersonCreateRequest and make it Person
object Person {
  def apply(person: PersonCreateRequest): Person = {
    Person(0L,
           person.name,
           person.age)
  }
}

same thing I have with Animal:

case class AnimalCreateRequest(animalType: String, age: Int)

This is how the db knows a Animal(personId = owner):

case class Animal(animalId: Long, animalType: String, age: Int, personId: Long)

// here I need to get personId as parameter cause I will only have it after a person was created:
object Animal {
  def apply(animal: AnimalCreateRequest, personId: Long): Animal = {
    Animal(0L,
           animal.animalType,
           animal.age,
           personId)
  }
}

So now this is how I tried to do it(and failed):

lazy val ctx = new MysqlAsyncContext(CamelCase, "ctx")
  import ctx._


  def insertPerson(personToCreate: PersonCreateRequest): Future[Long] = {

    // getting the person object that the db knows
    val dbPerson = Person.apply(personToCreate)

    // INSERT Person Query
    val insertPersonQuery = quote {
      query[Person].insert(lift(dbPerson)).returning(_.personId)
    }

    ctx.transaction { implicit ec =>
      for {
        personId   <- ctx.run(insertPersonQuery)
        contactIds <- {
          Future.sequence(
          personToCreate.animals.map(animal => {
            val animalToInsert = Animal.apply(animal, personId)
            insertAnimal(animalToInsert)
          })
          )
        }
      } yield personId
    }
  }

  def insertAnimal(animal: Animal): Future[Long] = {
    val q = quote {
      query[Animal].insert(lift(animal)).returning(_.animalId)
    }
    ctx.run(q)
  }

What happens is that I just dont get a response...its keep processing without returning anything or throwing an error


回答1:


Problem was, currently, Quill async does not support concurrent operations inside transactions.

So had to do the animal insertion sequentially:

ctx.transaction { implicit ec =>
  for {
    personId <- ctx.run(insertPersonQuery)
    animals = personCreate.animals.map(Animal.apply(personId, _))
    _ <- animals.foldLeft(Future.successful(0l)) {
      case (fut, animal) =>
        fut.flatMap(_ => insertAnimal(animal))
    }
  } yield personId
}

also, even better is to use batch insertion :)

Thanks for @fwbrasil and @mentegy for the assistance!




回答2:


Add implicit ExecutionContext parameter to insertAnimal method:

def insertAnimal(animal: Animal)(implicit ec: ExecutionContext): Future[Long] =

Without it, you're not passing ec from the transaction block and animal insertions will try and use other connections from the pool.




回答3:


Are you familiar with Scala Futures?

To get a result from a transaction you should add onSuccess handler to a Future returned from the ctx.transaction call:

ctx.transaction { ...
}.onSuccess {
  case personId => ...
}


来源:https://stackoverflow.com/questions/48488962/failing-to-use-transactions-in-quill-to-insert-one-to-many-relational-objects

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!