【发布时间】:2017-01-15 22:40:23
【问题描述】:
我有一个 Classified 实体,它有 2 个关系:一个公司和一个位置。为了持久化分类,我需要知道其关系的 id,这可能需要首先持久化一个实体(即它可能已经存在于数据库中,否则应该插入它)。
id 是应用程序分配的 UUID(即它不是由 db 自动递增的),因此应用程序分配一个 id,如果实体已经存在,该 id 最终将成为实体 id 或被事务中的实际 id 替换.
执行此操作的代码如下:
def create(classified: Classified, company: Company, location: Location): Future[String] = {
val interaction = for {
comp <- companies.filter(_.name === company.name).result.headOption flatMap {
case None => companies returning companies.map(_.id) += company
case Some(comp) => DBIO.successful(comp.id.get)
}
loc <- locations.filter(_.name === location.name).result.headOption flatMap {
case None => locations returning locations.map(_.id) += location
case Some(loc) => DBIO.successful(loc.id.get)
}
cl <- classifieds returning classifieds.map(_.id) += classified.copy(companyId = comp, locationId = loc)
} yield cl
db.run(interaction.transactionally)
}
上述方法在针对 Postgres(这是生产数据库)运行时完美运行,但对于 H2(这是测试和开发数据库)失败并出现错误:[SlickException:此 DBMS 只允许从单个 AutoInc 列返回插入]
看起来 H2 驱动程序不会返回 id,除非它们是自动增量类型的。
那么,如何编写此事务,以便 a) 插入发生在单个事务中 b) 以最少的数据库往返和 c) 以数据库中立的方式?
编辑:
上面的方法是从这样的控制器中使用的:
classifiedDao.create(
Classified(Some(UUID.randomUUID().toString), c.title, Jsoup.clean(c.body, Whitelist.basic()), c.refNo, "", ""),
Company(Some(UUID.randomUUID().toString), c.companyName, c.companyEmail, None, None),
Location(Some(UUID.randomUUID().toString), c.location, None)
).map(_ =>
Redirect(routes.Classifieds.form()).flashing("success" -> "Classified submitted")
)
【问题讨论】:
-
澄清一下:
company和location是否已经在插入时分配了 UUID(case None => companies returning companies.map(_.id) += company行)? -
是的。控制器将 id 分配给所有三个实体,然后将对象传递给 DAO 以进行持久化,如果实体已存储,则丢弃 id 并使用从 db 检索到的 id,而始终使用的 Classified 除外。
-
@Roman 那条评论简直是天才。在 None 情况下,我已经知道 id,我需要做的就是使用其他 DBAction 对结果进行排序,然后我就完成了。谢谢,那太棒了。 PS:我在下面发布答案以供后代使用。