2016-05-04 10 views
1

私はSlick 3の機能モデルで精神ブロックに直面していると確信していますが、Slick 3のオプションの従属dbステップをトランザクション的にどのようにシーケンスするかは分かりません。特に、オプションの)外部キーと私はそれが挿入された従属レコードのID(もしあれば、null)に設定されることを望みます。つまり、おおよそ次のようになります。slick 3でオプションのdbステップを処理する方法は?

if (x is non null) 
    start transaction 
    id = insert x 
    insert y(x = id) 
    commit 
else 
    start transaction 
    insert y(x = null) 
    commit 

もちろん、私は選択肢の周りに大きい場合はありません。 Option []を持たない依存関係は(比較的)単純ですが、オプションが私を投げています。

正確なサンプルコード(サンのインポート)が続きます。この例では、yがNoneである場合とそうでない場合の両方で、x(a)とy(b)の両方を同じトランザクションに保存する方法が問題です。すべての関連するCにはオプションではないB参照があるので、Y自体を保存するだけでも簡単ですが、Aの任意の参照に対処するのは不明です(私にとって)。

object test { 
     implicit val db = Database.forURL("jdbc:h2:mem:DataTableTypesTest;DB_CLOSE_DELAY=-1", driver = "org.h2.Driver") 


     /* Data model */ 
     case class A(id: Long, b: Option[Long], s: String) 

     class As(tag: Tag) extends Table[A](tag, "As") { 
     def id = column[Long]("ID", O.PrimaryKey, O.AutoInc) 
     def b = column[Option[Long]]("B") 
     def s = column[String]("S") 

     def * = (id, b, s) <> (A.tupled, A.unapply) 
     } 
     val as = TableQuery[As] 

     case class B(id: Long, s: String) 

     class Bs(tag: Tag) extends Table[B](tag, "Bs") { 
     def id = column[Long]("ID", O.PrimaryKey, O.AutoInc) 
     def s = column[String]("S") 

     def * = (id, s) <> (B.tupled, B.unapply) 
     } 
     val bs = TableQuery[Bs] 

     case class C(id: Long, b: Long, s: String) 

     class Cs(tag: Tag) extends Table[C](tag, "Cs") { 
     def id = column[Long]("ID", O.PrimaryKey, O.AutoInc) 
     def b = column[Long]("B") 
     def s = column[String]("S") 

     def * = (id, b, s) <> (C.tupled, C.unapply) 
     } 
     val cs = TableQuery[Cs] 


     /* Object model */ 
     case class X(id: Long, s: String, y: Option[Y]) 

     case class Y(id: Long, s: String, z: Set[Z]) 

     case class Z(id: Long, s: String) 


     /* Mappers */ 
     def xToA(x: X, bId: Option[Long]): A = { A(x.id, bId, x.s) } 
     def yToB(y: Y): B = { B(y.id, y.s) } 
     def zToC(z: Z, bId: Long): C = { C(z.id, bId, z.s) } 

     /* Given */ 
     val example1 = X(0, "X1", Some(Y(0, "Y1", Set(Z(0, "Z11"), Z(0, "Z12"))))) 
     val example2 = X(0, "X2", Some(Y(0, "Y2", Set()))) 
     val example3 = X(0, "X3", None) 

     Await.result(db.run((as.schema ++ bs.schema ++ cs.schema).create), 10.seconds) 

     val examples = Seq(example1, example2, example3) 
     for (example <- examples) { 
     val saveY = (for { y <- example.y } 
      yield (for { 
       id <- (bs returning bs.map(_.id)) += yToB(y) 
       _ <- cs ++= y.z.map(zToC(_, id)) 
      } yield id) transactionally) 
     if (saveY.isDefined) Await.result(db.run(saveY.get), 10.seconds) 
     } 

     println(Await.result(
     db.run(
      (for { a <- as } yield a).result 
     ), 
     10.seconds 
    )) 

     println(Await.result(
     db.run(
      (for { b <- bs } yield b).result 
     ), 
     10.seconds 
    )) 

     println(Await.result(
     db.run(
      (for { c <- cs } yield c).result 
     ), 
     10.seconds 
    )) 
    } 

答えて

1

これはかなり簡単です。 DBIOのモナディーネスを使用してください:

// Input B value; this is your `x` in the question. 
val x: Option[B] = _ 
// Assume `y` is fully-initialized with a `None` `b` value. 
val y: A = _ 

// DBIO wrapping the newly-inserted ID, if `x` is set. 
val maybeInsertX: DBIO[Option[Int]] = x match { 
    case Some(xToInsert) => 
    // Insert and return the new ID. 
    val newId: DBIO[Int] = bs.returning(bs.map(_.id)) += xToInsert 
    // Map to the expected Option. 
    newId.map(Some(_)) 
    case None => 
    // No x means no ID. 
    DBIO.successful(None) 
} 

// Now perform your insert, copying in the newly-generated ID. 
val insertA: DBIO[Int] = maybeInsertX.flatMap(bIdOption => 
    as += y.copy(b = bIdOption) 
) 

// Run transactionally. 
db.run(insertA.transactionally) 
+0

ありがとうございます。今精神的に消化する:) –

関連する問題