-
Notifications
You must be signed in to change notification settings - Fork 27
Associations
Jiawei Li edited this page Apr 26, 2014
·
14 revisions
where
assign
<<
associate
:=
toOption
You can load association while loading object using includes(association)
. (solution for the n+1 query problem)
For example:
Order.includes(_.client).limit(10).foreach { order =>
println(order.client.name)
}
This produces:
Select
orders.price,
orders.id
From
orders
limit 10 offset 0;
Select
clients.name,
clients.age,
clients.id
From
clients inner join orders on (clients.id = orders.client_id)
Where
(orders.id in (1,2,3,4,5,6,7,8,9,10))
When you want to clear the eager loading cache, using association#reload
:
Client.head.orders.reload
Orders.head.client.reload
package sample01
import com.github.aselab.activerecord._
import dsl._
object Tables extends ActiveRecordTables {
val suppliers = table[Supplier]
val accounts = table[Account]
}
case class Supplier(name: String) extends ActiveRecord {
// OneToOne relation
lazy val account = hasOne[Account]
}
case class Account(number: String) extends ActiveRecord {
val supplierId: Option[Long] = None
lazy val supplier = belongsTo[Supplier]
}
object Supplier extends ActiveRecordCompanion[Supplier]
object Account extends ActiveRecordCompanion[Account]
object OneToOneSample extends App {
Tables.initialize(Map("schema" -> "sample01.Tables"))
val supplier = Supplier("supplier").create
val account1 = Account("account1").create
val account2 = Account("account2").create
supplier.account := account1
println(supplier.account.number) // => account1
println(account1.supplier.toOption) // => Some(Supplier("supplier1"))
supplier.account := account2
println(supplier.account.number) // => account2
println(account2.supplier.toOption) // => Some(Supplier("supplier2"))
println(account1.supplier.toOption) // => None
Tables.cleanup
}
package sample02
import com.github.aselab.activerecord._
import dsl._
object Tables extends ActiveRecordTables {
val users = table[User]
val groups = table[Group]
}
case class User(name: String, isAdmin: Boolean = false) extends ActiveRecord {
val groupId: Option[Long] = None
// ManyToOne association
lazy val group = belongsTo[Group]
}
case class Group(name: String) extends ActiveRecord {
// OneToMany association
lazy val users = hasMany[User]
// only admin users association
lazy val adminUsers = hasMany[User](conditions = Map("isAdmin" -> true))
}
object User extends ActiveRecordCompanion[User]
object Group extends ActiveRecordCompanion[Group]
object OneToManySample extends App {
Tables.initialize(Map("schema" -> "sample02.Tables"))
val user1 = User("user1").create
val user2 = User("user2").create
val group1 = Group("group1").create
val group2 = Group("group2").create
group1.users << user1
group1.adminUsers << user2
println(group1.users.toList) // => List(User("user1", false), User("user2", true))
println(user1.group.orNull) // => Group("group1")
println(user2.isAdmin) // => true
Tables.cleanup
}
package sample03
import com.github.aselab.activerecord._
import dsl._
object Tables extends ActiveRecordTables {
val users = table[User]
val groups = table[Group]
}
case class User(name: String) extends ActiveRecord {
// ManyToMany(HABTM) association
lazy val groups = hasAndBelongsToMany[Group]
}
case class Group(name: String) extends ActiveRecord {
// ManyToMany(HABTM) association
lazy val users = hasAndBelongsToMany[User]
}
object User extends ActiveRecordCompanion[User]
object Group extends ActiveRecordCompanion[Group]
object HasAndBelongsToManySample extends App {
Tables.initialize(Map("schema" -> "sample03.Tables"))
val user1 = User("user1").create
val user2 = User("user2").create
val group1 = Group("group1").create
val group2 = Group("group2").create
user1.groups := List(group1, group2)
println(user1.groups.toList) // => List(Group("group1"), Group("group2"))
println(group1.users.toList) // => List(User("user1"))
Tables.cleanup
}
package sample04
import com.github.aselab.activerecord._
import dsl._
object Tables extends ActiveRecordTables {
val users = table[User]
val projects = table[Project]
val roles = table[Role]
val memberships = table[Membership]
}
case class User(name: String) extends ActiveRecord {
lazy val memberships = hasMany[Membership]
// ManyToMany(hasManyThrough) association
lazy val projects = hasManyThrough[Project, Membership](memberships)
}
case class Project(name: String) extends ActiveRecord {
lazy val memberships = hasMany[Membership]
// ManyToMany(hasManyThrough) association
lazy val users = hasManyThrough[User, Membership](memberships)
}
case class Role(name: String) extends ActiveRecord {
lazy val memberships = hasMany[Membership]
}
// Intermediate table's model
case class Membership(userId: Long, projectId: Long, roleId: Option[Long] = None) extends ActiveRecord {
lazy val user = belongsTo[User]
lazy val project = belongsTo[Project]
lazy val role = belongsTo[Role]
}
object User extends ActiveRecordCompanion[User]
object Project extends ActiveRecordCompanion[Project]
object Role extends ActiveRecordCompanion[Role]
object Membership extends ActiveRecordCompanion[Membership]
object HasManyThroughSample extends App {
Tables.initialize(Map("schema" -> "sample04.Tables"))
val user1 = User("user1").create
val user2 = User("user2").create
val role1 = Role("role1").create
val role2 = Role("role2").create
val project1 = Project("project1").create
val project2 = Project("project2").create
val membership1 = user1.projects.assign(project1)
membership1.role := Role("aaa")
membership1.save
user1.projects << project2
project1.users << user2
println(user1.projects.toList) //=> List(Project(project1), Project(project2))
println(project2.users.exists(_.name === "user1")) //=> true
Tables.cleanup
}
The hasMany
association supports these options (now available):
- conditions
- foreignKey
The conditions
option lets you specify the conditions that the associated object must meet via Map[String, Any]
.
case class User(name: String, isAdmin: Boolean = false) extends ActiveRecord {
val groupId: Option[Long] = None
lazy val group = belongsTo[Group]
}
case class Group(name: String) extends ActiveRecord {
lazy val adminUsers = hasMany[User](conditions = Map("isAdmin" -> true))
}
If you use :conditions
option, then record creation via this association will be automatically assign.
group.adminUsers << user // => user.isAdmin == true
By convention, Scala ActiveRecord assumes that the column used to hold the foreign key on the other model is the name of this model with the suffix Id
added. The foreignKey
option lets you set the name of the foreign key directly:
case class User(name: String) extends ActiveRecord {
lazy val comments = hasMany[Comment](foreignKey = "authorId")
}
case class Comment(content: String) extends ActiveRecord {
val authorId: Long = 0
lazy val author = belongsTo[User](foreignKey = "authorId")
}