Play framework 2.0 のためのブログです。
Comments


Slick2.1にて、ManyToMany(多対多)を実現する方法例
今回の実装では、User *---* Project というリレーションを実現します。

val user = Users.findById(1).get
val projects = user.projects // SQL走る
val projects2 = user.projects // ここでは、キャッシュされた結果が返るためSQLは走らない

上記のように、アクセス可能で、初期アクセス時のみにSQLが発行されるような使用としています。

■Dao.scala
package models

import scala.slick.lifted.TableQuery

private[models] trait DAO {
  val Users = TableQuery[Users]
  val Projects = TableQuery[Projects]
  val ProjectUserMap = TableQuery[ProjectUserMap]
}

■User.scala
package models

import java.sql.Timestamp
import play.api.db.slick.Config.driver.simple._

// define dto
case class User(id: Long, name: String){
  // ##############################
  //  user.projects という形で、アクセス可能
  //  また、キャッシュされるため、2度めのアクセスの際にはSQLは走らない
  // ##############################
  val projectCache = collection.mutable.Map[Long, List[Product]]()
  def projects(implicit session: Session) = {
    projectCache.getOrElseUpdate(this.id, ProjectUserMap.projects(this.id))
  }
}

// define table schema
class Users(tag: Tag) extends Table[User](tag, "users") with DAO{
  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name", O.NotNull)
  def * = (id, name) <> (User.tupled, User.unapply)
  def idx = index("idx_token", token ,unique = true)
}

object Users extends DAO {
  def findById(id: Long)(implicit s: Session): Option[User] = {
    Users.filter { _.id === id }.firstOption
  }

  def insert(user: User)(implicit s: Session) {
    Users += user
  }
}

■Project.scala
package models

import java.sql.Timestamp
import play.api.db.slick.Config.driver.simple._

// define dto
case class Project(id: Long, name: String, description: String){
  // ##############################
  //  project.users という形で、アクセス可能
  //  また、キャッシュされるため、2度めのアクセスの際にはSQLは走らない
  // ##############################
  val userCache = collection.mutable.Map[Long, List[Product]]()
  def users(implicit session: Session) = {
    userCache.getOrElseUpdate(this.id, ProjectUserMap.users(this.id))
  }
}

// define table schema
class Projects(tag: Tag) extends Table[Project](tag, "projects") with DAO{
  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name", O.NotNull)
  def description = column[String]("description", O.NotNull)
  def * = (id, name, description) <> (Project.tupled, Project.unapply)
}

// define companion Object
object Projects extends DAO {
  def findById(id: Long)(implicit s: Session): Option[Project] = {
    Projects.filter { _.id === id }.firstOption
  }
}

■ProjectUser.scala
package models

import java.sql.Timestamp
import models.Projects._
import play.api.db.slick.Config.driver.simple._

// define dto
case class ProjectUser(user_id: Long, project_id: Long)

// define table schema
class ProjectUserMap(tag: Tag) extends Table[ProjectUser](tag, "projects_users") with DAO{
  def user_id = column[Long]("user_id")
  def project_id = column[Long]("project_id")
  def * = (user_id, project_id) <> (ProjectUser.tupled, ProjectUser.unapply)
  def userFK = foreignKey("user2projectFK", user_id, Users)(u => u.id)
  def projectFK = foreignKey("project2userFK", project_id, Projects)(p => p.id)
}


// define companion Object
object ProjectUserMap extends DAO {
  def projects(user_id: Long)(implicit s: Session) = {
    ProjectUserMap.filter { _.user_id === user_id }.flatMap(_.projectFK).list
  }
  def users(project_id: Long)(implicit s: Session) = {
    ProjectUserMap.filter { _.project_id === project_id }.flatMap(_.userFK).list
  }
}

↓以下、使い方
■Application.scala
package controllers

import models._
import play.api.mvc._
import play.api.db.slick._
import play.api.Play.current


object Application extends Controller{

  def index = DBAction { implicit rc =>
    val user = Users.findById(1).get
    val projects = user.projects // SQL走る
    val projects2 = user.projects // ここでは、キャッシュされた結果が返るためSQLは走らない
    Ok("test")
  }

}

取得する列の指定など、拡張は今回は盛り込んでません!
ご指摘、などあればコメントお願い致します!!

Categories: ,

Leave a Reply