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") } }
取得する列の指定など、拡張は今回は盛り込んでません!
ご指摘、などあればコメントお願い致します!!