Module 03 - Hibernate Association Mapping-upd
Module 03 - Hibernate Association Mapping-upd
© 2014 Time2Master 1
Association Mapping
In Java associations are made with object
references
public class Person { public class Car {
private int id; private int id;
Person has a private String firstname; private short year;
cars collection private String lastname; private String model; Car also has an
of references private List<Car> cars private String maker; owner reference
= new ArrayList(); private Person owner; back to its owner
... ...
+firstname
+lastname
owns
Car
+year
+model
1 0..*
+birthday +maker
+cars
Can only be traversed
from person to car
public class Person { public class Car {
private int id; private int id;
Person has a private String firstname; private short year;
collection of private String lastname; private String model; Car does not have
references to private List<Car> cars private String maker; a reference back
Car objects = new ArrayList(); to person
...
...
Person Car
Bi-directional association
owns
+firstname +year
+lastname +model
1 0..*
+birthday +maker
+cars +owner
Association Can be
traversed in both directions
public class Person { public class Car {
Person has a private int id; private int id;
private String firstname; private short year;
collection of Car also has a
private String lastname; private String model;
references to private List<Car> cars private String maker; reference back
Car objects = new ArrayList(); private Person owner; to person
... © 2014 Time2Master ...
3
Seven Types of Associations
There are seven types of associations
4 Uni-directional
3 Bi-directional
Multiplicity Uni-Directional Bi-directional
One To One Uni-Directional Bi-Directional
Many To One Uni-Directional
Bi-Directional
One To Many Uni-Directional
Many To Many Uni-Directional Bi-Directional
© 2014 Time2Master 4
Association Mapping
© 2014 Time2Master 5
Many to One Uni-Directional
Objects A Car has one Customer
and a Customer can be
the owner of more than
Car Customer one Car
+year owned by +firstname
+model +lastname
+maker 0..* 1
+owner
© 2014 Time2Master 6
Many to One Uni-Directional
@Entity @Entity
public class Car { public class Customer {
@Id Optional @Id
@GeneratedValue @JoinColumn @GeneratedValue
private int id; to specify the private int id;
private short year; private String firstname;
FK column private String lastname;
@ManyToOne private String model;
private String maker;
name
@ManyToOne ...
@JoinColumn(name="customer_id")
private Customer customer;
...
CAR table
Car Customer
+year owned by +firstname
+model +lastname
+maker 0..* 1
+owner
CUSTOMER table
© 2014 Time2Master 7
Uni-directional Many to One XML
public class Car {
<hibernate-mapping package="manyToOne_uni"> private int id;
<class name="Car" > private short year;
<id name="id"> private String model;
<generator class="native"/> private String maker;
</id> private Customer customer;
<property name="year" />
<property name="model" /> ...
<property name="maker" />
<many-to-one name="customer" class="Customer“ <many-to-one> maps a FK
column="customer_id" />
column in the Car table
</class>
</hibernate-mapping>
As before the column
attribute is optional
© 2014 Time2Master 8
Optional Associations
Optional associations are associations that
may not exist
A Car can exist without a Customer
Car Customer
+year owned by +firstname
0..1 indicates that the
+model +lastname customer association
+maker 0..* 0..1
+owner is optional
CAR table
© 2014 Time2Master 9
Avoid Nullable FK Columns
Nullable columns are generally frowned upon
since they break normalization
To avoid nullable foreign key columns on
optional associations you can use a join table
for an optional many to one associations :
© 2014 Time2Master 10
Optional Many to One (join table)
Normally mapped
Customer class
@Entity @Entity
public class Car { public class Customer {
@Id @Id
@GeneratedValue @JoinTable and @GeneratedValue
private int id; the join table private int id;
private short year; name are private String firstname;
@ManyToOne private String model; required private String lastname;
private String maker;
@ManyToOne ...
@JoinTable(name="car_customer")
private Customer customer;
...
Car Customer
+year owned by +firstname
+model +lastname
+maker 0..* 0..1
+owner
© 2014 Time2Master 11
Optional Many to One XML
<hibernate-mapping package="manyToOne_uni"> public class Car {
<class name="Car" > private int id;
<id name="id"> private short year;
<generator class="native"/> private String model;
</id> private String maker;
<join> is used to private Customer customer;
<property name="year" />
<property name="model" /> specify the join table
<property name="maker" /> ...
<join table="car_customer" optional="true">
<key column="car_id" />
<many-to-one name="customer" class="Customer" <many-to-one> now maps a
column="customer_id" />
</join>
FK column in the join table
</class>
</hibernate-mapping>
© 2014 Time2Master 12
Association Mapping
© 2014 Time2Master 13
One to Many Uni-directional
Objects Person
owns
Car
+firstname +year
+lastname +model
1 0..*
+cars +maker
Default
Database (2 options)
PERSON table PERSON table
FK column on
CAR table the many side PERSON_CAR table
Join
table
CAR table
© 2014 Time2Master 14
One to Many Uni-Directional FK
@Entity
public class Person {
@Id
@GeneratedValue
private int id;
@OneToMany private String firstname;
private String lastname; Collection of car references
@OneToMany
@JoinColumn(name="person_id")
private List<Car> cars = new ArrayList();
...
Normal mapping
of Car class
Person Car
owns @Entity
+firstname +year
+lastname +model public class Car {
1 0..*
+cars +maker
@Id
@GeneratedValue
private int id;
private short year;
Using a FK on private String model;
the many side private String maker;
...
© 2014 Time2Master 15
Uni–directional One to Many FK XML
public class Person {
<hibernate-mapping package="oneToMany_uniFK"> private int id;
<class name="Person" > private String firstname;
<id name="id"> private String lastname;
<generator class="native"/> private List<Car> cars =
</id> A <key> tag to new ArrayList();
<property name="firstname" /> specify a foreign key
<property name="lastname" /> join column in Car ...
<bag name="cars">
<key column="person_id" />
<one-to-many class="Car"/>
</bag>
</class>
</hibernate-mapping>
<one-to-many> tag to
map the FK relation
...
Normal mapping
Person Car of Car class
owns @Entity
+firstname +year
+lastname +model public class Car {
1 0..*
+cars +maker
@Id
@GeneratedValue
private int id;
private short year;
private String model;
private String maker;
...
© 2014 Time2Master 17
Uni-direction One to Many XML
public class Person {
<hibernate-mapping package="oneToMany_uni"> private int id;
<class name="Person" > private String firstname;
<id name="id"> <bag> maps the private String lastname;
<generator class="native"/> collection and can specify private List<Car> cars =
</id> new ArrayList();
the join table name
<property name="firstname" />
<property name="lastname" /> ...
<bag name="cars" table="person_car">
<key column="person_id" />
<many-to-many column="car_id" unique="true" class="Car"/>
</bag>
</class> <many-to-many> used for join table
</hibernate-mapping> mappings ‘unique’ constrains it to
<one-to-many> functionality
... ...
This OneToMany association is stored This ManyToOne association is stored
in the foreign key column with name in the foreign key column with name
‘person_id’ in the CAR table ‘owner_id’ in the CAR table
PERSON table
Hibernate sees this bi-directional
association as 2 independent
associations
CAR table
Both FK column contain
the same information
© 2014 Time2Master 19
mappedBy
@Entity @Entity
public class Person { mappedby indicates public class Car {
@Id @Id
@GeneratedValue
that the FK is on the @GeneratedValue
private int id; other side private int id;
private String firstname; private short year; Optional
private String lastname; private String model; @JoinColumn
@OneToMany(mappedBy="owner") private String maker; to specify FK
private List<Car> cars = @ManyToOne
new ArrayList(); @JoinColumn(name="owner_id")
private Person owner;
...
...
The bi-directional
association is stored in
one FK colunm
© 2014 Time2Master 20
Many to One / One to Many XML
public class Car {
<hibernate-mapping package="oneToMany_uni"> private int id;
<class name="Car" > private short year;
<id name="id"> private String model;
<generator class="native"/> private String maker;
</id> private Person owner;
<many-to-one> creates a FK
<property name="year" />
<property name="model" /> column to the Person table ...
<property name="maker" />
<many-to-one name="owner" column="owner_id" class="Person" />
</class>
</hibernate-mapping>
© 2014 Time2Master 21
Association Mapping
© 2014 Time2Master 22
OneToOne Uni-Directional
Objects Address
OneToOne means that
Customer
+street only one Customer can
location
+firstname +suiteOrApt
+lastname +city live at a certain address
+address 1 1
+state
+zip
Database
CUSTOMER table ADDRESS table
© 2014 Time2Master 23
OneToOne and ManyToOne
Only one Customer can live at
OneToOne a certain address
© 2014 Time2Master 25
Unique Constriant XML
public class Customer {
private int id;
<hibernate-mapping package="oneToOne_uni"> private String firstname;
<class name="Customer" > private String lastname;
<id name="id"> private Address address;
<generator class="native"/>
</id> ...
<property name="firstname" />
<property name="lastname" />
<many-to-one name="address" class="Address" unique="true"/> many-to-one with a
</class> foreign key that is
</hibernate-mapping> Unique=true enforces unique
one to one behavior
© 2014 Time2Master 26
One to One Bi-directional
@Entity @Entity Other side
public class Customer { public class Address { also uses
@Id @Id @OneToOne
@GeneratedValue @GeneratedValue and specifies
private int id;
Optional
private int id;
private String firstname; @JoinColumn to
that the FK is
private String street;
private String lastname; specify FK name private String suiteOrApt; mappedBy
@OneToOne private String city; address
@JoinColumn(name="address_id") private String state;
private Address address; private String zip;
@OneToOne(mappedBy="address")
... private Customer customer;
...
mappedBy thus
specifies that
Address
Customer
this side is not
+street
+firstname
location
+suiteOrApt
the owning side
+lastname +city
+address 1 1
+state
+zip
+customer
© 2014 Time2Master 27
One to One bi-directional in XML
public class Customer {
<hibernate-mapping package="oneToOne_bi"> private int id;
<class name="Customer" > private String firstname;
<id name="id"> private String lastname;
<generator class="native"/> private Address address;
</id>
<property name="firstname" /> ...
<property name="lastname" />
<many-to-one name="address" class="Address" unique="true"/>
Unique=true constrains the
</class>
</hibernate-mapping> <many-to-one> creates a FK many-to-one into one-to-one
column to the Address table behavior
© 2014 Time2Master 29
One to One Shared PK XML
public class Customer {
private int id;
<hibernate-mapping package="oneToOne_uni_PKJoin"> private String firstname;
<class name="Customer" > private String lastname;
<id name="id"> private Address address;
<generator class="native"/>
</id> ...
<property name="firstname" />
<property name="lastname" />
<one-to-one name="address" class="Address" /> One-to-one indicates
</class> that this reference is
</hibernate-mapping> mapped without FK
© 2014 Time2Master 30
One to One Shared PK
Bi-directional
@Entity
@Entity
public class Customer {
public class Address { Custom generator
@Id
AUTO generated @Id
@GeneratedValue
@GeneratedValue(generator="myGenerator")
private int id;
@org.hibernate.annotations.GenericGenerator(
private String firstname;
name="myGenerator",
private String lastname; Hibernate ‘foreign ‘ strategy
strategy="foreign",
@OneToOne
extension parameters=@Parameter(name="property",
@PrimaryKeyJoinColumn
value="customer")
private Address address;
)
Specify PK Join private int id;
Select value from
...
private String street;
private String suiteOrApt; customer PK
private String city;
Address private String state;
Customer
location
+street private String zip;
+firstname +suiteOrApt Also Specify PK
+lastname +city
@OneToOne
1 1
+address +state @PrimaryKeyJoinColumn Join on this side
+zip
+customer
private Customer customer;
...
© 2014 Time2Master 31
Shared PK XML
Bi-directional
public class Customer {
<hibernate-mapping package="oneToOne_bi_PKJoin">
private int id;
<class name="Customer" >
private String firstname;
<id name="id">
private String lastname;
<generator class="native"/>
private Address address;
</id>
<property name="firstname" />
...
<property name="lastname" />
<one-to-one name="address" class="Address" />
</class> <one-to-one> specifies no
</hibernate-mapping> additional FK
© 2014 Time2Master 33
Many to Many Uni-directional
@Entity
public class Customer {
@Id
@GeneratedValue
private int id;
@ManyToMany private String firstname; Optional @JoinTable
private String lastname;
private String phoneNr;
@ManyToMany
@JoinTable(name = "Customer_SalesPerson",
joinColumns = { @JoinColumn(name = "Customer_id") },
inverseJoinColumns = { @JoinColumn(name = "SalesPerson_id") }
)
private List<SalesPerson> salesPeople = new ArrayList();
@Entity
Customer SalesPerson public class SalesPerson {
called by
+firstname +repId @Id
+lastname 0..* 1..* +alias
+phoneNr +phoneNr @GeneratedValue
+salesPeople private int id;
private String alias;
private String phoneNr;
...
Normal mapping of
SalesPerson class
© 2014 Time2Master 34
Uni-directional Many to Many XML
public class Customer {
private int id;
<hibernate-mapping package="manyToMany_uni"> private String firstname;
<class name="Customer" > private String lastname;
<id name="id"> private List<SalesPerson> salesPeople
<generator class="native"/> Table attribute = new ArrayList();
</id>
specifies the ...
<property name="firstname" />
<property name="lastname" /> join table name
<property name="phoneNr" />
<bag name="salesPeople" table="customer_salesperson">
<key column="customer_id" not-null="true" />
<many-to-many column="salesperson_id" class="SalesPerson" />
</bag>
</class>
</hibernate-mapping> <many-to-many> tag
to map the join table
based relation
public class SalesPerson {
<hibernate-mapping package="manyToMany_uni"> private int id;
<class name="SalesPerson" > private String alias;
<id name="id"> private String phoneNr;
<generator class="native"/>
</id> ...
<property name="alias" />
<property name="phoneNr" />
</class> Regular mapping of
</hibernate-mapping> the SalesPerson class
© 2014 Time2Master 35
Many to Many Bi-directional
@Entity
public class Customer {
@Id
@GeneratedValue
private int id;
private String firstname;
private String lastname; @ManyToMany
private String phoneNr;
@ManyToMany @JoinTable is optional
@JoinTable(name = "Customer_SalesPerson",
joinColumns = { @JoinColumn(name = "Customer_id") },
inverseJoinColumns = { @JoinColumn(name = "SalesPerson_id") }
)
private List<SalesPerson> salesPeople = new ArrayList();
...
Customer SalesPerson
@Entity
called by public class SalesPerson {
+firstname +repId mappedBy
+lastname 0..* 1..* +alias @Id
+phoneNr +phoneNr @GeneratedValue specifies that
+salesPeople +customers
private int id; the other side is
private String alias; the owning side
private String phoneNr;
@ManyToMany(mappedBy="salesPeople")
private List<Customer> customers =
new ArrayList();
...
© 2014 Time2Master 36
Bi-Directional Many to Many XML
public class Customer {
<hibernate-mapping package="manyToMany_bi"> private int id;
<class name="Customer" > private String firstname;
<id name="id"> private String lastname;
<generator class="native"/> private String phoneNr;
</id> private List<SalesPerson> salesPeople
<bag> maps collection = new ArrayList();
<property name="firstname" />
<property name="lastname" /> and specifies table name
<property name="phoneNr" /> ...
<bag name="salesPeople" table="customer_salesperson"> Think of <many-to-many> as
<key column="customer_id" /> the join table mapping tag
<many-to-many column="salesperson_id" class="SalesPerson" />
</bag>
</class>
public class SalesPerson {
</hibernate-mapping>
private int id;
private String alias;
<hibernate-mapping package="manyToMany_bi">
private String phoneNr;
<class name="SalesPerson" >
private List<Customer> customers =
<id name="id">
new ArrayList();
<generator class="native"/>
</id>
...
<property name="alias" />
<property name="phoneNr" />
<bag name="customers" table="customer_salesperson" inverse="true">
Inverse specifies that the
<key column="salesperson_id" /> other side is the owning side
<many-to-many column="customer_id" class="Customer" />
</bag>
</class> Opposite mapping is
</hibernate-mapping> similar, but adds inverse
© 2014 Time2Master 37
Association Mapping
ASSOCIATION CASCADES
© 2014 Time2Master 38
Association Cascades
@Entity @Entity
public class Person { public class Car {
@Id @Id
@GeneratedValue @GeneratedValue
private int id; private int id;
private String firstname; private short year;
private String lastname; private String model;
@OneToMany(mappedBy="owner") private String maker;
private List<Car> cars = @ManyToOne
new ArrayList(); @JoinColumn(name="owner_id")
private Person owner;
...
...
© 2014 Time2Master 39
Specifying Cascades
Each association tag has a cascade attribute
@Entity
public class Person {
@Id
@GeneratedValue Association will cascade
private int id; on Persist operations
private String firstname;
private String lastname;
@OneToMany(mappedBy="owner", cascade=CascadeType.PERSIST)
private List<Car> cars = new ArrayList();
When a person is persisted
... its cars will also be persisted
...
© 2014 Time2Master 40
Cascade Types
Hibernate JPA Description
all ALL Cascade on all operations
persist PERSIST Cascade on persist operations
merge MERGE Cascade on merge operations
remove REMOVE Cascade on remove operations
refresh REFRESH Cascade on refresh operations
save-update - Cascade on save or update operations or on flush
delete - Cascade on delete or remove operations
delete-orphan - Cascade into a collection (normal delete doesn’t)
lock - Cascade on lock operations
replicate - Cascade on replicate operations
evict - Cascade on evict operations
© 2014 Time2Master 41
Hibernate Cascade Annotation
Hibernate annotation extensions can be used
to specify Hibernate specific cascade types
@Entity
public class Person {
@Id
@GeneratedValue
private int id; Cascade on persist and Merge
private String firstname;
private String lastname;
@OneToMany(mappedBy="owner", cascade={CascadeType.PERSIST, CascadeType.MERGE})
@org.hibernate.annotations.Cascade(
org.hibernate.annotations.CascadeType.SAVE_UPDATE
)
private List<Car> cars = new ArrayList();
Cascade on save or update
...
© 2014 Time2Master 42
XML Cascades
<hibernate-mapping package="cascade">
<class name="Car" >
<id name="id">
<generator class="native"/>
</id>
<property name="year" /> Cascade attribute on *-to-one> tags
<property name="model" />
<property name="maker" />
<many-to-one name="owner" column="owner_id" class="Person"
cascade="persist, merge, save-update" />
</class>
</hibernate-mapping>
Any of the Hibernate cascade types
<hibernate-mapping package="cascade">
<class name="Person" >
<id name="id">
<generator class="native"/>
</id> Cascade attribute on collection tags
<property name="firstname" />
<property name="lastname" />
<bag name="cars" inverse="true" cascade="persist, merge, save-update">
<key column="owner_id" />
<one-to-many class="Car"/>
Any of the Hibernate cascade types
</bag>
</class>
</hibernate-mapping>
© 2014 Time2Master 43
Delete Orphan
One to Many without delete-orphan One to Many using delete-orphan
Persistence Persistence
context context
customer customer
Remove a Remove a
creditcard creditcard creditcard creditcard
from the from the
creditcard collection creditcard collection
creditcard creditcard
Persistence Persistence
context context
customer The removed customer
creditcard will The removed
creditcard creditcard creditcard is
be ‘orphaned’
automatically
creditcard deleted from
the database
creditcard creditcard
© 2014 Time2Master 44
Association Mapping
WRAPPING UP
© 2014 Time2Master 45
Mapping Tips
Reconsider many to many relationships
Often people want to store additional data related
to many-to-many association
If not now then perhaps in the near future
May be better to map the join table as an entity
© 2014 Time2Master 46
Mapping Tips
One to One relationships are often very tight
E.g. a Customer always has an Address
You may want to to include address in the
customer table
You can do so using embedded classes which we
will explain later in the course
Address
Customer
+street
location
+firstname +suiteOrApt
+lastname +city
+address 1 1
+state
+zip
+customer
© 2014 Time2Master 47
Convenience Methods
To create or remove a bi-directional association
two references have to be created or removed
It can become a bit tedious to set both sides
Create convenience methods that set both
sides in one go
Normal Getter / Setter for the
public class Person { collection of car references
...
public List<Car> getCars() { return cars; }
public void setCars(List<Car> cars) { this.cars = cars; }
© 2014 Time2Master 49
Active Learning
What are the seven different types of
associations?
© 2014 Time2Master 50
Module Summary
In this module we discussed how to map the
seven different types of associations
Both uni-directional and bi-directional associations
ManyToOne, OneToMany, OneToOne, and ManyToMany
Most can be mapped in more than one way
With or without a join table / joining on FKs or PKs
Association can also specify which actions
should cascade down to the related entities
Persist, merge, remove, refresh, all
© 2014 Time2Master 51