Sei sulla pagina 1di 15

Hibernate Association Mappings

Introduction

Association mappings are the often most difficult thing to get right. In this section we'll go through the
canonical cases one by one, starting with unidirectional mappings, and then considering the bidirectional cases.
We'll use Person and Address in all the examples.

We'll classify associations by whether or not they map to an intervening join table, and by multiplicity.

Nullable foreign keys are not considered good practice in traditional data modelling, so all our examples use not
null foreign keys. This is not a requirement of Hibernate, and the mappings will all work if you drop the
nullability constraints.

Unidirectional associations

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 )

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. (Notice that we've
reversed the direction of the association in this example.)

<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 )

One- to- Many:

A unidirectional one-to-many association on a foreign key is a very unusual case, and is not really
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 )
We think it's better to use a join table for this kind of association.

Unidirectional associations with join tables

One-to-Many:

A unidirectional one-to-many association on a join table is much preferred. Notice that by specifying
unique="true", we have changed 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 )

Many- to-One:

A unidirectional many-to-one association on a join table is quite common when the association is optional.

<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 )

One- to- One:

A unidirectional one-to-one association on a join table is extremely unusual, but possible.

<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 )

Many- to- Many:

Finally, we have 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

One- to- Many / Many- to- One:

A bidirectional many-to-one association is the most common kind of association. (This is 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) you need to set the key column of the foreign key to not null, and
let Hibernate 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>

It is important that you define not-null="true" on the <key> element of the collection mapping if the underlying
foreign key column is NOT NULL. Don't only declare not-null="true" on a possible nested <column> element,
but on the <key> element.

One- to- One:

A bidirectional one-to-one association on a foreign key is quite 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

One- to- Many / Many- to- One:

A bidirectional one-to-many association on a join table. Note that 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 )

One- to-One:

A bidirectional one-to-one association on a join table is extremely unusual, but possible.

<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 )

Many- to-Many:

Finally, we have 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 makes it possible to handle more complex
situations using SQL fragments embedded in the mapping document. For example, if a table with historical
account information data defines accountNumber, effectiveEndDate and effectiveStartDatecolumns, 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"/>

Then we can map an association to the current instance (the one with null effectiveEndDate) 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. Then an association to the employee's most recent
employer (the one with the most recent startDate) might be mapped this 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>

You can get quite creative with this functionality, but it is usually more practical to handle these kinds of cases
using HQL or a criteria query.
Hibernate: Understanding Associations - Associations: What are They?

To represent relationships between classes, associations are used. Before going into the details of how Hibernate
perceives associations, an understanding of the working of container managed associations is needed. Managed
association means that, if a change is made to one end of the association, it will be reflected at the other end.

For example, let's consider the Order table. If it has a one-to-many relationship with the Product table, the Order
class will have a one-to-many association with the Product class. So when changes are made to the attributes
participating in this association, they will be reflected at the other end automatically. The developer doesn’t
have to mange the associations manually.

How Hibernate implements the management of association is different from that of Container Managed
Relationships/Associations or CMR generally provided by EJB CMP. In CMR the association is bidirectional,
whereas Hibernate treats each association as different. The primary reason is that Hibernate builds its
persistence based on Plain Old Java Object or POJO, and in Java associations are unidirectional. Thus Hibernate
doesn’t implement CMR. So the associations are unidirectional. In essence it means if the on-update-cascade
attribute is set in the mapping for Order and not in Product, any operation on Product would not affect Order.

Keeping these points in mind, let's move on to the different types of associations supported by Hibernate.
Hibernate mainly supports two types of associations:

1. One-to-Many
2. Many-to-One

To work with associations, the changes would be required in both the mapping as well as in the persistence
class. The details are as follows:

1. One-to-Many:

In this kind of association one object of a class is in a relationship with many objects of another class. The class
that is having the single instance contains a collection of instances of the other class. To specify this association
the mapping file would have to be modified. The added code would be:

<one-to-many
name=”nameOfheVariable
column="NAME_OF_THE_COLUMN"
class="ClassName"
not-null="true"/>

The name attribute takes the name of the variable that is participating in the association. The column attribute is
used to specify the table column that is participating in the association. The class attribute takes the class name
to which this class is associated. In the Persistent class the following change would be there:

class <className>
{
//other variable declarations
Set <className> =new HashSet();
//constructors and getter/setter code follows
:
}
Then the constructor with the added parameter for Set must be given along with the getter and setter for the Set.
In the third section I will be discussing a real world example to illustrate this point.

2. Many-to-One:

This is the opposite of the One-to-Many association. In this case, the class that is having a Many-to-One
association contains the object of the class. For example, if class A has a Many-to-One association with class B,
then each instance of B would have an instance of A. And the identity of this instance can be the same for
multiple objects of B. The change in the mapping would be:

<many-to-one
name=”nameOfheVariable
column="NAME_OF_THE_COLUMN"
class="ClassName"
not-null="true"/>

The attributes are the same as the case of One-to-Many. In the case of code it would be

class <className>
{
<classNameofAssociatedClass> o=new <classNameofAssociatedClass>
//construtors and getter/setter code follows
:
}

The associations will be clearer when I discuss the real world usage of association in the next section.

Hibernate: Understanding Associations - Associations in the Real World

Till now I was using only one table. Let's make things interesting by adding one more table. This table is
the Product table. Each Order can have more than one Product. Hence the relationship between Order and
Product is One-to-Many. The schema of the Product table is:

CREATE TABLE PRODUCT(


ID VARCHAR NOT NULL PRIMARY KEY,
NAME VARCHAR NOT NULL,
PRICE DOUBLE NOT NULL,
AMOUNT INTEGER NOT NULL,
ORDER_ID VARCHAR NOT NULL)

The next step is to create the persistent class for the Product table. The persistent class is as follows:

package com.someorg.persist;

public class Product {


private String id;
private String name;
private double price;
private int amount;
private Order order;
public Product(String id, String name, double price, int amount, Order
order)
{
this.order=order;
//others not shown for brevity
}
public String getId() {
return id;
}
public void setId(String string) {
id = string;
}
// default constructor and other
// getters/setters not shown for brevity
// ...
}

The next part is Product.hbm.xml, that is the mapping file:

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping>
<class name="test.hibernate.Product"
table="products">

<id name="id" type="string"


unsaved-value="null">
<column name="id" sql-type="char(32)"
not-null="true"/>
<generator class="assigned"/>
</id>
<property name="name">
<column name="name" sql-type="char(255)"
not-null="true"/>
</property>
<property name="price">
<column name="price" sql-type="double"
not-null="true"/>
</property>
<property name="amount">
<column name="amount" sql-type="integer"
not-null="true"/>
</property>
<property name="orderId">
<column name="ORDER_ID" sql-type="char(255)"
not-null="true"/>

<many-to-one
name="orderId"
column="ORDER_ID"
class="ORDER"
not-null="true"/>

</class>
</hibernate-mapping>

That is all that is required for the Product table. Now we need to make some changes in the Order class.

package com.someorg.persist;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
public class Order {
private String id;
private Date date;
private double priceTotal;
private Set products =new HashSet();
// Automatically set the creation time of
// this Order
public Order() {
this.date = new Date();
}

public Order(String id, Date date, private double priceTotal, Set products){
this.products=products;
//others are not shown for brevity
}
public String getId() {
return id;
}

public void setProducts(Set products) {


this.products = products;
}

public Set getProducts () {


return products;
}

public void setId(String string) {


id = string;
}
// other getters/setters not shown for
// brevity
// ...
}

The next part is changing in the Order.hbm.xml, which is:

<?xml version='1.0' encoding='utf-8'?>


<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-mapping default-cascade="none" default-access="property" auto-import="true">
<class name=" com.someorg.persist.Order" table="orders" mutable="true" select- before-update="false"
optimistic-lock="version">
<id name="id" type="string" unsaved-value="null">
<column name="id" sql-type="char(32)" not-null="true" />
<generator class="assigned" />
</id>
<property name="date" not-null="false" >
<column name="order_date" sql-type="datetime" not-null="true" />
</property>
<property name="priceTotal" not-null="false" >
<column name="price_total" sql-type="double" not-null="true" />
</property>

<set name="products">
<key column="PRODUCT_ID"/>
<one-to-many class="Product"/>
</set>

</class>
</hibernate-mapping>

The next step is to test it. To test it I will be using the Criteria query. In a QBC the joins are done using the
setFetchMode method of the Criteria class. The mode would be JOIN. Here is how it works:

import java.util.List;
//other imports
// use as
// java test. FindOrderById name
public class FindOrderById {
public static void main(String[] args) throws Exception {
// query to issue
String query =
"select order from Order "
+ "where order.id=:id";
// search for what?
String name = args[0];
// init
Configuration cfg = new Configuration()
.addClass(Order.class);
SessionFactory sf = cfg.buildSessionFactory();
// open session
Session sess = sf.openSession();

// search and return


Criteria criteria = session.createCriteria(Order.class);
criteria.add( Expression.eq("id", name) )
.setFetchMode(“products”,FetchMode.JOIN);
List result = criteria.list();
if (list.size() == 0) {
System.out.println("No Order having id "
+ name);
System.exit(0);
}
Order o = (Order) list.get(0);
sess.close();
System.out.println("Found Order: " + o);//this is just an example Here the o //
//object can be traversed to achieve anything
}
}

That brings us to the end of this discussion. Though the complete picture is becoming clear, some edges are still
hazy. These edges will be brought into sharper focus in the forthcoming discussions. Till next time.

Potrebbero piacerti anche