Suppose we have these tables which contain the data shown below:

CONTINENT
| CONT_ID |
CONT_NAME |
| 1 |
Africa |
| 2 |
Asia |
| 3 |
Europe |
| 4 |
North America |
| 5 |
South America |
| 6 |
Oceania |
| 7 |
Antarctica |
|
|
COUNTRY
| CTRY_ID |
CONT_ID |
CTRY_NAME |
AREA |
| 14 |
3 |
Germany |
137882 |
| 48 |
1 |
Ghana |
92100 |
| 53 |
6 |
Australia |
2969907 |
| 73 |
3 |
Greece |
50949 |
| 122 |
3 |
Georgia |
26900 |
| 123 |
6 |
New Zealand |
104428 |
| 147 |
1 |
Gambia |
4361 |
| 149 |
1 |
Gabon |
103347 |
|
Our slightly modified COUNTRY table now has a foreign key column (CONT_ID)
which references the CONTINENT table.
We map a Continent POJO class to the CONTINENT table in a similar fashion
to the Country class introduced in
Getting Started with Hibernate.
@Entity
@Table(name="CONTINENT")
public class Continent {
@Id
@GeneratedValue
@Column(name="CONT_ID")
private Integer id;
@Column(name="CONT_NAME")
private String name;
@OneToMany
@JoinColumn(name = "CONT_ID")
private Set<Country> countries = new HashSet<Country>();
// Accessors
}
The
@OneToMany annotation maps the association between Continent and Country with
one-to-many multiplicity; a continent consists of many countries but a country can only
exist in one continent.
The CONT_ID column, which is specified in the
@JoinColumn
annotation, names the foreign key column on the COUNTRY table which is used to join the two tables together.
Here we use a Set with a HashSet implementation to store the Country objects.
Always use a collection interface (Set, List, Map etc) for the field type and initialize it with an appropriate implementation in its declaration. Other collection types such
as List and Map can be used if these are more suitable.
To access all the countries belonging to a particular continent, simply iterate over the collection.
Continent europe = (Continent) session.load(Continent.class, 3);
System.out.println(europe.getName() + " has " +
europe.getCountries().size() +
" countries which are");
for (Country country : europe.getCountries()) {
System.out.println(" " + country.getName());
}
Fetching Strategy
Let's take a moment to look at the SQL generated by Hibernate in this example. When europe.getName() is called, the following SQL is executed and the results used to populate the Continent object:
select
CONT_ID,
CONT_NAME
from
CONTINENT
where
CONT_ID=?
Next, this SQL is used to populate the countries field but only when the europe.getCountries().size() method is called:
select
CONT_ID,
CTRY_ID,
CTRY_NAME,
AREA
from
COUNTRY
where
CONT_ID=?
In other words, the countries collection is only populated when it is accessed for the first time and
not when the Continent object is loaded. This on-demand style of access is called
lazy loading and is the default for
@OneToMany and
@ManyToMany associations.
We can retrieve the data more aggressively by changing the fetch strategy to EAGER on the
countries field of the Continent class.
@OneToMany (fetch=FetchType.EAGER)
@JoinColumn(name = "CONT_ID")
private Set<Country> countries = new HashSet<Country>();
Now the Continent and all the related Country objects will be
retrieved using a single SQL statement.
select
CONTINENT.CONT_ID,
CONTINENT.CONT_NAME,
COUNTRY.CTRY_ID,
COUNTRY.CTRY_NAME,
COUNTRY.AREA
from
CONTINENT
left outer join
COUNTRY
on CONTINENT.CONT_ID = COUNTRY.CONT_ID
where
CONTINENT.CONT_ID=?
If the countries collection is rarely accessed from the Continent object then lazy loading may be the best option.
However, if the countries collection is frequently used then eager loading should be considered.
|