Rhyztech blog

Play2.0と学ぶsqueryl (4) Statelessな関連とStatefulな関連の違い

tech

前回、前々回で squeryl での OneToMany や ManyToMany の関連について取り上げました。

今回は関連情報の取得方法に関わる話、stateless な関連と stateful な関連を取り上げます。

stateless な関連と stateful な関連では関連情報を参照する際、具体的には前々回の User クラスに posts フィールドにアクセスする際、そのときの挙動が異なります。

stateless な関連ではフィールドにアクセスした際に毎回クエリが発行され DB へのアクセスが発生します。

一方、stateful な関連では初めてフィールドにアクセスした際に DB からの取得し、それ以降、その状態を維持します。

stateful な関連は同じフィールドにアクセスする度に DB アクセスが発生しないが、逆に明示的にリフレッシュしないと DB の状態が反映されません。

具体的なプログラムで見ていきましょう。

まず、stateless な関連をもつ User クラスと Post クラスを定義していきます。

package models

import org.squeryl.KeyedEntity
import org.squeryl._
import org.squeryl.dsl._
import org.squeryl.PrimitiveTypeMode._

case class User(email: String, password: String, fullname: String, isAdmin: Boolean) extends KeyedEntity[Long] {
  val id: Long = 0

  lazy val posts: OneToMany[Post] = YabeDB.usersPosts.left(this)
}

case class Post(title: String, postedAt: Date, content: String, author_id: Long = 0) extends KeyedEntity[Long] {
  valid: Long = 0
}

object YabeDB extends Schema {

  val users = table[User]("user_tb")

  val posts = table[Post]

  val usersPosts =
    oneToManyRelation(users, posts).via((u, p) => u.id === p.author_id)
}

関連情報を取得する検証を行います。

package models

import play.api.test._
import play.api.test.Helpers._
import org.specs2.mutable.Specification
import org.squeryl._
import org.squeryl.PrimitiveTypeMode._
import java.util.Date

class PostSpec extends Specification {

  "Post" should {

    "creat new Post stateless" in {
      running(FakeApplication()) {

        inTransaction {

          valuser: User = YabeDB.users.insert(User("bob@gmail.com", "secret", "Bob", false))

          YabeDB.posts.size must beEqualTo(0)

          valpost: Post = Post("My first post", new Date(), "Hellow world")

          user.posts.associate(post)

          // associateで明示的に関連づけた場合は関連情報を持つpostsフィールドが更新される
          YabeDB.posts.size must beEqualTo(1)
          user.posts.size must beEqualTo(1)

          // Userインスタンスのidを渡してインサートする
          YabeDB.posts.insert(Post("Hex", new Date(), "Hellow world", user.id))

          // statelessな関連はフィールドアクセスごとにクエリを発行するのでDBの状態が反映される
          YabeDB.posts.size must beEqualTo(2)
          user.posts.size must beEqualTo(2)


        }
      }
    }
  }
}

stateless な関連では posts フィールドにアクセスした際に必ずクエリが発行されるので、DB の状態が必ず反映される。

そのため、2 つめの Post クラスをインサートした後も、posts フィールドに DB の状態が反映される。

続いて Stateful な関連を定義します。

stateful な関連では left ではなく、leftStateful を使って定義します。

package models

import org.squeryl.KeyedEntity
import org.squeryl._
import org.squeryl.dsl._
import org.squeryl.PrimitiveTypeMode._

case class User(email: String, password: String, fullname: String, isAdmin: Boolean) extends KeyedEntity[Long] {
  val id: Long = 0

  // Statefulな関連を定義するために left を leftStateful にする
  lazy val posts: StatefulOneToMany[Post] = YabeDB.usersPosts.leftStateful(this)

}

検証用のテストを書いてみます。

package models

import play.api.test._
import play.api.test.Helpers._
import org.specs2.mutable.Specification
import org.squeryl._
import org.squeryl.PrimitiveTypeMode._
import java.util.Date

class PostSpec extends Specification {

  "Post" should {

    "creat new Post stateful" in {
      running(FakeApplication()) {

        inTransaction {


          val user: User = YabeDB.users.insert(User("bob@gmail.com", "secret", "Bob", false))
          YabeDB.posts.size must beEqualTo(0)

          val post: Post = Post("My first post", new Date(), "Hellow world")

          user.posts.associate(post)

          // associateで明示的に関連づけた場合は関連情報を持つpostsフィールドが更新される
          YabeDB.posts.size must beEqualTo(1)
          user.posts.size must beEqualTo(1)

          // Userインスタンスのidを渡してインサートする
          YabeDB.posts.insert(Post("Hex", new Date(), "Hellow world", user.id))

          // statefulな関連は初めてフィールドにアクセスした時の状態を保存するのでDBの状態と一致しない場合がある
          YabeDB.posts.size must beEqualTo(2)
          user.posts.size must beEqualTo(1)

          // 明示的にrefreshをした場合、DBの状態を再取得してフィールドに反映される。
          user.posts.refresh

          YabeDB.posts.size must beEqualTo(2)
          user.posts.size must beEqualTo(2)

        }
      }
    }

  }
}

stateful な関連の方は初めて posts フィールドにアクセスしたときの DB の状態を保持していて、その後に DB が変更されても posts フィールドの状態が保持されていることが分かります。

その後、refresh メソッドを呼び出して明示的に DB の状態を再取得した場合、posts フィールドに DB の状態が反映されます。

以上。

Copyright 2024, rhyztech. All Rights Reserved.