Module 06 - Hibernate Complex Mapping-upd
Module 06 - Hibernate Complex Mapping-upd
© 2014 Time2Master 1
Complex Mappings
In this module we will cover:
Secondary tables – allow a class to be mapped to
multiple tables
Embedded classes – allow multiple classes to be
mapped to a single table
Composite keys – can be made using embedded
classes
Immutable entities – Hibernate can optimize for
entities that never change
© 2014 Time2Master 2
Complex Mapping
SECONDARY TABLES
© 2014 Time2Master 3
Secondary Tables
Secondary tables can be used anywhere to
move properties into separate table(s)
To do so, the property has to specify the table
Secondary tables can even be used in combination
with the Single table inheritance strategy
Secondary table used in an
@Entity inheritance hierarchy
@DiscriminatorValue("savings")
@SecondaryTable(
name="SavingsAccount",
pkJoinColumns=@PrimaryKeyJoinColumn(name="number")
)
public class SavingsAccount extends Account {
@Column(table="SavingsAccount")
private double APY;
Property specifies that it should
... be on the SavingsAccount table
© 2014 Time2Master 4
Secondary Table
@SecondaryTables can specify
multiple @SecondaryTable pkJoinColumns can be used to
@Entity specify a multi column join
@SecondaryTables(
@SecondaryTable(name="warehouse", pkJoinColumns = {
@PrimaryKeyJoinColumn(name="product_id", referencedColumnName="number")
}
))
public class Product { JoinColumn name can differ
@Id from the referenced column
@GeneratedValue
private int number;
private String name;
private BigDecimal price; Properties need to
@Column(table="warehouse") specify the secondary
private boolean available; table to be on it All you really need is @SecondaryTable
and a name, the rest is optional
... @Entity
@SecondaryTable(name="warehouse")
public class Product {
@Id
Product
@GeneratedValue
private int number;
+number
+name private String name;
+price private BigDecimal price;
+available
@Column(table = "warehouse")
private int available;
...
© 2014 Time2Master 5
XML
<hibernate-mapping package="join_tables">
<class name="Product">
<id name="number">
<generator class="native" />
</id>
<property name="name" />
<property name="price" />
<join> tag to specify the table
<join table="warehouse">
<key column="product_id" /> Requires <key> to specify
<property name="available" /> the pk join column
</join>
</class>
</hibernate-mapping>
Product Table
Product
+number
+name
+price
+available
Warehouse Table
© 2014 Time2Master 6
Complex Mapping
EMBEDDED CLASSES
© 2014 Time2Master 7
Embedded Classes
Combine multiple classes in a single table
Especially useful for tight associations
These classes are considered value classes
rather than entity classes
Address is embedded
inside the Person table
Address
Person
lives at +street
+firstname +city
+lastname 1 1 +state
+zip
© 2014 Time2Master 8
Embeddable
@Embeddable
instead of @Entity
@Entity @Embeddable
public class Person { public class Address {
@Id private String street;
@GeneratedValue private String city;
private int id; private String state;
@Embedded private String zip;
private String firstname;
annotation is private String lastname;
used for ... No @Id in embeddable
embeddable @Embedded
objects private Address address;
...
Address
Person
lives at +street
+firstname +city
+lastname 1 1 +state
+zip
© 2014 Time2Master 9
XML
<hibernate-mapping package="embedded">
<class name="Person">
<id name="id">
<generator class="native" />
</id>
<property name="firstname" />
<property name="lastname" />
<component> tag indicates
<component name="address" class="Address"> an embedded object
<property name="street" />
<property name="city" />
<property name="state" />
<property name="zip" />
</component>
</class>
</hibernate-mapping>
Address
Person
lives at +street
+firstname +city
+lastname 1 1 +state
+zip
© 2014 Time2Master 10
Multiple Embedded Addresses
@Entity
public class Customer {
@Id
@GeneratedValue
private int id;
private String firstname; Rename the column names
private String lastname; for the embedded object
@Embedded
using @AttributeOverrides
@AttributeOverrides( {
@AttributeOverride(name="street", column=@Column(name="ship_street")),
@AttributeOverride(name="city", column=@Column(name="ship_city")),
@AttributeOverride(name="state", column=@Column(name="ship_state")),
@AttributeOverride(name="zip", column=@Column(name="ship_zip"))
})
private Address shipping;
@Embedded
@AttributeOverrides( {
@AttributeOverride(name="street", column=@Column(name="bill_street")),
@AttributeOverride(name="city", column=@Column(name="bill_city")),
@AttributeOverride(name="state", column=@Column(name="bill_state")),
@AttributeOverride(name="zip", column=@Column(name="bill_zip"))
})
private Address billing;
11
Multiple Addresses XML
<hibernate-mapping package="embedded">
<class name="Customer">
<id name="id">
<generator class="native" />
</id>
<property name="firstname" />
<property name="lastname" />
© 2014 Time2Master 12
Complex Mapping
COMPOSITE KEYS
© 2014 Time2Master 13
Composite Keys
Composite Keys are multi-column Primary Keys
By definition these are natural keys
Have to be set by the application (not generated)
Generally found in legacy systems
Also create multi-column Foreign Keys
© 2014 Time2Master 14
Composite Ids
@Embeddable
@Embeddable
public class Name {
private String firstname;
private String lastname;
@Entity
public class Employee { Embeddable object as identifier
@EmbeddedId creates composite key
private Name name;
@Temporal(TemporalType.DATE)
private Date startDate;
...
PK is made of
Both firstname
and lastname
© 2014 Time2Master 15
equals() & hashCode()
@Embeddable
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 16
XML
<hibernate-mapping package="composite_key">
<class name="Employee">
<composite-id name="name" class="Name"> <composite-id> tag is used
<key-property name="firstname" /> in XML to specify the
<key-property name="lastname" /> property and class name
</composite-id>
PK is made of
Both firstname
and lastname
© 2014 Time2Master 17
Foreign Keys to Composite Ids
@Entity
public class Employee { Same Name embeddable
@EmbeddedId @Id as before
private Name name;
@Temporal(TemporalType.DATE)
private Date startDate; Normal mappedBy on this side
@OneToMany(mappedBy = "owner")
private List<Project> projects = new ArrayList<Project>();
...
@Entity
public class Project { Two column
@Id
Foreign Key
@GeneratedValue
private int id;
private String name;
@ManyToOne
@JoinColumns( {
@JoinColumn(name = "Emp_firstname", referencedColumnName = "firstname"),
@JoinColumn(name = "Emp_lastname", referencedColumnName = "lastname")
})
private Employee owner;
Two column FK
... specification
© 2014 Time2Master 18
XML Composite FK
<hibernate-mapping package="composite_key">
<class name="Employee">
<composite-id name="name" class="Name">
<key-property name="firstname" />
<key-property name="lastname" />
</composite-id>
<property name="startDate" type="date" /> Even though the collection
is inverse we still need to
<bag name="projects" inverse="true"> specify both columns
<key>
<column name="Emp_firstname" />
<column name="Emp_lastname" />
</key> Using <column> tags inside <key>
<one-to-many class="Project" /> instead of the column attribute on <key>
</bag>
</class>
</hibernate-mapping>
<hibernate-mapping package="composite_key">
<class name="Project">
<id name="id">
<generator class="native" />
</id>
ELEMENT COLLECTIONS
© 2014 Time2Master 20
Element Collections
For collections of primitive values or
collections of embeddables
© 2014 Time2Master 21
@ElementCollection
@Entity
public class Person { Optionally specify the name
@Id @GeneratedValue for the collection table
private int id;
@ElementCollection
@CollectionTable(name = "firstNames")
private List<String> givenNames = new ArrayList<>();
@ElementCollection
@CollectionTable(name = "lastNames")
private List<String> familyNames = new ArrayList<>();
@ElementCollection
private List<Address> addresses = new ArrayList<>();
...
© 2014 Time2Master 22
Map
@Entity
public class Person {
@Id @GeneratedValue
Optionally specify the name
private int id;
private String name; for the additional key column
@ElementCollection
@MapKeyColumn(name = "name")
private Map<String, Pet> Pets = new HashMap<>();
...
© 2014 Time2Master 23
Complex Mapping
IMMUTABLE ENTITIES
© 2014 Time2Master 24
Immutable Entities
An immutable entity is an entity that
Once created, does not change – no updates
Hibernate can perform several optimizations
© 2014 Time2Master 25
Immutability
@Entity
@org.hibernate.annotations.Entity(mutable=false) Set mutable false using
public class Payment { Hibernate Entity extension
@Id Field access through
@GeneratedValue placement of @Id
private final int id;
private final double amount;
@Column(name="`to`")
private final String to;
@Column(name="`from`")
private final String from;
Data is set in constructor
public Payment() {}
public Payment(double amount, String to, String from) {
this.amount = amount;
this.to = to;
this.from = from;
} Getters, but no Setters
public int getId() { return id; }
public double getAmount() { return amount; }
public String getTo() { return to; }
public String getFrom() { return from; }
}
© 2014 Time2Master 26
XML
© 2014 Time2Master 27
Active Learning
What is a value class?
© 2014 Time2Master 28
Module Summary
In this module we covered some of the more
interesting mappings possible with Hibernate
Many of these mappings are very useful when
mapping to a legacy database
Embeddable components also have their place
in non-legacy systems
Allow a fine-grained object model to be mapped
to a more coarse and efficient db model
Sacrifices some flexibility for greater efficiency
© 2014 Time2Master 29