lishman levelup
«previous  next»


Getting Started
Associations
HQL



One-To-Many Association

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

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.
»