光滑3.1.1:在单个事务中插入实体及其关系

时间:2016-09-08 05:54:54

标签: scala h2 slick

我有一个分类有两个关系的实体:公司和位置。为了坚持分类,我需要知道它的关系的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列返回一个INSERT]

看起来H2驱动程序不会返回id,除非它们属于自动增量变量。

那么,如何编写此事务以便a)在单个事务中插入b)以最小的db往返和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")
    )

2 个答案:

答案 0 :(得分:1)

事实证明,从db中检索id是很容易的部分,因为执行此操作的代码已经具有所需类型(DBIOAction),困难的部分是在插入后获取id但在这种情况下我已经知道id,因为它是我设置它的代码。没有必要使用return()并依赖于RDBMS。

解决方案:

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 += company
      DBIO.successful(company.id.get)
    }
    case Some(comp) => DBIO.successful(comp.id.get)
  }
  loc <- locations.filter(_.name === location.name).result.headOption flatMap {
    case None => {
      locations += location
      DBIO.successful(location.id.get)
    }
    case Some(loc) => DBIO.successful(loc.id.get)
  }
  cl <- {
    classifieds += classified.copy(companyId = comp, locationId = loc)
    DBIO.successful(classified.id.get)
  }
} yield cl
db.run(interaction.transactionally)
}

答案 1 :(得分:0)

For Slick returning工作Id必须auto generated primary key

Id不仅应该是主键,还应该是自动增量ID。

注意

许多数据库系统只允许返回单个列,该列必须是表的自动递增主键。如果要求其他列,则在运行时抛出SlickException(除非数据库实际支持它)。

所以编写一个函数,在插入数据库之后返回Id

def getIdAfterInsert(entity: Entity): DBIO[EntityId] = {
  (entities += entity).flatMap { _ =>
    entities.filter(_.name === entity.name).result.flatMap {
      case Some(entity) => DBIO.successful(entity.id)
      case None => DBIO.fail(new Exception("something terrible happened"))
    }
  }.transactionally
}