Hibernate Framework : ORM Tool :: Hibernate Advanced Mapping
- Overview of Mapping
- Association Mapping
- Collection Mapping
- Inheritance Mapping
- Hibernate Annotations
Overview of Mapping |
- So far, we have learned simple configuration mapping provided by the Hibernate O/R Mapping Framework.
- And we have also learned how to map a POJO class with a database table by using elements such as <class>, <id> , <property> and <column> in a Hibernate configuration file.
- Now, let us understand advance mapping configuration in a Hibernate like association mapping, collection mapping , inheritance mapping and annotation mapping.
Association Mapping |
- Association mappings are often the most difficult thing to implement correctly.
- In this section we examine some canonical cases one by one, starting with unidirectional mappings and then bidirectional cases.
- We will use Person and Address in all the examples.
- Associations will be classified by multiplicity and whether or not they map to an intervening join table.
- Nullable foreign keys are not considered to be good practice in traditional data modelling, so our examples do not use nullable foreign keys.
- This is not a requirement of Hibernate, and the mappings will work if you drop the nullability constraints.
Unidirectional associations
1. Many-to-one
A unidirectional many-to-one association is the most common kind of unidirectional association.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address" column="addressId" not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
create table Person ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )
2. One-to-one
A unidirectional one-to-one association on a foreign key is almost identical. The only difference is the column unique constraint.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address" column="addressId" unique="true" not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
A unidirectional one-to-one association on a primary key usually uses a special id generator In this example, however, we have reversed the direction of the association.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<one-to-one name="person" constrained="true"/>
</class>
create table Person ( personId bigint not null primary key )
create table Address ( personId bigint not null primary key )
3. One-to-many
A unidirectional one-to-many association on a foreign key is an unusual case, and is not recommended.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses">
<key column="personId" not-null="true"/>
<one-to-many class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
create table Person ( personId bigint not null primary key )
create table Address ( addressId bigint not null primary key, personId bigint not null )
You should instead use a join table for this kind of association.
Unidirectional associations with join tables
1. One-to-many
A unidirectional one-to-many association on a join table is the preferred option. Specifying unique="true", changes the multiplicity from many-to-many to one-to-many.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId" unique="true" class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId not null, addressId bigint not null primary key )
create table Address ( addressId bigint not null primary key )
2. Many-to-one
A unidirectional many-to-one association on a join table is common when the association is optional. For example
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress" optional="true">
<key column="personId" unique="true"/>
<many-to-one name="address" column="addressId" not-null="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )
3. One-to-one
A unidirectional one-to-one association on a join table is possible, but extremely unusual.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress" optional="true">
<key column="personId" unique="true"/>
<many-to-one name="address" column="addressId" not-null="true" unique="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
4. Many-to-many
Finally, here is an example of a unidirectional many-to-many association.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId" class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
</class>
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
create table Address ( addressId bigint not null primary key )
Bidirectional associations
1. one-to-many / many-to-one
- A bidirectional many-to-one association is the most common kind of association.
- The following example illustrates the standard parent/child relationship.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address" column="addressId" not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true">
<key column="addressId"/>
<one-to-many class="Person"/>
</set>
</class>
create table Person ( personId bigint not null primary key, addressId bigint not null )
create table Address ( addressId bigint not null primary key )
- If you use a List, or other indexed collection, set the key column of the foreign key to not null.
- Hibernate will manage the association from the collections side to maintain the index of each element, making the other side virtually inverse by setting update="false" and insert="false":
<class name="Person">
<id name="id"/>
...
<many-to-one name="address" column="addressId" not-null="true" insert="false"
update="false"/>
</class>
<class name="Address">
<id name="id"/>
...
<list name="people">
<key column="addressId" not-null="true"/>
<list-index column="peopleIdx"/>
<one-to-many class="Person"/>
</list>
</class>
If the underlying foreign key column is NOT NULL, it is important that you define not-null="true" on the <key> element of the collection mapping.
Do not only declare not-null="true" on a possible nested <column> element, but on the <key> element.
2. One-to-one
A bidirectional one-to-one association on a foreign key is common:
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<many-to-one name="address" column="addressId" unique="true" not-null="true"/>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<one-to-one name="person"
property-ref="address"/>
</class>
create table Person ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
A bidirectional one-to-one association on a primary key uses the special id generator:
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<one-to-one name="address"/>
</class>
<class name="Address">
<id name="id" column="personId">
<generator class="foreign">
<param name="property">person</param>
</generator>
</id>
<one-to-one name="person" constrained="true"/>
</class>
create table Person ( personId bigint not null primary key )
create table Address ( personId bigint not null primary key )
Bidirectional associations with join tables
1.one-to-many / many-to-one
- The following is an example of a bidirectional one-to-many association on a join table.
- The inverse="true" can go on either end of the association, on the collection, or on the join.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses"
table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId" unique="true" class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress" inverse="true" optional="true">
<key column="addressId"/>
<many-to-one name="person" column="personId" not-null="true"/>
</join>
</class>
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint not null primary key )
create table Address ( addressId bigint not null primary key )
2. one to one
A bidirectional one-to-one association on a join table is possible, but extremely unusual.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<join table="PersonAddress" optional="true">
<key column="personId" unique="true"/>
<many-to-one name="address" column="addressId" not-null="true" unique="true"/>
</join>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<join table="PersonAddress" optional="true" inverse="true">
<key column="addressId" unique="true"/>
<many-to-one name="person" column="personId" not-null="true" unique="true"/>
</join>
</class>
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null primary key, addressId bigint not null unique )
create table Address ( addressId bigint not null primary key )
3. Many-to-many
Here is an example of a bidirectional many-to-many association.
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId" class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true" table="PersonAddress">
<key column="addressId"/>
<many-to-many column="personId" class="Person"/>
</set>
</class>
create table Person ( personId bigint not null primary key )
create table PersonAddress ( personId bigint not null, addressId bigint not null, primary key (personId, addressId) )
create table Address ( addressId bigint not null primary key )
More complex association mappings
- More complex association joins are extremely rare. Hibernate handles more complex situations by using SQL fragments embedded in the mapping document.
- For example, if a table with historical account information data defines accountNumber, effectiveEndDate and effectiveStartDatecolumns,
- it would be mapped as follows:
<properties name="currentAccountKey">
<property name="accountNumber" type="string" not-null="true"/>
<property name="currentAccount" type="boolean">
<formula>case when effectiveEndDate is null then 1 else 0 end</formula>
</property>
</properties>
<property name="effectiveEndDate" type="date"/>
<property name="effectiveStateDate" type="date" not-null="true"/>
You can then map an association to the current instance, the one with null effectiveEndDate, by using:
<many-to-one name="currentAccountInfo" property-ref="currentAccountKey" class="AccountInfo">
<column name="accountNumber"/>
<formula>'1'</formula>
</many-to-one>
In a more complex example, imagine that the association between Employee and Organization is maintained in an Employment table full of historical employment data. An association to the employee’s most recent employer, the one with the most recent startDate, could be mapped in the following way.
<join>
<key column="employeeId"/>
<subselect>
select employeeId, orgId
from Employments
group by orgId
having startDate = max(startDate)
</subselect>
<many-to-one name="mostRecentEmployer" class="Organization" column="orgId"/>
</join>
This functionality allows a degree of creativity and flexibility, but it is more practical to handle these kinds of cases using HQL or a criteria query.
Collection Mapping |
Understanding Collection Mapping
- As we know , collection framework that is used to store a group of objects into a single unit.
- This collection framework has the interfaces like Set, List, and Map that are used in Hibernate applications to persists objects of POJO class in database tables.
- The Hibernate O/R mapping framework provides the <set>, there is also <list>,
- <map>, <bag>, <array> mapping elements that allow you to work with various objects.
These elements are know as collection mapping elements.
<map | set | list | bag
name="propertyName"
table="table_name"
schema="schema_name"
lazy="true|extra|false"
inverse="true|false"
cascade="all|none|save-update|delete|all-delete-orphan|delet e-orphan"
sort="unsorted|natural|comparatorClass"
order-by="column_name asc|desc"
where="arbitrary sql where condition"
fetch="join|select|subselect"
batch-size="N"
access="field|property|ClassName"
optimistic-lock="true|false"
mutable="true|false"
node="element-name|."
embed-xml="true|false">
<key .... />
<map-key .... />
<element .... />
- name: the collection property name
- table (optional – defaults to property name): the name of the collection table. It is not used for one-to-many associations.
- schema (optional): the name of a table schema to override the schema declared on the root element
-
lazy (optional – defaults to true): disables lazy fetching and specifies that the association is always eagerly fetched. It can also be used to enable
"extra-lazy" fetching where most operations do not initialize the collection. This is suitable for large collections. - inverse (optional – defaults to false): marks this collection as the "inverse" end of a bidirectional association.
- cascade (optional – defaults to none): enables operations to cascade to child entities.
- sort (optional): specifies a sorted collection with natural sort order or a given comparator class.
- order-by (optional, JDK1.4 only): specifies a table column or columns that define the iteration order of the Map, Set or bag, together with an optional asc or desc.
-
where (optional): specifies an arbitrary SQL WHERE condition that is used when retrieving or removing the collection. This is useful if the collection needs to
contain only a subset of the available data. - fetch (optional, defaults to select): chooses between outer-join fetching, fetching by sequential select, and fetching by sequential subselect.
- batch-size (optional, defaults to 1): specifies a "batch size" for lazily fetching instances of this collection.
- access (optional – defaults to property): the strategy Hibernate uses for accessing the collection property value.
- optimistic-lock (optional – defaults to true): specifies that changes to the state of the collection results in increments of the owning entity’s version. For one-to-many associations you may want to disable this setting.
- mutable (optional – defaults to true): a value of false specifies that the elements of the collection never change. This allows for minor performance optimization in some cases.
Here are some examples
1. <set>
A set element is used to store an unordered and unique collection of values. A set of strings:
<set name="names" table="person_names">
<key column="person_id"/>
<element column="person_name" type="string"/>
</set>
Or
<set name="emailaddress" table="EMP_email_Address">
<key column="emp_id"/>
<element column="email_address" type="string"/>
</set>
2. <bag>
The <bag> element is used to store unordered and duplicate collection of objects. A bag containing integers with an iteration order determined by the order-by attribute:
<bag name="sizes" table="item_sizes" order-by="size asc">
<key column="item_id"/>
<element column="size" type="integer"/>
</bag>
3. <array>
An array of entities, in this case, a many-to-many association:
<array name="addresses"
table="PersonAddress"
cascade="persist">
<key column="personId"/>
<list-index column="sortOrder"/>
<many-to-many column="addressId" class="Address"/>
</array>
4. <map>
A map element is used to store a collection of objects, values, or elements in the key/value pairs. A map from string indices to dates:
<map name="holidays" table="holidays" schema="dbo"
order-by="hol_name asc">
<key column="id"/>
<map-key column="hol_name" type="string"/>
<element column="hol_date" type="date"/>
</map>
5. <list>
A list element is used to store a collection of objects, values, or elements that can contains duplicate elements.
<list name="carComponents" table="CarComponents">
<key column="carId"/>
<list-index column="sortOrder"/>
<composite-element class="CarComponent">
<property name="price"/>
<property name="type"/>
<property name="serialNumber" column="serialNum"/>
</composite-element>
</list>
Inheritance Mapping |
The three strategies Hibernate supports the three basic inheritance mapping strategies:
- table per class hierarchy
- table per subclass
- table per concrete class
1.table per class hierarchy
Suppose we have an interface Payment with the implementors CreditCardPayment, CashPayment, and ChequePayment. The table per hierarchy mapping would display in the following way:
<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<discriminator column="PAYMENT_TYPE" type="string"/>
<property name="amount" column="AMOUNT"/>
...
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
<property name="creditCardType" column="CCTYPE"/>
...
</subclass>
<subclass name="CashPayment" discriminator-value="CASH">
...
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
...
</subclass>
</class>
Exactly one table is required. There is a limitation of this mapping strategy: columns declared by the subclasses, such as CCTYPE, cannot have NOT NULL constraints.
2. table per subclass
A table per subclass mapping looks like this:
<class name="Payment" table="PAYMENT">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</id>
<property name="amount" column="AMOUNT"/>
...
<joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
<key column="PAYMENT_ID"/>
<property name="creditCardType" column="CCTYPE"/>
...
</joined-subclass>
<joined-subclass name="CashPayment" table="CASH_PAYMENT">
<key column="PAYMENT_ID"/>
...
</joined-subclass>
<joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
<key column="PAYMENT_ID"/>
...
</joined-subclass>
</class>
Four tables are required. The three subclass tables have primary key associations to the superclass table so the relational model is actually a one-to-one association.
3. table per concrete class
There are two ways we can map the table per concrete class strategy. First, you can use <union-subclass>.
<class name="Payment">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="sequence"/>
</id>
<property name="amount" column="AMOUNT"/>
...
<union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
<property name="creditCardType" column="CCTYPE"/>
...
</union-subclass>
<union-subclass name="CashPayment" table="CASH_PAYMENT">
...
</union-subclass>
<union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
...
</union-subclass>
</class>
- Three tables are involved for the subclasses.
- Each table defines columns for all properties of the class, including inherited properties.
- The limitation of this approach is that if a property is mapped on the superclass, the column name must be the same on all subclass tables.
- The identity generator strategy is not allowed in union subclass inheritance.
- The primary key seed has to be shared across all unioned subclasses of a hierarchy.
- If your superclass is abstract, map it with abstract=”true”.
- If it is not abstract, an additional table (it defaults to PAYMENT in the example above), is needed to hold instances of the superclass.
Hibernate Annotations |
- Annotations can be defined as some additional information that allows you to insert metadata into a program.
- You can insert just an annotation in a POJO class in place of the hibernate mapping file in Hibernate Application.
The following jar file required
- hibernate-annotations.jar
- Hibernate-commons-annotations.jar
- Ejb3.persistence.jar
XML does not suit all users so there are some alternative ways to define O/R mapping metadata in Hibernate.
Phone.java
package com.javaskool;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "PHONE")
public class Phone {
private long phoneId;
private String phoneType;
private String phoneNumber;
public Phone() {
}
public Phone(String phoneType, String phoneNumber) {
this.phoneType = phoneType;
this.phoneNumber = phoneNumber;
}
@Id
@GeneratedValue
@Column(name = "PHONE_ID")
public long getPhoneId() {
return this.phoneId;
}
public void setPhoneId(long phoneId) {
this.phoneId = phoneId;
}
@Column(name = "PHONE_TYPE", nullable = false, length=10)
public String getPhoneType() {
return this.phoneType;
}
public void setPhoneType(String phoneType) {
this.phoneType = phoneType;
}
@Column(name = "PHONE_NUMBER", nullable = false, length=15)
public String getPhoneNumber() {
return this.phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
TestDrive.java
import com.javaskool.*;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
//import com.example.Phone;
//import com.example.Student;
import com.util.HibernateUtil;
public class Main {
public static void main(String[] args) {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
Set<Phone> phoneNumbers = new HashSet<Phone>();
phoneNumbers.add(new Phone("house", "3236654353"));
phoneNumbers.add(new Phone("mobile", "98893643423"));
Student student = new Student("Eswar", phoneNumbers);
session.save(student);
transaction.commit();
} catch (HibernateException e) {
transaction.rollback();
e.printStackTrace();
} finally {
session.close();
}
}}
Recent Comments