无法在 Quill 中使用事务来插入一对多关系对象



>我有一个人表和动物表,在动物表中有FK到personId,因为它们之间存在一对多关系。

我只想创建一个人并使用事务创建它的动物,因为我希望这个过程是原子的(如果我无法创建它的动物,则在数据库中没有使用人(

这是我如何接受人员创建请求的模型:

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

DB是这样认识一个人的:

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)
  }
}

我对动物也有同样的事情:

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

这就是数据库如何知道动物(personId = 所有者(:

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)
  }
}

所以现在这就是我尝试这样做的方式(但失败了(:

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)
  }

发生的事情是我只是没有得到回应...它继续处理而不返回任何内容或抛出错误

问题是,目前,Quill 异步不支持事务内的并发操作。

所以不得不按顺序进行动物插入:

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
}

另外,更好的是使用批量插入:)

感谢您@fwbrasil和@mentegy的帮助!

将隐式ExecutionContext参数添加到insertAnimal方法:

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

没有它,您就不会从事务块传递 ec,动物插入将尝试使用池中的其他连接。

你熟悉 Scala Futures 吗?

若要从事务中获取结果,应将onSuccess处理程序添加到从ctx.transaction调用返回的Future

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

相关内容

  • 没有找到相关文章

最新更新