用分支链接 DBIOAction,一个分支什么都不做

Chaining DBIOAction with branching, one branch does nothing

来自

我的代码还有一个问题:

def add(languageCode: String,
  typeId: Long,
  properties: Seq[Property]): Unit = {
    val dbAction = (
        for{
            nodeId <- (nodes.all returning nodes.all.map(_.id)) += Node(typeId)
            language <- (languages.all filter (_.code === languageCode)).result.head
            _ <- DBIO.seq(properties.map
            {
            property =>
                val id = property.id
                val name = property.key
                val value = property.value
                if(id == 0) {
                  val currentPropId: FixedSqlAction[Option[Long], h2Profile.api.NoStream, Effect.Read] = this.properties.all.map(_.id).max.result
                  val propertyId = (this.properties.all returning this.properties.all.map(_.id)) += Property(language.id.get, currentPropId + 1, name)
                  nodeProperties.all += NodeProperty(nodeId, 2, value)
                } else {
                  nodeProperties.all += NodeProperty(nodeId, id, value)
                }
            }: _*)
        } yield()).transactionally

        db.run(dbAction)
}

1) 基本思想是,如果每个 属性 的 id 为 0,则有两个插入要调用。一个在某些 table 中实际创建新的 属性,另一个也将那个 属性 的值添加到另一个 table.

那是在:

if(id == 0) {
    val currentPropId: Rep[Option[Long]] = this.properties.all.map(_.id).max
    // FIXME get rid of hardcoded languageId
    val DUMMY_LANGUAGE_ID = 1
    val propertyId = (this.properties.all returning this.properties.all.map(_.id)) += Property(DUMMY_LANGUAGE_ID, currentPropId + 1, name)
    nodeProperties.all += NodeProperty(nodeId, 2, value)
}

部分。

现在,如果不是的话,只需要将属性的值插入到正确的table,就是这样else 部分用于.

我面临的问题是,对于 id == 0 什么都没有插入的情况。

我也这样试过:

if(id == 0) {
    val nextPropId: Rep[Option[Long]] = this.properties.all.map(_.id).max
    // FIXME get rid of hardcoded languageId
    val propertyId = (this.properties.all returning this.properties.all.map(_.id)) += Property(1, 10, name)
    val nodeProperty = nodeProperties.all += NodeProperty(nodeId, 2, value)
    propertyId andThen nodeProperty
}

但无济于事。

else 部分工作得很好。我在这里错过了什么?

编辑:

因为有人问我。

val propertyId = (this.properties.all returning this.properties.all.map(_.id)) += Property(language.id.get, 10, name)
val nodeProperty = nodeProperties.all += NodeProperty(nodeId, 2, value)

log.info(s"Statements: ${propertyId.statements}")
log.info(s"${nodeProperty.statements}")

给我:

Statements: Vector(insert into "properties" ("language_id","property_id","name") values (?,?,?)) Vector(insert into "node_property" ("node_id","property_id","value") values (?,?,?))

看起来还不错。

我也很想记录 dbAction 本身的内容,但由于它是 DBIOAction 我没有找到一种有意义的方式来做到这一点。

如果我将日志记录设置为跟踪,我会得到:

22:18:16:016 - [debug] s.c.QueryCompilerBenchmark - ------------------- Phase: Time ---------
22:18:16:016 - [debug] s.c.QueryCompilerBenchmark -       assignUniqueSymbols:    0.456428 ms
22:18:16:016 - [debug] s.c.QueryCompilerBenchmark -                inferTypes:    0.089135 ms
22:18:16:016 - [debug] s.c.QueryCompilerBenchmark -            insertCompiler:    0.292443 ms
22:18:16:016 - [debug] s.c.QueryCompilerBenchmark -                   codeGen:    0.534494 ms
22:18:16:016 - [debug] s.c.QueryCompilerBenchmark -                     TOTAL:    1.372500 ms
22:18:16:016 - [debug] s.b.B.action - #5: Rollback

不知道为什么最后一行说 Rollback 因为之前的所有内容(相当多)对我来说似乎没问题。

edit2:

看来我终于有了:

val dbAction = for {
      nodeId <- (nodes.all returning nodes.all.map(_.id)) += Node(typeId)
      languageId <- (languages.all filter (_.code === languageCode)).map(_.id).result.head
      _ <- DBIO.seq(properties.values.map
      {
        property =>
          val id = property.id
          val name = property.key
          val value = property.value

          if(id == 0) {
            for {
              currentPropId <- this.properties.all.map(_.id).max.result
              propertyId <- (this.properties.all returning this.properties.all.map(_.id)) += Property(languageId, currentPropId.get + 1, name)
              _ <- this.nodeProperties.all += NodeProperty(nodeId, propertyId, value)
            } yield ()
          } else {
            this.nodeProperties.all += NodeProperty(nodeId, id, value)
          }
      }: _*)
    } yield ()

看来最后的答案是重组一下:

 val dbAction = for {
      nodeId <- (nodes.all returning nodes.all.map(_.id)) += Node(typeId)
      languageId <- (languages.all filter (_.code === languageCode)).map(_.id).result.head
      _ <- DBIO.seq(properties.values.map
      {
        property =>
          val id = property.id
          val name = property.key
          val value = property.value

          if(id == 0) {
            for {
              currentPropId <- this.properties.all.map(_.id).max.result
              propertyId <- (this.properties.all returning this.properties.all.map(_.id)) += Property(languageId, currentPropId.get + 1, name)
              _ <- this.nodeProperties.all += NodeProperty(nodeId, propertyId, value)
            } yield ()
          } else {
            this.nodeProperties.all += NodeProperty(nodeId, id, value)
          }
      }: _*)
    } yield ()

我很想看到其他解决方案,但这是我在又一次长时间的反复试验后找到的解决方案。我仍然不确定为什么我无法使用 andThen 方法。

而且我想知道如果在内部 for 理解中发生某些事情,transactionally 会发生什么。我想根据我对文档的解释它不会起作用 - http://slick.lightbend.com/doc/3.2.1/dbio.html#transactions-and-pinned-sessions

Nested transactionally actions simply execute inside the existing transaction without additional savepoints.