Chapter 4. Persisting Collections
Working with data structures, formally known as Java collections, is inevitable for any Java developer. In this chapter, we will learn about Hibernate’s support for persisting and retrieving collections. We will look in detail at how we can persist the standard Java collections using the Hibernate framework.
As a Java developer, at some point in your programming life, you have probably worked with Java collections. Java collections are the most common data structures in Java, with various algorithm features.
We will, at some point, encounter persistent objects with collections as their values. Persisting simple values from Hibernate’s view is different from persisting collection elements such as java.util.List
or java.util.Map
structures. We need to follow certain mapping procedures and processes to let Hibernate know our intentions.
The familiar Java collections—such as List
, Set
, Array
, and Map
—are all supported by Hibernate. In fact, it goes one step further and creates a couple of other collections, such as bag
and idbag
. Let’s look at them one by one.
Designing to Interfaces
Java provides collection interfaces such as java.util.Collection
(which is the parent to all the collection interfaces except the map interface), java.util.List
(the interface for list data structures), java.util.Set
(for set data structures), java.util.Map
(for key/value map data structures), and more.
The List
implementation is intended to hold ordered lists of elements. We use concrete implementations, such as ArrayList
or LinkedList
, but the point here is that we must design to the List
interface in our Hibernate application rather than to its concrete implementation.
Note
Always use interfaces when you are defining your collection variables. Hibernate does not like it when we use concrete classes as the variable types:
ArrayList
<
String
>
actors
=
new
ArrayList
<
String
>();
Instead, we should define the types using interfaces, like so:
List
<
String
>
actors
=
new
ArrayList
<
String
>();
The reason is that behind the scenes, Hibernate uses its own implementation of List
!
Next we’ll explore the mechanics of persisting one of the most widely used collection structures—lists!
Persisting Lists
Lists are simple and easy data structures to hold items in an orderly manner. They can also keep track of an element’s position, using indexes. We can insert an element anywhere in the list, and retrieve the element using its index.
For example, let’s say we have persisted a list of manufacturers (Toyota, BMW, Mercedes, etc.) via our Java program. We should expect to retrieve the car data from the table in the same order in which it was inserted. So, if we run a query list.get(n)
, we should get back the nth element without fail.
To satisfy this requirement, Hibernate maintains another table with the cars’ indexes. So, when it fetches the cars from the main table, it also fetches the indexed order of these items from the additional table (called, say, CAR_LIST
). It will then associate and map these items together to find out the order, and accordingly feed the client with the ordered responses.
But enough theory. How can we persist our cars list using Hibernate? To get a better understanding, let’s see an example.
List Example: Car Showroom
Consider a simple case of a car showroom. A showroom consists of dozens of cars for customers to view and possibly purchase. We can represent this with a simple model.
Every showroom will have a variety of cars to sell. Some cars may be brand new, while others are secondhand. We model these cars as java.util.List
, as shown here in Showroom
’s implementation (along with the Car
definition):
// Showroom class
public
class
Showroom
{
private
int
id
=
0
;
private
String
manager
=
null
;
private
String
location
=
null
;
private
List
<
Car
>
cars
=
null
;
...
// Setters and getters
}
// Car class
public
class
Car
{
private
int
id
;
private
String
name
=
null
;
private
String
color
=
null
;
...
// Setters and getters
}
The Showroom
and Car
classes are very simple POJOs. The only notable point is the declaration of a one-to-many association of Showroom
with cars—that is, one showroom consists of many cars.
Once the POJOs are ready, we add the mapping definitions:
<hibernate-mapping
package=
"com.madhusudhan.jh.collections.list"
>
<!-- Showroom class mapping definition -->
<class
name=
"Showroom"
table=
"SHOWROOM_LIST"
>
<id
column=
"SHOWROOM_ID"
name=
"id"
>
<generator
class=
"native"
/>
</id>
...<list
name=
"cars"
cascade=
"all"
table=
"CARS_LIST"
>
<key
column=
"SHOWROOM_ID"
/>
<index
column=
"CAR_INDEX"
/>
<one-to-many
class=
"Car"
/>
</list>
</class>
<!-- Car class mapping definition -->
<class
name=
"Car"
table=
"CARS_LIST"
>
<id
column=
"CAR_ID"
name=
"id"
>
<generator
class=
"native"
/>
</id>
<property
column=
"NAME"
name=
"name"
/>
<property
column=
"COLOR"
name=
"color"
/>
</class>
</hibernate-mapping>
Notice the use of the list
element in the preceding snippet. This element defines the mapping of the cars declared in the Showroom
object to the table.
The main table, SHOWROOM_LIST
, will be created and populated as expected, so there are no surprises there. However, CARS_LIST
is an additional table that gets created in the process. In addition to the CAR_ID
, NAME
, and COLOR
properties present in the CARS_LIST
table, which are directly declared on the object itself, Hibernate creates two other columns. One of them is a foreign key, SHOWROOM_ID
, while the other is the CAR_INDEX
column to hold the list indexes. CAR_INDEX
is populated with each list element’s position, and is used later to reconstruct the list elements in their original positions.
When retrieving the cars, at runtime, Hibernate reorders the records according to the index held in CAR_INDEX
. Let’s run the test client to check out how this works in practice.
Test Client for List Persistence
Fire up a small test client to test the list persistence functionality, as shown here:
private
void
persistLists
()
{
// Create showroom object
Showroom
showroom
=
new
Showroom
();
showroom
.
setLocation
(
"East Croydon, Greater London"
);
showroom
.
setManager
(
"Barry Larry"
);
// Create list of cars
List
<
Car
>
cars
=
new
ArrayList
<
Car
>();
cars
.
add
(
new
Car
(
"Toyota"
,
"Racing Green"
));
cars
.
add
(
new
Car
(
"Toyota"
,
"Racing Green"
));
cars
.
add
(
new
Car
(
"Nissan"
,
"White"
));
cars
.
add
(
new
Car
(
"BMW"
,
"Black"
));
cars
.
add
(
new
Car
(
"Mercedes"
,
"Silver"
));
...
// Associate cars to the showroom
showroom
.
setCars
(
cars
);
// Save the showroom
session
.
save
(
showroom
);
}
The test client is self-explanatory. Notice that I’m adding an extra Toyota to the list! When you run the test to retrieve the results, the following output is printed to the console (check out the duplicate Toyota cars too in the output!):
Showroom
{
id
=
6
,
manager
=
Barry
Larry
,
location
=
East
Croydon
,
Greater
London
,
cars
=[
Car
{
id
=
15
,
name
=
Toyota
,
color
=
Racing
Green
},
Car
{
id
=
16
,
name
=
Toyota
,
color
=
Racing
Green
},
Car
{
id
=
17
,
name
=
Nissan
,
color
=
White
},
Car
{
id
=
18
,
name
=
BMW
,
color
=
Black
},
Car
{
id
=
19
,
name
=
Mercedes
,
color
=
Silver
}]}
As you can see, Hibernate honors the insertion order of the cars in the list.
Persisting Sets
java.util.Set
represents an unordered data structure where duplicates are not allowed. Using sets is straightforward just like lists. We’ll revisit the showroom cars example from the previous example to demonstrate how sets are used with Hibernate.
In our modified example, the collection of cars that belong to a showroom is modeled as java.util.Set
; thus we define the cars
variable as the Set
type. We use HashSet
as our concrete implementation of the Set
interface.
The Showroom
class is shown here:
public
class
Showroom
{
private
int
id
=
0
;
private
String
manager
=
null
;
private
String
location
=
null
;
// Cars are represented as set
private
Set
<
Car
>
cars
=
null
;
// Getters and setters
...
The notable change is the use of the Set
collection instead of List
. Once you finish modifying the Showroom
class, the mapping of Set
is done via the set
tag, as demonstrated in the following snippet:
<hibernate-mapping
package=
"com.madhusudhan.jh.collections.set"
>
<!- The showroom class. Note the mapping of cars -->
<class
name=
"Showroom"
table=
"SHOWROOM_SET"
>
<id
column=
"SHOWROOM_ID"
name=
"id"
>
<generator
class=
"native"
/>
</id>
...<set
name=
"cars"
table=
"CARS_SET"
cascade=
"all"
>
<key
column=
"SHOWROOM_ID"
/>
<one-to-many
class=
"Car"
/>
</set>
</class>
<!-- The car mapping definition - very simple -->
<class
name=
"Car"
table=
"CARS_SET"
>
<id
column=
"CAR_ID"
name=
"id"
>
<generator
class=
"native"
/>
</id>
<property
column=
"NAME"
name=
"name"
/>
<property
column=
"COLOR"
name=
"color"
/>
</class>
</hibernate-mapping>
A Showroom
instance is mapped to SHOWROOM_SET
table, whereas the cars
variable representing the set collection is mapped to the CARS_SET
table, as expected. The key
element represents the presence of a foreign key in the CARS_SET
table. Hibernate adds this foreign key to the CARS_SET
table automatically. Hence, the CARS_SET
table, which is created and managed by Hibernate, will have the additional foreign key SHOWROOM_ID
, thus associating the two tables.
Create a test client as shown here:
private
void
persistSets
()
{
// Create and populate showroom
Showroom
showroom
=
new
Showroom
();
showroom
.
setLocation
(
"East Croydon, Greater London"
);
showroom
.
setManager
(
"Barry Larry"
);
// Create and populate cars set
Set
<
Car
>
cars
=
new
HashSet
<
Car
>();
cars
.
add
(
new
Car
(
"Toyota"
,
"Racing Green"
));
cars
.
add
(
new
Car
(
"Nissan"
,
"White"
));
cars
.
add
(
new
Car
(
"BMW"
,
"Black"
));
cars
.
add
(
new
Car
(
"BMW"
,
"Black"
));
// Associate cars to the showroom and persist it
showroom
.
setCars
(
cars
);
session
.
save
(
showroom
);
...
}
In the preceding example, we created a Showroom
object to which we’ve added three new cars. We are using HashSet
as our concrete implementation for our cars
collection. Did you notice that we are trying to add another BMW to the set? The set would identify these two cars as identical based on equality matching and hence would throw away the duplicate one.
When working with sets, we need to satisfy an equality requirement: we must create equals
and hashCode
methods in the Car
object. As we know, each individual item that’s being added to the set must be unique. The equals
and hashCode
methods would help to satisfy this requirement. Make sure the equals
and hashCode
contracts are fulfilled correctly—for example, use the fields that will identify a car uniquely.
The retrieveSets
test method would fetch the persisted set from the database, as shown in this listing:
Showroom
{
id
=
7
,
manager
=
Barry
Larry
,
location
=
East
Croydon
,
Greater
London
,
cars
=[
Car
{
id
=
27
,
name
=
Nissan
,
color
=
White
},
Car
{
id
=
26
,
name
=
Mercedes
,
color
=
Silver
},
Car
{
id
=
28
,
name
=
Toyota
,
color
=
Racing
Green
},
Car
{
id
=
29
,
name
=
BMW
,
color
=
Black
}]}
...
Did you notice that the BMW isn’t listed twice, although we added another to the set earlier? This demonstrates the set’s “exclusion of duplicates” policy.
Persisting Maps
When you have a requirement to represent name/value pairs, your first choice should be Map
s. The Map
data structures are like dictionaries where you have a key (word) and related values (meanings). Maps are the de facto choice for key/value-paired data, such as bank accounts (value) of a single customer (key) or stock quotes for an issuer.
Continuing with our car showroom example, next we’ll add the capability for the showroom to hold potential customers’ reservations for test driving cars. We can best implement this functionality by employing a Map
data structure, linking customers to car reservations:
public
class
Showroom
{
private
int
id
=
0
;
private
String
manager
=
null
;
private
String
location
=
null
;
private
Map
<
String
,
Car
>
cars
=
null
;
// getters & setters
...
Each car is reserved for a customer, and all the cars belong to the showroom. We can implement the customer-to-cars data type as a Map<String, Car>
type.
The main meat is in the mapping, which is defined here:
<hibernate-mapping
package=
"com.madhusudhan.jh.collections.map"
>
<!-- Showroom mapping definition with cars variable
mapped to CARS_MAP table using map tag -->
<class
name=
"Showroom"
table=
"SHOWROOM_MAP"
>
<id
column=
"SHOWROOM_ID"
name=
"id"
>
<generator
class=
"native"
/>
</id>
<property
column=
"MANAGER"
name=
"manager"
/>
...<map
name=
"cars"
cascade=
"all"
table=
"CARS_MAP"
>
<key
column=
"SHOWROOM_ID"
/>
<map-key
column=
"CUST_NAME"
type=
"string"
/>
<one-to-many
class=
"Car"
/>
</map>
</class>
<!-- Simple Car class-table mapping -->
<class
name=
"Car"
table=
"CARS_MAP"
>
<id
column=
"CAR_ID"
name=
"id"
>
<generator
class=
"native"
/>
</id>
<property
name=
"name"
column=
"CAR_NAME"
/>
<property
name=
"color"
column=
"COLOR"
/>
</class>
</hibernate-mapping>
As expected, the showroom’s cars
variable is represented by a map
element referring to a table, CARS_MAP
, in the mapping definition. The map
element defines a foreign key (SHOWROOM_ID
, in this case). The map-key
attribute defines the key of the map—the customer, in our case. The car class mapping is a simple and straightforward one. Note that Hibernate would add a couple of more columns to the CARS_MAP
table—SHOWROOM_ID
and CUST_NAME
—in addition to the name and color
columns.
Once the mapping is done, we need to demonstrate its workings using a test client as follows:
private
void
persistMaps
()
{
Showroom
showroom
=
new
Showroom
();
showroom
.
setLocation
(
"East Croydon, Greater London"
);
showroom
.
setManager
(
"Cherry Flurry"
);
Map
<
String
,
Car
>
cars
=
new
HashMap
<
String
,
Car
>();
cars
.
put
(
"barry"
,
new
Car
(
"Toyota"
,
"Racing Green"
));
cars
.
put
(
"larry"
,
new
Car
(
"Nissan"
,
"White"
));
cars
.
put
(
"harry"
,
new
Car
(
"BMW"
,
"Black"
));
showroom
.
setCars
(
cars
);
...
}
Here we create a map with a customer name and the cars to test drive. We then attach them to the showroom. As you can see in our Map
data structure, we have a brand new car corresponding to a customer.
As expected, the following output would be printed to the console if we run the retrieveMaps
method on the client:
Showroom
{
id
=
1
,
manager
=
Cherry
Flurry
,
location
=
East
Croydon
,
Greater
London
,
cars
={
barry
=
Car
{
id
=
1
,
name
=
Toyota
,
color
=
Racing
Green
},
harry
=
Car
{
id
=
2
,
name
=
BMW
,
color
=
Black
},
larry
=
Car
{
id
=
3
,
name
=
Nissan
,
color
=
White
},
fairy
=
Car
{
id
=
4
,
name
=
Mercedes
,
color
=
Pink
}}}
...
The output shows all the cars in the showroom are reserved to customers for a test drive.
Persisting Arrays
Persisting arrays is similar to persisting lists, so we will only breeze through the mechanics and will not delve into too much detail here. The Showroom
class now has a variable, cars
, of the String
array type, as listed here:
public
class
Showroom
{
private
int
id
=
0
;
private
String
manager
=
null
;
private
String
location
=
null
;
// List of cars
private
String
[]
cars
=
null
;
...
The mapping of the classes is defined here:
<hibernate-mapping
package=
"com.madhusudhan.jh.collections.array"
>
<class
name=
"Showroom"
table=
"SHOWROOM_ARRAY"
>
<id
column=
"SHOWROOM_ID"
name=
"id"
>
<generator
class=
"native"
/>
</id>
...<array
name=
"cars"
cascade=
"all"
table=
"CARS_ARRAY"
>
<key
column=
"SHOWROOM_ID"
/>
<index
column=
"CAR_INDEX"
/>
<element
column=
"CAR_NAME"
type=
"string"
not-null=
"true"
/>
</array>
</class>
</hibernate-mapping>
The array
tag defines the mapping between the cars
variable and the CARS_ARRAY
table. As expected, the CARS_ARRAY
table will have a foreign key (SHOWROOM_ID
, in this case). Hibernate also preserves the insertion order; hence, index
tag must be defined with a name. element
defines the actual values composing the array—in this case, the model name of each car.
The persistArrays
method on our test class (shown next) would persist the relevant arrays in the database. We create the String[]
of cars, passing in the model names, as shown in the following snippet:
private
void
persistArrays
()
{
...
Showroom
showroom
=
new
Showroom
();
showroom
.
setLocation
(
"East Croydon, Greater London"
);
showroom
.
setManager
(
"Barry Larry"
);
// Create array of cars and
// associate with the showroom
String
[]
cars
=
new
String
[]{
"Toyota"
,
"BMW"
,
"Citroen"
};
showroom
.
setCars
(
cars
);
// Normal saving of the showroom
session
.
save
(
showroom
);
...
}
The retrieveArrays
method on the test client would fetch the cars as expected:
Showroom
{
id
=
9
,
manager
=
Barry
Larry
,
location
=
East
Croydon
,
Greater
London
,
cars
=[
Toyota
,
BMW
,
Citroen
]}
Persisting Bags and IdBags
If we wish to have an unordered collection and no indexing of the elements, Java doesn’t have any data structure that supports that. The closest is java.util.List
, but obviously it maintains both order and indexing. To satisfy this requirement, Hibernate created a special type of collection called bags.
Bags are the opposite of lists: they are unordered and nonindexed collections that allow duplicate elements. Bags are unique to Hibernate, and there is no equivalent collection in the Java space.
Implementing bags is very simple; we don’t notice any difference to our entities. In fact, we could still be using List
to represent the bag in the Java code (remember, there is no bags collection in Java). The actual difference appears in the mapping side. Instead of declaring the collection as a list
, we use bag
.
See the following mapping definition, with no changes to entities:
<
hibernate
-
mapping
package
=
"com.madhusudhan.jh.collections.bags"
>
<
class
name
=
"Showroom"
table
=
"SHOWROOM_BAGS"
>
<
id
column
=
"SHOWROOM_ID"
name
=
"id"
>
<
generator
class
=
"native"
/>
</
id
>
...
<
bag
name
=
"cars"
cascade
=
"all"
table
=
"CARS_LIST"
>
<
key
column
=
"SHOWROOM_ID"
/>
<
one
-
to
-
many
class
=
"Car"
/>
</
bag
>
</
class
>
<
class
name
=
"Car"
table
=
"CARS_BAGS"
>
...
</
class
>
</
hibernate
-
mapping
>
In the bag
element, did you notice we dropped the index element that must exist in the list
definition? In bags, the index of the collection is not persisted anymore; hence, you won’t see the index
element defined in the mapping.
Apart from this difference, the rest of the mechanics for running the tests is exactly the same as with lists. The test class defined here shows how we populate the data before persisting the entity:
private
void
persist
()
{
...
Showroom
showroom
=
new
Showroom
();
showroom
.
setLocation
(
"East Croydon, Greater London"
);
showroom
.
setManager
(
"Barry Larry"
);
// Define our cars - note their type
String
[]
cars
=
new
String
[]{
"Toyota"
,
"BMW"
,
"Citroen"
};
// Attach them to the showroom and persist
showroom
.
setCars
(
cars
);
session
.
save
(
showroom
);
...
}
Warning
Bags are not a standard collection; they are Hibernate-specific. Although your code still uses java.util.List
for a bag, the mapping needs to be explicit. It’s better to stay away from bags if possible, and choose standard collections wherever you can.
In addition to bags, Hibernate supports idbags, a collection that provides a mechanism to have a surrogate key on the persisted collection itself, unlike bags where no key exists. As usual, the POJOs will not be changed, but the mapping deserves special attention:
<hibernate-mapping
package=
"com.madhusudhan.jh.collections.idbags"
>
<class
name=
"Showroom"
table=
"SHOWROOM_IDBAGS"
>
<id
column=
"SHOWROOM_ID"
name=
"id"
>
<generator
class=
"native"
/>
</id>
...<idbag
name=
"cars"
cascade=
"all"
table=
"SHOWROOM_CARS_IDBAGS"
>
<collection-id
column=
"SHOWROOM_CAR_ID"
type=
"long"
>
<generator
class=
"hilo"
/>
</collection-id>
<key
column=
"SHOWROOM_ID"
/>
<many-to-many
class=
"Car"
column=
"CAR_ID"
/>
</idbag>
</class>
<class
name=
"Car"
table=
"CARS_IDBAGS"
>
<id
column=
"CAR_ID"
name=
"id"
>
<generator
class=
"native"
/>
</id>
...</class>
</hibernate-mapping>
Here we introduce the idbags
element to represent our cars
collection, pointing to a join table, SHOWROOM_CARS_IDBAGS
. The collection-id
element creates a primary key on the join table. In addition to its own primary key, the join table will also carry primary keys from the other two tables.
Warning
The idbags collection is rarely used, so I would suggest you revisit your requirements should you wish to use it.
Persisting Collections Using Annotations
In the previous sections, we have seen the inner workings of saving collections using the XML mapping route. As an alternative, we can follow the annotations path for persisting the collections. The first thing we need to do is decorate the entities with the appropriate annotations. We’ll enhance the car showroom example in this section.
There are two methods of preparing our code for annotations: using a foreign key or using an intermediary join table. We’ll cover both of them next.
Using a Foreign Key
As we know, each showroom will have many cars, as represented by a one-to-many association. The Showroom
entity consists of the collection of cars, showcasing them to customers. The cars, on the other hand, belong to a showcase; hence, are modeled to have a foreign key relationship to the showroom.
Let’s first see the Showroom
entity, which is defined as follows:
@Entity
(
name
=
"SHOWROOM_LIST_ANN"
)
@Table
(
name
=
"SHOWROM_LIST_ANN"
)
public
class
Showroom
{
@Id
@Column
(
name
=
"SHOWROOM_ID"
)
@GeneratedValue
(
strategy
=
GenerationType
.
AUTO
)
private
int
id
=
0
;
@OneToMany
@JoinColumn
(
name
=
"SHOWROOM_ID"
)
@Cascade
(
CascadeType
.
ALL
)
private
List
<
Car
>
cars
=
null
;
// other properties
private
String
manager
=
null
;
private
String
location
=
null
;
The class is declared as a persistable entity (via the @Entity
annotation) mapping to a table identified with the @Table
annotation. We define the identifier using an autogeneration strategy, meaning the identifier is set by one of the database’s functions, such as auto_increment
or identity
.
Let’s focus on one important property of the showroom: the collection of cars represented by a variable called cars
. We use a java.util.List
collection to hold the cars data. This variable is decorated with the @OneToMany
annotation because each showroom will have many cars, and each car belongs to a showroom.
We learned earlier that the cars
collection will have its own table with a foreign key referring to the showroom table’s primary key (SHOWROOM_ID
, in this case).
To let Hibernate know about this dependency, we declare the cars
variable along with an @JoinColumn
annotation defining the foreign key. We must provide the column name SHOWROOM_ID
to pick up the list of cars from the cars
table. The @Cascade
annotation enables Hibernate to persist the collections associated with the main instance.
The Car
entity is simple and straightforward:
@Entity
(
name
=
"CAR_LIST_ANN"
)
@Table
(
name
=
"CAR_LIST_ANN"
)
public
class
Car
{
@Id
@GeneratedValue
(
strategy
=
GenerationType
.
AUTO
)
@Column
(
name
=
"CAR_ID"
)
private
int
id
;
private
String
name
=
null
;
private
String
color
=
null
;
...
}
Here are the showroom and car database table scripts:
-- Table for showroom. CREATE TABLE showroom_list_ann ( SHOWROOM_ID int(10) NOT NULL AUTO_INCREMENT, location varchar(255) DEFAULT NULL, manager varchar(255) DEFAULT NULL, PRIMARY KEY (SHOWROOM_ID) ) -- Table for cars CREATE TABLE car_list_ann ( CAR_ID int(11) NOT NULL AUTO_INCREMENT, color varchar(255) DEFAULT NULL, name varchar(255) DEFAULT NULL, SHOWROOM_ID int(11) DEFAULT NULL, PRIMARY KEY (CAR_ID), FOREIGN KEY (SHOWROOM_ID) REFERENCES showroom_list_ann (SHOWROOM_ID) )
The cars
table has its own primary key (CAR_ID
) plus a foreign key (SHOWROOM_ID
) referring to the main table.
Prepare your test case, adding the annotated classes to the configuration, as shown here:
Configuration
config
=
new
Configuration
()
.
configure
(
"collections/list/ann/hibernate.cfg.xml"
)
.
addAnnotatedClass
(
Showroom
.
class
)
.
addAnnotatedClass
(
Car
.
class
);
As the client’s persist mechanism won’t be any different from what we saw when persisting lists, we will not repeat the test case here.
The output of the showroom is as follows:
Showroom
{
id
=
1
,
manager
=
Barry
Larry
,
location
=
East
Croydon
,
Greater
London
,
cars
=[
Car
{
id
=
1
,
name
=
Toyota
,
color
=
Racing
Green
},
Car
{
id
=
2
,
name
=
Nissan
,
color
=
White
},
Car
{
id
=
3
,
name
=
BMW
,
color
=
Black
},
Car
{
id
=
4
,
name
=
Mercedes
,
color
=
Silver
}]}
Now that we have seen how to persist collections using a foreign key, let’s explore the second method: using a join table.
Using a Join Table
When using a join table strategy, we must have a mapping (join) table, which is an intermediary table holding the primary keys from both tables. For example, the following join table consists of primary keys from both showroom
and cars
:
// Join Table for showrooms and cars
SHOWROOM_ID
CAR_ID
1
1
1
2
2
1
2
2
Make the following alterations to the Showroom
entity, annotating the cars
table as appropriate:
@Entity
(
name
=
"SHOWROOM_SET_ANN_JOINTABLE"
)
@Table
(
name
=
"SHOWROOM_SET_ANN_JOINTABLE"
)
public
class
Showroom
{
@Id
@Column
(
name
=
"SHOWROOM_ID"
)
@GeneratedValue
(
strategy
=
GenerationType
.
AUTO
)
private
int
id
=
0
;
private
String
manager
=
null
;
private
String
location
=
null
;
@OneToMany
@JoinTable
(
name
=
"SHOWROOM_CAR_SET_ANN_JOINTABLE"
,
joinColumns
=
@JoinColumn
(
name
=
"SHOWROOM_ID"
)
)
@Cascade
(
CascadeType
.
ALL
)
private
Set
<
Car
>
cars
=
null
;
...
}
The @JoinTable
annotation in the preceding snippet indicates that we will be using an intermediary table (SHOWROOM_CAR_SET_ANN_JOINTABLE
, in this case). Also, note that cars are fetched using the SHOWROOM_ID
join column.
The join table that is created is as follows:
CREATE TABLE showroom_car_set_ann_jointable ( SHOWROOM_ID int(11) NOT NULL, car_id int(11) NOT NULL, PRIMARY KEY (SHOWROOM_ID,car_id), FOREIGN KEY (SHOWROOM_ID) REFERENCES showroom_set_ann_jointable (SHOWROOM_ID), FOREIGN KEY (car_id) REFERENCES car_set_ann_jointable (id) )
As expected, the primary key is the combination of showroom_id
and car_id
. Also, notice that we have defined the foreign keys from the individual tables for showroom and car.
Summary
In this chapter, we have run through the various parts of persisting collections using the Hibernate framework. We have walked through the entities and their mappings in detail. We worked through examples of lists, sets, array lists, bags, and maps. We wrapped up by exploring how to persist the collections using annotations.
Get Just Hibernate now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.