>我有一个人表和动物表,在动物表中有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 => ...
}