Module 04 - Hibernate Collection Mapping-upd
Module 04 - Hibernate Collection Mapping-upd
© 2014 Time2Master 1
Collections
Java uses collections of references to
implement the many side(s) of associations
So far we’ve always mapped collections as bags
© 2014 Time2Master 2
Program to Interface
Hibernate requires you to program to
interface (P2I) for your collections
Replaces collections with its own implementations
P2I is also a general Java best practice
@Entity
public class Person {
@Id
@GeneratedValue
private int id; collection initialized
private String firstname; right away
private String lastname;
@OneToMany
private List<Car> cars = new ArrayList();
...
Interface
Hibernate replaces ArrayList with its own
List implementation once the collection
has been retrieved or persisted
© 2014 Time2Master 3
Collection Mapping
COLLECTION: BAG
© 2014 Time2Master 4
Bags
The most basic collection is a bag
A bag has no inherent order
A bag can contain duplicates
© 2014 Time2Master 5
Bag Implementation
java.util.Collection is a bag interface
Java has no official bag implementation
Implemented with a
single foreign key
© 2014 Time2Master 6
Mapping a Bag
java.util.Collection maps as a Bag
@Entity
public class Person {
@Id
@GeneratedValue
private int id;
private String firstname;
private String lastname;
@OneToMany(mappedBy="owner", cascade=CascadeType.PERSIST)
private Collection<Tool> tools = new ArrayList();
...
Hibernate will map a We use an ArrayList since
Collection as a Bag there is no official java
Bag implementation
@Entity
public class Tool { We’ve mapped this Person Tool
owns
@Id collection as a bi- +firstname +type
+lastname 1 0..* +size
@GeneratedValue directional one to many
private int id;
private String type;
private String size;
@ManyToOne
private Person owner;
...
© 2014 Time2Master 7
Mapping a Bag
By default java.util.List maps as a Bag
@Entity
public class Person {
@Id
@GeneratedValue
private int id;
private String firstname;
private String lastname;
@OneToMany(mappedBy="owner", cascade=CascadeType.PERSIST)
private List<Tool> tools = new ArrayList();
...
By default List also ArrayList is the
maps to a Bag most common List
implementation
@Entity
public class Tool { Person Tool
owns
@Id Same bi-directional one +firstname +type
+lastname 1 0..* +size
@GeneratedValue to many as last slide
private int id;
private String type;
private String size;
@ManyToOne
private Person owner;
...
© 2014 Time2Master 8
Bag XML
The XML bag mappings for
<hibernate-mapping package="bag_collection"> java.util.Collection and
<class name="Person" >
java.util.List are the same
<id name="id">
<generator class="native"/>
Only the </id>
name <property name="firstname" /> Use the <bag> tag to map bag
<property name="lastname" />
attribute <bag name="tools" inverse="true" access="field" cascade="persist">
is required <key column="owner_id" />
<one-to-many class="Tool"/>
</bag> <key> and an
</class> association tag
</hibernate-mapping> are required
<hibernate-mapping package="bag_collection">
<class name="Tool" >
<id name="id"> For the sake of completeness: the
<generator class="native"/> other side of the bi-directional one
</id> to many association
<property name="type" />
<property name="size" />
<many-to-one name="owner" column="owner_id" class="Person" />
</class>
</hibernate-mapping>
© 2014 Time2Master 9
Collection Mapping
COLLECTION: SET
© 2014 Time2Master 10
Sets
Sets are bags that can not contain duplicates:
A set still has no inherent order
A set can not contain duplicates
© 2014 Time2Master 11
Set Implementation
Java has the java.util.Set interface
java.util.HashSet is the general implementation
Foreign key
© 2014 Time2Master 12
equals() & hashCode()
@Entity
public class Name {
private String firstname;
private String lastname;
...
Compares object
public boolean equals(Object obj) { contents for equality
if (this == obj)
return true;
if ((obj == null) || obj.getClass() != this.getClass())
return false;
Name n = (Name) obj;
if (firstname == n.firstname || (firstname != null && firstname.equals(n.firstname))
&& lastname == n.lastname || (lastname != null && lastname.equals(n.lastname))) {
return true;
} else {
return false;
}
}
Generates an int based on
public int hashCode() { the class contents
int hash = 1234;
if (firstname != null)
hash = hash + firstname.hashCode();
if (lastname != null)
hash = hash + lastname.hashCode();
return hash;
}
© 2014 Time2Master 13
Mapping a Set
java.util.Set maps as a Set
@Entity
public class Toolbox {
@Id
@GeneratedValue
private int id;
private String manufacturer;
private String model;
@OneToMany(mappedBy="toolbox", cascade=CascadeType.PERSIST)
private Set<Tool> tools = new HashSet();
...
Set maps as a set HashSet is the
most common Set
implementation
@Entity
public class Tool {
Tool class completes the Toolbox Tool
@Id contains
@GeneratedValue bi-directional many to one +manufacturer +type
1 1..* +size
private int id;
private String type;
private String size;
@ManyToOne
private Toolbox toolbox;
...
© 2014 Time2Master 14
Set XML
<hibernate-mapping package="set">
<class name="Toolbox" >
<id name="id"> Only difference between
<generator class="native"/> mapping a set and a bag is
</id> in the tag name <set>
<property name="manufacturer" />
<set name="tools" inverse="true" access="field" cascade="persist">
<key column="toolbox_id" />
<one-to-many class="Tool"/>
</set> As with the bag mapping
</class> <key> and an association
</hibernate-mapping>
tag are required
<hibernate-mapping package="set">
<class name="Tool" >
<id name="id">
<generator class="native"/>
</id>
<property name="type" />
<property name="size" />
<many-to-one name="toolbox" column="toolbox_id" class="Toolbox" />
</class>
</hibernate-mapping>
© 2014 Time2Master 15
Collection Mapping
COLLECTION: MAP
© 2014 Time2Master 16
Maps
A Map ‘maps’ a set of keys to a bag of values:
Each value in the bag has a unique key
Given a key, the map can quickly retrieve the value
No inherent order in either keys or values
© 2014 Time2Master 17
Map Implementation
Java has the java.util.Map interface
Java.util.HashMap is the most common map
Foreign key
© 2014 Time2Master 18
Map Mapping Issues
There are two types of map mappings:
1. The key is already part of the value entity class
E.g. name is already part of the Pet class
2. The key is not part of the value entity class
The key index column becomes an additional column,
just like the index column for a list
Mapping issues similar to List (coming up)
Pet
Person Person Pet
owns +name owns
+firstname +species +firstname +species
+lastname +race +lastname +race
1 0..* 1 0..*
Name
already Added
there Name
column
© 2014 Time2Master 19
Key is part of the Entity Class
@Entity
public class Person {
@Id
@GeneratedValue
private int id;
@MapKey private String firstname; Normal @OneToMany
specifies the private String lastname;
key column @OneToMany(mappedBy="owner", cascade=CascadeType.PERSIST)
on the @MapKey(name="name")
private Map<String, Pet> pets = new HashMap();
remote class
...
@Entity
public class Pet {
Person Pet
@Id owns
+firstname +species
@GeneratedValue Specified by @MapKey, +lastname +race
1 0..*
private int id; will be indexed +name
private String name;
Normal private String species;
@ManyToOne private String race;
@ManyToOne
private Person owner;
...
© 2014 Time2Master 20
XML
<hibernate-mapping package="map">
<class name="Person" >
<id name="id">
<generator class="native"/>
</id>
<property name="firstname" /> <map> similar to <bag> and <set>
<property name="lastname" />
<map name="pets" access="field" cascade="persist" inverse="true">
<key column="owner_id" />
<map-key type="string" column="name"/>
<one-to-many class="Pet"/> <map-key> specifies the key
</map> column on the remote table
</class>
</hibernate-mapping>
<hibernate-mapping package="map">
<class name="Pet">
<id name="id">
<generator class="native" />
</id>
<property name="name" />
<property name="species" />
Regular <many-to-one>
<property name="race" />
<many-to-one name="owner" column="owner_id" class="Person" />
</class>
</hibernate-mapping>
© 2014 Time2Master 21
Map – Separate Key
@Entity
public class Person {
@Id
@GeneratedValue
private int id; @OneToMany is
private String firstname; the owning side
@JoinColumn private String lastname; @MapKeyColumn
and its name @OneToMany(cascade = CascadeType.PERSIST) defaults to not nullable
attribute are @JoinColumn(name="owner_id") - >insertion problems
@MapKeyColumn(name="name", nullable=true)
required
private Map<String, Pet> pets = new HashMap();
...
@Entity
public class Pet {
@Id False updateble and
@ManyToOne @GeneratedValue
insertable on
does not have private int id;
private String species; @JoinColumn
a mappedBy instead of mappedBy
private String race;
attribute @ManyToOne
@JoinColumn(name="owner_id", updatable=false, insertable=false)
private Person owner;
...
© 2014 Time2Master 22
Separate Key XML
<hibernate-mapping package="map_seperateKey">
<class name="Person" >
<id name="id">
<generator class="native"/>
</id>
<property name="firstname" />
<property name="lastname" /> Map side is the owning
<map name="pets" access="field" cascade="persist" > side (no inverse)
<key column="owner_id" />
<map-key type="string" column="name" />
<one-to-many class="Pet"/>
</map> When <map-key> specifies a
</class> non-existing column Hibernate
</hibernate-mapping> will add it as the key column
<hibernate-mapping package="map_seperateKey">
<class name="Pet"> <many-to-one> does not
<id name="id">
have an inverse attribute
<generator class="native" />
</id> to specify that it is not
<property name="species" /> the owning side
<property name="race" />
<many-to-one name="owner" column="owner_id" class="Person"
insert="false" update="false" />
</class> Uses insert and update false instead
</hibernate-mapping> to emulate inverse behavior
© 2014 Time2Master 23
Collection Mapping
COLLECTION: LIST
© 2014 Time2Master 24
Lists
Lists are bags with an inherent order:
A List has an inherent, arbitrary order
A List can still contain duplicates
© 2014 Time2Master 25
List Implementation
Java has the java.util.List interface
Java.util.ArrayList is the most common list
Foreign key
@Entity
public class Item {
@Id
@GeneratedValue
private int id;
private String name;
private String description;
@ManyToOne Normal @ManyToOne
private Person buyer;
...
© 2014 Time2Master 27
Hibernate List Mapping Issues
List side needs to be the owning side
Bi-directional problems:
In a bi-directional one to many, the side without
the Foreign Key needs to be the owning side
In a bi-directional many to many with two lists,
both will have to be ‘owner’ (2 uni != bi-direct)
Person Item
needs to buy
+firstname +name
+lastname +description
1 0..*
Although the
Foreign key is
on the other
side, Person will Foreign key
need to be the
owning side
Additional indexed column
to maintain sequence
© 2014 Time2Master 28
One to Many bi-directional List
@Entity
public class Person {
@Id
@GeneratedValue
private int id; @OneToMany is
private String firstname; the owning side
@JoinColumn private String lastname;
and its name @OneToMany(cascade=CascadeType.PERSIST)
attribute are @JoinColumn(name="buyer_id")
@OrderColumn(name="sequence")
required (Why?)
private List<Item> shopList = new ArrayList();
...
@Entity
public class Item {
@Id Updateble and
@ManyToOne @GeneratedValue
insertable false on
does not have private int id;
private String name; @JoinColumn to
a mappedBy emulate mappedBy
private String description;
attribute @ManyToOne
@JoinColumn(name="buyer_id", updatable=false, insertable=false)
private Person buyer;
...
© 2014 Time2Master 29
List XML
<hibernate-mapping package="list">
<class name="Person" >
<id name="id">
<generator class="native"/>
</id>
<property name="firstname" /> <list> is similar to <bag> and <set>
<property name="lastname" />
<list name="shopList" access="field" cascade="persist"> List side is the owning
<key column="buyer_id"/>
<list-index column="sequence"/>
side (no inverse)
<one-to-many class="Item"/>
</list> Also requires a <list-index>
</class> tag not just a <key> and an
</hibernate-mapping> association tag
<hibernate-mapping package="list">
<class name="Item"> <many-to-one> does not
<id name="id">
have an inverse attribute
<generator class="native" />
</id> to specify that it is not
<property name="name" /> the owning side
<property name="description" />
<many-to-one name="buyer" column="buyer_id" class="Person"
insert="false" update="false" />
</class> Uses insert and update
</hibernate-mapping> false instead of inverse
© 2014 Time2Master 30
Collection Mapping
ORDER BY
© 2014 Time2Master 31
Order By
Hibernate can add an ‘ORDER BY’ SQL clause
when retrieving a collection
Sets, bags, and maps can be ordered in this way,
the order is done by the database
Collections mapped as list can not be re-ordered,
they already have a specific order
@Entity
public class Toolbox {
@Id
@GeneratedValue
private int id;
private String manufacturer;
private String model;
@OneToMany(mappedBy="toolbox", cascade=CascadeType.PERSIST)
@OrderBy("size ASC")
private Set<Tool> tools = new HashSet<Tool>();
<hibernate-mapping package="bag_collection">
<class name="Person" >
<id name="id">
<generator class="native"/>
</id>
<property name="firstname" />
<property name="lastname" />
<bag name="tools" inverse="true" access="field" cascade="persist“ order-by="type ASC">
<key column="owner_id" />
<one-to-many class="Tool"/>
</bag>
In XML <bag>, <set>,
</class> and <map> can specify
</hibernate-mapping> the order-by attribute
© 2014 Time2Master 33
Collection Mapping
WRAPPING UP
© 2014 Time2Master 34
Mapping Tip
Recommend always creating accessor
convenience methods
Not just for bi-directional associations
Person Car
@Entity owns
public class Person { +firstname +year
+lastname +model
@Id 1 0..*
+cars +maker
@GeneratedValue
private int id;
private String firstname; Uni-directional one-to-many
private String lastname;
@OneToMany
private List<Car> cars = new ArrayList<Car>();
...
© 2014 Time2Master 35
Active Learning
What does it mean to have inherent order?
© 2014 Time2Master 36
Module Summary
We’ve covered the following collections:
Bags – Allow duplicates, no guaranteed order, but can
be ordered by Hibernate
Sets – Do not allow duplicates, no guaranteed order,
can be ordered by Hibernate
Lists – Allow duplicates, a guaranteed order by using
an additional index column in the db
Maps – Link a set of keys to a bag of values, by having
an (additional) key that is used as key
Default to using bags, use other types as needed
© 2014 Time2Master 37