Skip to content

Latest commit

 

History

History
133 lines (95 loc) · 4.5 KB

OneToManyMappings.md

File metadata and controls

133 lines (95 loc) · 4.5 KB

Domain classes

Our sample domain consists of Persons who own a set of Houses. One Person can own one or more Houses.

For a start, lets review the domain classes, which are case classes so that equals and hashCode are implemented for us. We'll use case classes to simplify the examples, but it is not necessary. Providing proper equals() and hashCode() implementation is not required by mapper dao but is required by scala collections.

case class Person(var name: String, val surname: String, owns: Set[House], var age: Int)

case class House(val address: String)

Assuming that we got mapperDao configured ( SetupDaos ), our tables (Person,House) & mappings (PersonEntity,HouseEntity, we describe those later on) ready, we can use mapperdao to persist, update, select, delete and query for our entities:

import com.googlecode.mapperdao._

// configure mapperDao and queryDao as described at https://code.google.com/p/mapperdao/wiki/SetupDaos ...

...

// and now create & persist entities

val person1 = Person("Kostas", "K", Set(House("London"), House("Rhodes")), 16)
// the insert command will persist the set of houses and person1
// and return the equivalent persisted object which contains the id
// of the entity
val person1Inserted=mapperDao.insert(PersonEntity, person1)
// person1Inserted.id is now available

// lets persist one more person with his own houses
val person2 = Person("X", "Y", Set(House("Paris"), House("Athens")), 20)
// again, both the set of houses and person2 will be persisted
val inserted=mapperDao.insert(PersonEntity, person2)

println("%d : %s".format(inserted.id,inserted.name))


val loaded=mapperDao.select(PersonEntity,inserted.id)
// "loaded" is an instance of Person with IntId
println("%d : %s".format(loaded.id,loaded.name))

// and now a simple query

import Query._
val p=PersonEntity
val h=HouseEntity
val q = select from p join (p,p.owns, h) where h.address === "London"
val l=queryDao.query(q)
println(l)
// prints List(Person("Kostas", "K", Set(House("London"), House("Rhodes")), 16))

Lets now proceed with the table ddl and mappings.

Tables

The table ddl (for postgresql) follows:

create table Person (
	id serial not null,
	name varchar(100) not null,
	surname varchar(100) not null,
	age int not null,
	primary key (id)
)

create table House (
	id serial not null,
	address varchar(100) not null,
	person_id int not null,
	primary key (id),
	constraint FK_House_Person foreign key (person_id) references Person(id) on delete cascade on update cascade
)

Please Note: serial is an autoincrement int column.

Mappings

We can now create the mappings between our domain model and the database tables. Lets start with the House entity:

import com.googlecode.mapperdao._

object HouseEntity extends Entity[Int,SurrogateIntId, House] {
	val id = key("id") autogenerated (_.id)
	val address = column("address") to (_.address)

	def constructor(implicit m) = new House(address) with Stored {
		val id:Int = HouseEntity.id
	}
}

Nothing strange here, typical mappings of an entity with autogenerated PK.

Now lets have a look at the really juicy part: the actual one-to-many mapping. Here is the mapping object for Person:

object PersonEntity extends Entity[Int,SurrogateIntId, Person] {
	val id = key("id") autogenerated (_.id)
	val name = column("name") to (_.name)
	val surname = column("surname") to (_.surname)
	val owns = onetomany(HouseEntity) to (_.owns)
	val age = column("age") to (_.age)

	def constructor(implicit m) = new Person(name, surname, owns /* implicitly converted to Set, to do manually: m(owns).toSet  */, age) 
		with Stored {
		val id:Int = PersonEntity.id
	}
}

Again, the person is an Entity[Int,SurrogateIntId,Person], contains an id which is an autogenerated primary key, a name, a surname and an age. But a person has 0 or more houses:

val owns = onetomany(HouseEntity) to (_.owns)

Each house is of HouseEntity and mapped via House.person_id column (the mapping to table "House" is known via HouseEntity).

No primary key for the "many" part?

The "many" part doesn't have to have a primary key. I.e. in the examples above, the HouseEntity doesn't need a primary key since it's always attached to a PersonEntity.

For this to be achieved, declarePrimaryKey must be invoked to declare which columns act as a primary key (even if they are not).

An example of this can be found here