Advanced entity association mappings (Hibernate)

 
When we use the word associations, we always refer to relationships between entities. In the previous topic, we demonstrated a unidirectional many-to-one association, made it bidirectional, and finally turned it into a parent/children relationship (one-to-many and many-to-one with cascading options).
One reason we discuss more advanced entity mappings in a separate topic is that quite a few of them are considered rare, or at least optional.
It’s absolutely possible to only use component mappings and many-to-one (occasionally one-to-one) entity associations. You can write a sophisticated application without ever mapping a collection! Of course, efficient and easy access to persistent data, by iterating a collection for example, is one of the reasons why you use full object/relational mapping and not a simple JDBC query service. However, some exotic mapping features should be used with care and even avoided most of the time.
We’ll point out recommended and optional mapping techniques in this topic, as we show you how to map entity associations with all kinds of multiplicity, with and without collections.

Single-valued entity associations

Let’s start with one-to-one entity associations.
The relationships between User and Address (the user has a billingAddress, homeAddress, and shippingAddress) are best represented with a <component> mapping. This is usually the simplest way to represent one-to-one relationships, because the lifecycle is almost always dependent in such a case, it’s either an aggregation or a composition in UML.
But what if you want a dedicated table for Address, and you map both User and Address as entities? One benefit of this model is the possibility for shared ref-erences—another entity class (let’s say Shipment) can also have a reference to a particular Address instance. If a User has a reference to this instance, as their shippingAddress, the Address instance has to support shared references and needs its own identity.
In this case, User and Address classes have a true one-to-one association. Look at the revised class diagram in figure 7.1.
The first change is a mapping of the Address class as a stand-alone entity:
tmp9B196_thumbtmp9B197_thumb
We assume you won’t have any difficulty creating the same mapping with annotations or changing the Java class to an entity, with an identifier property—this is the only change you have to make.
Now let’s create the association mappings from other entities to that class. There are several choices, the first being a primary key one-to-one association.
tmp9B-198_thumb
Figure 7.1 Address as an entity with two associations referencing the same instance


Shared primary key associations

Rows in two tables related by a primary key association share the same primary key values. The main difficulty with this approach is ensuring that associated instances are assigned the same primary key value when the objects are saved. Before we try to solve this problem, let’s see how you map the primary key association.

Mapping a primary key association with XML

The XML mapping element that maps an entity association to a shared primary key entity is <one-to-one>. First you need a new property in the User class:
tmp9B199_thumb
Next, map the association in User.hbm.xml:
tmp9B200_thumb
You add a cascading option that is natural for this model: If a User instance is made persistent, you usually also want its shippingAddress to become persistent. Hence, the following code is all that is needed to save both objects:
tmp9B201_thumb
Hibernate inserts a row into the USERS table and a row into the ADDRESS table. But wait, this doesn’t work! How can Hibernate possibly know that the record in the ADDRESS table needs to get the same primary key value as the USERS row? At the beginning of this section, we intentionally didn’t show you any primary-key generator in the mapping of Address.
You need to enable a special identifier generator.

The foreign identifier generator

If an Address instance is saved, it needs to get the primary key value of a User object. You can’t enable a regular identifier generator, let’s say a database sequence. The special foreign identifier generator for Address has to know where to get the right primary key value.
The first step to create this identifier binding between Address and User is a bidirectional association. Add a new user property to the Address entity:
tmp9B202_thumb
Map the new user property of an Address in Address.hbm.xml:
tmp9B203_thumb
This mapping not only makes the association bidirectional, but also, with con-strained=”true”, adds a foreign key constraint linking the primary key of the ADDRESS table to the primary key of the USERS table. In other words, the database guarantees that an ADDRESS row’s primary key references a valid USERS primary key. (As a side effect, Hibernate can now also enable lazy loading of users when a shipping address is loaded. The foreign key constraint means that a user has to exist for a particular shipping address, so a proxy can be enabled without hitting the database. Without this constraint, Hibernate has to hit the database to find out if there is a user for the address; the proxy would then be redundant. We’ll come back to this in later topics.)
You can now use the special foreign identifier generator for Address objects:
tmp9B204_thumb
This mapping seems strange at first. Read it as follows: When an Address is saved, the primary key value is taken from the user property. The user property is a reference to a User object; hence, the primary key value that is inserted is the same as the primary key value of that instance. Look at the table structure in figure 7.2.
The USERS and ADDRESS tables have the same primary keys.
Figure 7.2
The USERS and ADDRESS tables have the same primary keys.
The code to save both objects now has to consider the bidirectional relationship and it finally works:
tmp9B206_thumb
Let’s do the same with annotations.

Shared primary key with annotations

JPA supports one-to-one entity associations with the @OneToOne annotation. To map the association of shippingAddress in the User class as a shared primary key association, you also need the @PrimaryKeyJoinColumn annotation:
tmp9B207_thumb
This is all that is needed to create a unidirectional one-to-one association on a shared primary key. Note that you need @PrimaryKeyJoinColumns (plural) instead if you map with composite primary keys. In a JPA XML descriptor, a one-to-one mapping looks like this:
tmp9B208_thumb
The JPA specification doesn’t include a standardized method to deal with the problem of shared primary key generation, which means you’re responsible for setting the identifier value of an Address instance correctly before you save it (to the identifier value of the linked User instance). Hibernate has an extension annotation for custom identifier generators which you can use with the Address entity (just like in XML):
tmp9B209_thumb
Shared primary key one-to-one associations aren’t uncommon but are relatively rare. In many schemas, a to-one association is represented with a foreign key field and a unique constraint.

One-to-one foreign key associations

Instead of sharing a primary key, two rows can have a foreign key relationship. One table has a foreign key column that references the primary key of the associated table. (The source and target of this foreign key constraint can even be the same table: This is called a self-referencing relationship.)
The mapping element in XML for this association is <many-to-one>—not <one-to-one>, as you might have expected. The reason is simple: You don’t care what’s on the target side of the association, so you can treat it like a to-one association without the many part. All you want is to express “This entity has a property that is a reference to an instance of another entity” and use a foreign key field to represent that relationship. The database schema for this mapping is shown in figure 7.3.
A one-to-one foreign key association between USERS and ADDRESS
Figure 7.3 A one-to-one foreign key association between USERS and ADDRESS
An additional constraint enforces this relationship as a real one to one. By making the SHIPPING_ADDRESS_ID column unique, you declare that a particular address can be referenced by at most one user, as a shipping address. This isn’t as strong as the guarantee from a shared primary key association, which allows a particular address to be referenced by at most one user, period. With several foreign key columns (let’s say you also have unique HOME_ADDRESS_ID and BILLING_ADDRESS_ID), you can reference the same address target row several times. But in any case, two users can’t share the same address for the same purpose.
Let’s make the association from User to Address bidirectional.

Inverse property reference

The last foreign key association was mapped from User to Address with <many-to-one> and a unique constraint to guarantee the desired multiplicity. What mapping element can you add on the Address side to make this association bidirectional, so that access from Address to User is possible in the Java domain model?
Let’s change the mapping from a User to an Address. Instead of the shared primary key, you now add a SHIPPING_ADDRESS_ID column in the USERS table:
tmp9B211_thumb
In XML, you create a <one-to-one> mapping with a property reference attribute:
tmp9B212_thumb
You tell Hibernate that the user property of the Address class is the inverse of a property on the other side of the association. You can now call anAd-dress.getUser() to access the user who’s shipping address you’ve given. There is no additional column or foreign key constraint; Hibernate manages this pointer for you.
Should you make this association bidirectional? As always, the decision is up to you and depends on whether you need to navigate through your objects in that direction in your application code. In this case, we’d probably conclude that the bidirectional association doesn’t make much sense. If you call anAd-dress.getUser(), you are saying “give me the user who has this address has its shipping address,” not a very reasonable request. We recommend that a foreign key-based one-to-one association, with a unique constraint on the foreign key column—is almost always best represented without a mapping on the other side.
Let’s repeat the same mapping with annotations.

Mapping a foreign key with annotations

The JPA mapping annotations also support a one-to-one relationship between entities based on a foreign key column. The main difference compared to the mappings earlier in this topic is the use of @JoinColumn instead of @Prima-ryKeyJoinColumn.
First, here’s the to-one mapping from User to Address with the unique constraint on the SHIPPING_ADDRESS_ID foreign key column. However, instead of a @ManyToOne annotation, this requires a @OneToOne annotation:
tmp9B213_thumb
Hibernate will now enforce the multiplicity with the unique constraint. If you want to make this association bidirectional, you need another @OneToOne mapping in the Address class:
tmp9B214_thumb
The effect of the mappedBy attribute is the same as the property-ref in XML mapping: a simple inverse declaration of an association, naming a property on the target entity side.
The equivalent mapping in JPA XML descriptors is as follows:
tmp9B215_thumb
You’ve now completed two basic single-ended association mappings: the first with a shared primary key, the second with a foreign key reference. The last option we want to discuss is a bit more exotic: mapping a one-to-one association with the help of an additional table.

Mapping with a join table

Let’s take a break from the complex CaveatEmptor model and consider a different scenario. Imagine you have to model a data schema that represents an office allocation plan in a company. Common entities include people working at desks. It seems reasonable that a desk may be vacant and have no person assigned to it. On the other hand, an employee may work at home, with the same result. You’re dealing with an optional one-to-one association between Person and Desk.
If you apply the mapping techniques we discussed in the previous sections, you may come to the following conclusions: Person and Desk are mapped to two tables, with one of them (let’s say the PERSON table) having a foreign key column that references the other table (such as ASSIGNED_DESK_ID) with an additional unique constraint (so two people can’t be assigned the same desk). The relationship is optional if the foreign key column is nullable.
On second thought, you realize that the assignment between persons and desks calls for another table that represents ASSIGNMENT. In the current design, this table has only two columns: PERSON_ID and DESK_ID. The multiplicity of these foreign key columns is enforced with a unique constraint on both—a particular person and desk can only be assigned once, and only one such an assignment can exist.
It also seems likely that one day you’ll need to extend this schema and add columns to the ASSIGNMENT table, such as the date when a person was assigned to a desk. As long as this isn’t the case, however, you can use object/relational mapping to hide the intermediate table and create a one-to-one Java entity association between only two classes. (This situation changes completely once additional columns are introduced to ASSIGNMENT.)
Where does such an optional one-to-one relationship exist in CaveatEmptor?

The CaveatEmptor use case

Let’s consider the Shipment entity in CaveatEmptor again and discuss its purpose. Sellers and buyers interact in CaveatEmptor by starting and bidding on auctions. The shipment of the goods seems to be outside the scope of the application; the seller and the buyer agree on a method of shipment and payment after the auction ends. They can do this offline, outside of CaveatEmptor. On the other hand, you could offer an extra escrow service in CaveatEmptor. Sellers would use this service to create a trackable shipment once the auction completed. The buyer would pay the price of the auction item to a trustee (you), and you’d inform the seller that the money was available. Once the shipment arrived and the buyer accepted it, you’d transfer the money to the seller.
If you’ve ever participated in an online auction of significant value, you’ve probably used such an escrow service. But you want more service in CaveatEmp-tor. Not only will you provide trust services for completed auctions, but you’ll also allow users to create a trackable and trusted shipment for any deal they make outside an auction, outside CaveatEmptor.
This scenario calls for a Shipment entity with an optional one-to-one association to an Item. Look at the class diagram for this domain model in figure 7.4.
A shipment has an optional link with a single auction item.
Figure 7.4 A shipment has an optional link with a single auction item.
In the database schema, you add an intermediate link table called ITEM_SHIPMENT. A row in this table represents a Shipment made in the context of an auction. The tables are shown in figure 7.5.
You now map two classes to three tables: first in XML, and then with annotations.

Mapping a join table in XML

The property that represents the association from Shipment to Item is called auction:
tmp9B217_thumb
Because you have to map this association with a foreign key column, you need the <many-to-one> mapping element in XML. However, the foreign key column isn’t in the SHIPMENT table, it’s in the ITEM_SHIPMENT join table. With the help of the <join> mapping element, you move it there.
An optional one-to-many relationship mapped to a join table
Figure 7.5 An optional one-to-many relationship mapped to a join table
tmp9B219_thumb
The join table has two foreign key columns: SHIPMENT_ID, referencing the primary key of the SHIPMENT table; and ITEM_ID, referencing the ITEM table. The ITEM_ID column is unique; a particular item can be assigned to exactly one shipment. Because the primary key of the join table is SHIPMENT_ID, which makes this column also unique, you have a guaranteed one-to-one multiplicity between Shipment and Item.
By setting optional=”true” on the <join> mapping, you tell Hibernate that it should insert a row into the join table only if the properties grouped by this mapping are non-null. But if a row needs to be inserted (because you called aShipment.setAuction(anItem)), the NOT NULL constraint on the ITEM_ID column applies.
You could map this association bidirectional, with the same technique on the other side. However, optional one-to-one associations are unidirectional most of the time.
JPA also supports association join tables as secondary tables for an entity. Mapping secondary join tables with annotations
You can map an optional one-to-one association to an intermediate join table with annotations:
tmp9B220_thumb
You don’t have to specify the SHIPMENT_ID column because it’s automatically considered to be the join column; it’s the primary key column of the SHIPMENT table.
Alternatively, you can map properties of a JPA entity to more than one table, as demonstrated in “Moving properties into a secondary table”  First, you need to declare the secondary table for the entity:
tmp9B221_thumb
Note that the @SecondaryTable annotation also supports attributes to declare the foreign-key column name—the equivalent of the <key column=”…”/> you saw earlier in XML and the joinColumn(s) in a @JoinTable. If you don’t specify it, the primary-key column name of the entity is used—in this case, again SHIPMENT_ID.
The auction property mapping is a @OneToOne; and as before, the foreign key column referencing the ITEM table is moved to the intermediate secondary table:
tmp9B222_thumb
The table for the target @JoinColumn is named explicitly. Why would you use this approach instead of the (simpler) @JoinTable strategy? Declaring a secondary table for an entity is useful if not only one property (the many-to-one in this case) but several properties must be moved into the secondary table. We don’t have a great example with Shipment and Item, but if your ITEM_SHIPMENT table would have additional columns, mapping these columns to properties of the Shipment entity might be useful.
This completes our discussion of one-to-one association mappings. To summarize, use a shared primary key association if one of the two entities seems more important and can act as the primary key source. Use a foreign key association in all other cases, and a hidden intermediate join table when your one-to-one association is optional.
We now focus on many-valued entity associations, including more options for one-to-many, and finally, many-to-many mappings.

Many-valued entity associations

A many-valued entity association is by definition a collection of entity references. You mapped one of these in the previous topic, section 6.4, “Mapping a parent/children relationship.” A parent entity instance has a collection of references to many child objects—hence, one-to-many.
One-to-many associations are the most important kind of entity association that involves a collection. We go so far as to discourage the use of more exotic association styles when a simple bidirectional many-to-one/one-to-many will do the job. A many-to-many association may always be represented as two many-to-one associations to an intervening class. This model is usually more easily extensible, so we tend not to use many-to-many associations in applications. Also remember that you don’t have to map any collection of entities, if you don’t want to; you can always write an explicit query instead of direct access through iteration.
If you decide to map collections of entity references, there are a few options and more complex situations that we discuss now, including a many-to-many relationship.

One-to-many associations

The parent/children relationship you mapped earlier was a bidirectional association, with a <one-to-many> and a <many-to-one> mapping. The many end of this association was implemented in Java with a Set; you had a collection of bids in the Item class.
Let’s reconsider this mapping and focus on some special cases. Considering bags
It’s possible to use a <bag> mapping instead of a set for a bidirectional one-to-many association. Why would you do this?
Bags have the most efficient performance characteristics of all the collections you can use for a bidirectional one-to-many entity association (in other words, if the collection side is inverse=”true”). By default, collections in Hibernate are loaded only when they’re accessed for the first time in the application. Because a bag doesn’t have to maintain the index of its elements (like a list) or check for duplicate elements (like a set), you can add new elements to the bag without triggering the loading. This is an important feature if you’re going to map a possibly large collection of entity references. On the other hand, you can’t eager-fetch two collections of bag type simultaneously (for example, if bids and images of an Item were one-to-many bags). “Defining the global fetch plan.” In general we would say that a bag is the best inverse collection for a one-to-many association.
To map a bidirectional one-to-many association as a bag, you have to replace the type of the bids collection in the Item persistent class with a Collection and an ArrayList implementation. The mapping for the association between Item and Bid is left essentially unchanged:
tmp9B223_thumb
You rename the <set> element to <bag>, making no other changes. Even the tables are the same: The BID table has the ITEM_ID foreign key column. In JPA, all Collection and List properties are considered to have bag semantics, so the following is equivalent to the XML mapping:
tmp9B224_thumb
A bag also allows duplicate elements, which the set you mapped earlier didn’t. It turns out that this isn’t relevant in this case, because duplicate means you’ve added a particular reference to the same Bid instance several times. You wouldn’t do this in your application code. But even if you add the same reference several times to this collection, Hibernate ignores it—it’s mapped inverse.

Unidirectional and bidirectional lists

If you need a real list to hold the position of the elements in a collection, you have to store that position in an additional column. For the one-to-many mapping, this also means you should change the bids property in the Item class to List and initialize the variable with an ArrayList (or keep the Collection interface from the previous section, if you don’t want to expose this behavior to a client of the class).
The additional column that holds the position of a reference to a Bid instance is the BID_POSITION, in the mapping of Item:
tmp9B225_thumb
So far this seems straightforward; you’ve changed the collection mapping to <list> and added the <list-index> column BID_POSITION to the collection table (which in this case is the BID table). Verify this with the table shown in figure 7.6.
This mapping isn’t really complete. Consider the ITEM_ID foreign key column: It’s NOT NULL (a bid has to reference an item). The first problem is that you don’t specify this constraint in the mapping. Also, because this mapping is unidirectional (the collection is noninverse), you have to assume that there is no opposite side mapped to the same foreign key column (where this constraint could be declared). You need to add a not-null=”true” attribute to the <key> element of the collection mapping:
Storing the position of each bid in the list collection
Figure 7.6
Storing the position of each bid in the list collection
tmp9B227_thumb
Note that the attribute has to be on the <key> mapping, not on a possible nested <column> element. Whenever you have a noninverse collection of entity references (most of the time a one-to-many with a list, map, or array) and the foreign key join column in the target table is not nullable, you need to tell Hibernate about this. Hibernate needs the hint to order INSERT and UPDATE statements correctly, to avoid a constraint violation.
Let’s make this bidirectional with an item property of the Bid. If you follow the examples from earlier topics, you might want to add a <many-to-one> on the ITEM_ID foreign key column to make this association bidirectional, and enable inverse=”true” on the collection. Remember that Hibernate ignores the state of an inverse collection! This time, however, the collection contains information that is needed to update the database correctly: the position of its elements. If only the state of each Bid instance is considered for synchronization, and the collection is inverse and ignored, Hibernate has no value for the BID_POSITION column.
If you map a bidirectional one-to-many entity association with an indexed collection (this is also true for maps and arrays), you have to switch the inverse sides. You can’t make an indexed collection inverse=”true”. The collection becomes responsible for state synchronization, and the one side, the Bid, has to be made inverse. However, there is no inverse=”true” for a many-to-one mapping so you need to simulate this attribute on a <many-to-one>:
tmp9B228_thumb
Setting insert and update to false has the desired effect. As we discussed earlier, these two attributes used together make a property effectively read-only. This side of the association is therefore ignored for any write operations, and the state of the collection (including the index of the elements) is the relevant state when the in-memory state is synchronized with the database. You’ve switched the inverse/ noninverse sides of the association, a requirement if you switch from a set or bag to a list (or any other indexed collection).
The equivalent in JPA, an indexed collection in a bidirectional one-to-many mapping, is as follows:
tmp9B229_thumb
This mapping is noninverse because no mappedBy attribute is present. Because JPA doesn’t support persistent indexed lists (only ordered with an @OrderBy at load time), you need to add a Hibernate extension annotation for index support. Here’s the other side of the association in Bid:
tmp9B230_thumb
We now discuss one more scenario with a one-to-many relationship: an association mapped to an intermediate join table.

Optional one-to-many association with a join table

A useful addition to the Item class is a buyer property. You can then call anItem.getBuyer() to access the User who made the winning bid. (Of course, anItem.getSuccessfulBid().getBidder() can provide the same access with a
Items may be bought by users.
Figure 7.7
Items may be bought by users.
different path.) If made bidirectional, this association will also help to render a screen that shows all auctions a particular user has won: You call aUser.get-BoughtItems() instead of writing a query.
From the point of view of the User class, the association is one-to-many. The classes and their relationship are shown in figure 7.7.
Why is this association different than the one between Item and Bid? The multiplicity 0..* in UML indicates that the reference is optional. This doesn’t influence the Java domain model much, but it has consequences for the underlying tables. You expect a BUYER_ID foreign key column in the ITEM table. The column has to be nullable—a particular Item may not have been bought (as long as the auction is still running).
You can accept that the foreign key column can be NULL and apply additional constraints (“allowed to be NULL only if the auction end time hasn’t been reached or if no bid has been made”). We always try to avoid nullable columns in a relational database schema. Information that is unknown degrades the quality of the data you store. Tuples represent propositions that are true; you can’t assert something you don’t know. And, in practice, many developers and DBAs don’t create the right constraint and rely on (often buggy) application code to provide data integrity.
An optional entity association, be it one-to-one or one-to-many, is best represented in an SQL database with a join table. See figure 7.8 for an example schema.
You added a join table earlier in this topic, for a one-to-one association. To guarantee the multiplicity of one-to-one, you applied unique constraints on both foreign key columns of the join table. In the current case, you have a one-to-many multiplicity, so only the ITEM_ID column of the ITEM_BUYER table is unique. A particular item can be bought only once.
Let’s map this in XML. First, here’s the boughtItems collection of the User class.
An optional relationship with a join table avoids nullable foreign key columns.
Figure 7.8 An optional relationship with a join table avoids nullable foreign key columns.
tmp9B233_thumb
You use a Set as the collection type. The collection table is the join table, ITEM_BUYER; its primary key is a composite of USER_ID and ITEM_ID. The new mapping element you haven’t seen before is <many-to-many>; it’s required because the regular <one-to-many> doesn’t know anything about join tables. By forcing a unique constraint on the foreign key column that references the target entity table, you effectively force a one-to-many multiplicity.
You can map this association bidirectional with the buyer property of Item. Without the join table, you’d add a <many-to-one> with a BUYER_ID foreign key column in the ITEM table. With the join table, you have to move this foreign key column into the join table. This is possible with a <join> mapping:
tmp9B234_thumb
Two important details: First, the association is optional, and you tell Hibernate not to insert a row into the join table if the grouped properties (only one here, buyer) are null. Second, this is a bidirectional entity association. As always, one side has to be the inverse end. You’ve chosen the <join> to be inverse; Hibernate now uses the collection state to synchronize the database and ignores the state of
the Item.buyer property. As long as your collection is not an indexed variation (a list, map, or array), you can reverse this by declaring the collection inverse=”true”. The Java code to create a link between a bought item and a user object is the same in both cases:
At the time of writing, this mapping has the limitation that you can’t set it to optional=”true”; hence, the USER_ID column is nullable. If you try to add a nul-lable=”false” attribute on the @JoinColumn, Hibernate Annotations thinks that you want the whole buyer property to never be null. Furthermore, the primary key of the join table is now the ITEM_ID column only. This is fine, because you don’t want duplicate items in this table—they can be bought only once.
To make this mapping bidirectional, add a collection on the User class and make it inverse with mappedBy:
tmp9B235_thumb
We showed a <many-to-many> XML mapping element in the previous section for a one-to-many association on a join table. The @JoinTable annotation is the equivalent in annotations. Let’s map a real many-to-many association.

Many-to-many associations

The association between Category and Item is a many-to-many association, as can be seen in figure 7.9.
In a real system, you may not have a many-to-many association. Our experience is that there is almost always other information that must be attached to each link between associated instances (such as the date and time when an item was added
tmp9B236_thumb
You can map secondary tables in JPA to create a one-to-many association with a join table. First, map a @ManyToOne to a join table:
tmp9B237_thumb
A many-to-many valued association between Category and Item
Figure 7.9
A many-to-many valued association between Category and Item
to a category) and that the best way to represent this information is via an intermediate association class. In Hibernate, you can map the association class as an entity and map two one-to-many associations for either side. Perhaps more conveniently, you can also map a composite element class, a technique we show later. It’s the purpose of this section to implement a real many-to-many entity association. Let’s start with a unidirectional example.

A simple unidirectional many-to-many association

If you require only unidirectional navigation, the mapping is straightforward. Unidirectional many-to-many associations are essentially no more difficult than the collections of value-type instances we discussed earlier. For example, if the Category has a set of Items, you can create this mapping:
tmp9B239_thumb
The join table (or link table, as some developers call it) has two columns: the foreign keys of the CATEGORY and ITEM tables. The primary key is a composite of both columns. The full table structure is shown in figure 7.10.
In JPA annotations, many-to-many associations are mapped with the @ManyTo-Many attribute:
tmp9B240_thumbMany-to-many entity association mapped to an association table
Figure 7.10 Many-to-many entity association mapped to an association table
In Hibernate XML you can also switch to an <idbag> with a separate primary key column on the join table:
tmp9B242_thumb
As usual with an <idbag> mapping, the primary key is a surrogate key column, CATEGORY_ITEM_ID. Duplicate links are therefore allowed; the same Item can be added twice to a Category. (This doesn’t seem to be a useful feature.) With annotations, you can switch to an identifier bag with the Hibernate @CollectionId:
tmp9B243_thumb
A JPA XML descriptor for a regular many-to-many mapping with a set (you can’t use a Hibernate extension for identifier bags) looks like this:
tmp9B244_thumb
You may even switch to an indexed collection (a map or list) in a many-to-many association. The following example maps a list in Hibernate XML:
tmp9B245_thumb
The primary key of the link table is a composite of the CATEGORY_ID and DISPLAY_POSITION columns; this mapping guarantees that the position of each Item in a Category is persistent. Or, with annotations:
tmp9B246_thumb
As discussed earlier, JPA only supports ordered collections (with an optional @OrderBy annotation or ordered by primary key), so you again have to use a Hibernate extension for indexed collection support. If you don’t add an @Index-Column, the List is stored with bag semantics (no guaranteed persistent order of elements).
Creating a link between a Category and an Item is easy:
tmp9B247_thumb
Bidirectional many-to-many associations are slightly more difficult. A bidirectional many-to-many association
You know that one side in a bidirectional association has to be mapped as inverse because you have named the foreign key column(s) twice. The same principle applies to bidirectional many-to-many associations: Each row of the link table is represented by two collection elements, one element at each end of the association. An association between an Item and a Category is represented in memory by the Item instance in the items collection of the Category, but also by the Category instance in the categories collection of the Item.
Before we discuss the mapping of this bidirectional case, you have to be aware that the code to create the object association also changes:
tmp9B248_thumb
As always, a bidirectional association (no matter of what multiplicity) requires that you set both ends of the association.
When you map a bidirectional many-to-many association, you must declare one end of the association using inverse=”true” to define which side’s state is used to update the join table. You can choose which side should be inverse.
Recall this mapping of the items collection from the previous section:
tmp9B249_thumb
You may reuse this mapping for the Category end of the bidirectional association and map the other side as follows:
tmp9B250_thumb
Note the inverse=”true”. Again, this setting tells Hibernate to ignore changes made to the categories collection and that the other end of the association, the items collection, is the representation that should be synchronized with the database if you link instances in Java code.
You have enabled cascade=”save-update” for both ends of the collection. This isn’t unreasonable, we suppose. On the other hand, the cascading options all, delete, and delete-orphans aren’t meaningful for many-to-many associations. (This is good point to test if you understand entities and value types—try to come up with reasonable answers why these cascading options don’t make sense for a many-to-many association.)
In JPA and with annotations, making a many-to-many association bidirectional is easy. First, the noninverse side:
tmp9B251_thumb
Now the opposite inverse side:
tmp9B252_thumb
tmp9B253_thumb
In JPA, a bag is a collection without a persistent index:
tmp9B254_thumb
As you can see, you don’t have to repeat the join-table declaration on the inverse side.
What types of collections may be used for bidirectional many-to-many associations? Do you need the same type of collection at each end? It’s reasonable to map, for example, a <list> for the noninverse side of the association and a <bag> on the inverse side.
For the inverse end, <set> is acceptable, as is the following bag mapping:
No other mappings can be used for the inverse end of a many-to-many association. Indexed collections (lists and maps) don’t work, because Hibernate won’t initialize or maintain the index column if the collection is inverse. In other words, a many-to-many association can’t be mapped with indexed collections on both sides.
We already frowned at the use of many-to-many associations, because additional columns on the join table are almost always inevitable.

Adding columns to join tables

In this section, we discuss a question that is asked frequently by Hibernate users: What do I do if my join table has additional columns, not only two foreign key columns?
Imagine that you need to record some information each time you add an Item to a Category. For example, you may need to store the date and the name of the user who added the item to this category. This requires additional columns on the join table, as you can see in figure 7.11.
Additional columns on the join table in a many-to-many association
Figure 7.11
Additional columns on the join table in a many-to-many association
You can use two common strategies to map such a structure to Java classes. The first strategy requires an intermediate entity class for the join table and is mapped with one-to-many associations. The second strategy utilizes a collection of components, with a value-type class for the join table.

Mapping the join table to an intermediate entity

The first option we discuss now resolves the many-to-many relationship between Category and Item with an intermediate entity class, CategorizedItem. Listing 7.1 shows this entity class, which represents the join table in Java, including JPA annotations:
Listing 7.1 An entity class that represents a link table with additional columns
Listing 7.1 An entity class that represents a link table with additional columns tmp9B257_thumb
An entity class needs an identifier property. The primary key of the join table is CATEGORY_ID and ITEM_ID, a composite. Hence, the entity class also has a composite key, which you encapsulate in a static nested class for convenience. You can also see that constructing a CategorizedItem involves setting the values of the identifier—composite key values are assigned by the application. Pay extra attention to the constructor and how it sets the field values and guarantees referential integrity by managing collections on either side of the association.
Let’s map this class to the join table in XML:
tmp9B258_thumbtmp9B259_thumb
The entity class is mapped as immutable—you’ll never update any properties after creation. Hibernate accesses <composite-id> fields directly—you don’t need getters and setters in this nested class. The two <many-to-one> mappings are effectively read-only; insert and update are set to false. This is necessary because the columns are mapped twice, once in the composite key (which is responsible for insertion of the values) and again for the many-to-one associations.
The Category and Item entities (can) have a one-to-many association to the CategorizedItem entity, a collection. For example, in Category:
tmp9B260_thumb
And here’s the annotation equivalent:
tmp9B261_thumb
There is nothing special to consider here; it’s a regular bidirectional one-to-many association with an inverse collection. Add the same collection and mapping to Item to complete the association. This code creates and stores a link between a category and an item:
tmp9B262_thumb
The referential integrity of the Java objects is guaranteed by the constructor o CategorizedItem, which manages the collection in aCategory and in anItem Remove and delete the link between a category and an item:
tmp9B263_thumb
The primary advantage of this strategy is the possibility for bidirectional navigation: You can get all items in a category by calling aCategory.getCategor-izedItems() and the also navigate from the opposite direction with anItem.getCategorizedItems(). A disadvantage is the more complex code needed to manage the CategorizedItem entity instances to create and remove associations—they have to be saved and deleted independently, and you need some infrastructure in the CategorizedItem class, such as the composite identifier. However, you can enable transitive persistence with cascading options on the collections from Category and Item to CategorizedItem.
The second strategy for dealing with additional columns on the join table doesn’t need an intermediate entity class; it’s simpler.

Mapping the join table to a collection of components

First, simplify the CategorizedItem class, and make it a value type, without an identifier or any complex constructor:
tmp9B264_thumb
As for all value types, this class has to be owned by an entity. The owner is the Category, and it has a collection of these components:
tmp9B-265_thumb
This is the complete mapping for a many-to-many association with extra columns on the join table. The <many-to-one> element represents the association to Item; the <property> mappings cover the extra columns on the join table. There is only one change to the database tables: The CATEGORY_ITEM table now has a primary key that is a composite of all columns, not only CATEGORY_ID and ITEM_ID, as in the previous section. Hence, all properties should never be nullable—otherwise you can’t identify a row in the join table. Except for this change, the tables still look as shown in figure 7.11.
You can enhance this mapping with a reference to the User instead ofjust the user’s name. This requires an additional USER_ID column on the join table, with a foreign key to USERS. This is a ternary association mapping:
tmp9B266_thumb

This is a fairly exotic beast!

The advantage of a collection of components is clearly the implicit lifecycle of the link objects. To create an association between a Category and an Item, add a new CategorizedItem instance to the collection. To break the link, remove the element from the collection. No extra cascading settings are required, and the Java code is simplified:
tmp9B267_thumb
The downside of this approach is that there is no way to enable bidirectional navigation: A component (such as CategorizedItem) can’t, by definition, have shared references. You can’t navigate from Item to CategorizedItem. However, you can write a query to retrieve the objects you need.
Let’s do the same mapping with annotations. First, make the component class @Embeddable, and add the component column and association mappings:
tmp9B268_thumb
Now map this as a collection of components in the Category class:
tmp9B269_thumb
That’s it: You’ve mapped a ternary association with annotations. What looked incredibly complex at the beginning has been reduced to a few lines of annotation metadata, most of it optional.
The last collection mapping we’ll explore are Maps of entity references.

Mapping maps

You mapped a Java Map in the last topic—the keys and values of the Map were value types, simple strings. You can create more complex maps; not only can the keys be references to entities, but so can the values. The result can therefore be a ternary association.

Values as references to entities

First, let’s assume that only the value of each map entry is a reference to another entity. The key is a value type, a long. Imagine that the Item entity has a map of Bid instances and that each map entry is a pair of Bid identifier and reference to a Bid instance. If you iterate through anItem.getBidsByIdentifier(), you iterate through map entries that look like (1, <reference to Bid with PK 1>), (2, <reference to Bid with PK 2>), and so on.
The underlying tables for this mapping are nothing special; you again have an ITEM and a BID table, with an ITEM_ID foreign key column in the BID table. Your motivation here is a slightly different representation of the data in the application, with a Map.
In the Item class, include a Map:
tmp9B270_thumb
New here is the @MapKey element of JPA—it maps a property of the target entity as key of the map.The default if you omit the name attribute is the identifier property of the target entity (so the name here is redundant). Because the keys of a map form a set, values are expected to be unique for a particular map—this is the case for Bid primary keys but likely not for any other property of Bid.
In Hibernate XML, this mapping is as follows:
tmp9B271_thumb
The formula key for a map makes this column read-only, so it’s never updated when you modify the map. A more common situation is a map in the middle of a ternary association.

Ternary associations

You may be a little bored by now, but we promise this is the last time we’ll show another way to map the association between Category and Item. Let’s summarize what you already know about this many-to-many association:
■ It can be mapped with two collections on either side and a join table that has only two foreign key columns. This is a regular many-to-many association mapping.
■ It can be mapped with an intermediate entity class that represents the join table, and any additional columns therein. A one-to-many association is mapped on either side (Category and Item), and a bidirectional many-to-one equivalent is mapped in the intermediate entity class.
■ It can be mapped unidirectional, with a join table represented as a value type component. The Category entity has a collection of components. Each component has a reference to its owning Category and a many-to-one entity association to an Item. (You can also switch the words Category and Item in this explanation.)
You previously turned the last scenario into a ternary association by adding another many-to-one entity association to a User. Let’s do the same with a Map.
A Category has a Map of Item instances—the key of each map entry is a reference to an Item. The value of each map entry is the User who added the Item to the Category. This strategy is appropriate if there are no additional columns on the join table; see the schema in figure 7.12.
The advantage of this strategy is that you don’t need any intermediate class, no entity or value type, to represent the ADDED_BY_USER_ID column of the join table in your Java application.
First, here’s the Map property in Category with a Hibernate extension annotation.
A ternary association with a join table between three entities
Figure 7.12 A ternary association with a join table between three entities
To remove the link, remove the entry from the map. As an exercise, you can try to make this mapping bidirectional, with a collection of categories in Item.
tmp9B-273_thumb
The Hibernate XML mapping includes a new element, <map-key-many-to-many>:
tmp9B274_thumb
To create a link between all three entities, if all your instances are already in persistent state, add a new entry to the map:
tmp9B275_thumb
Remember that this has to be an inverse collection mapping, so it doesn’t support indexed collections.
Now that you know all the association mapping techniques for normal entities, we still have to consider inheritance and associations to the various levels of an inheritance hierarchy. What we really want is polymorphic behavior. Let’s see how Hibernate deals with polymorphic entity associations.

Polymorphic associations

Polymorphism is a defining feature of object-oriented languages like Java. Support for polymorphic associations and polymorphic queries is an absolutely basic feature of an ORM solution like Hibernate. Surprisingly, we’ve managed to get this far without needing to talk much about polymorphism. Even more surprisingly, there is not much to say on the topic—polymorphism is so easy to use in Hibernate that we don’t need to spend a lot of effort explaining it.
To get an overview, we first consider a many-to-one association to a class that may have subclasses. In this case, Hibernate guarantees that you can create links to any subclass instance just like you would to instances of the superclass.

Polymorphic many-to-one associations

A polymorphic association is an association that may refer instances of a subclass of the class that was explicitly specified in the mapping metadata. For this example, consider the defaultBillingDetails property of User. It references one particular BillingDetails object, which at runtime can be any concrete instance of that class. The classes are shown in figure 7.13.
You map this association to the abstract class BillingDetails as follows in User.hbm.xml.
A user has either a credit card or a bank account as the default.
Figure 7.13 A user has either a credit card or a bank account as the default.
tmp9B277_thumb
But because BillingDetails is abstract, the association must refer to an instanc of one of its subclasses—CreditCard or CheckingAccount—at runtime. You don’t have to do anything special to enable polymorphic associations in Hibernate; specify the name of any mapped persistent class in your association mapping (or let Hibernate discover it using reflection), and then, if that clas declares any <union-subclass>, <subclass>, or <joined-subclass> elements the association is naturally polymorphic.
The following code demonstrates the creation of an association to an instanc of the CreditCard subclass:
tmp9B278_thumb
Now, when you navigate the association in a second unit of work, Hibernate automatically retrieves the CreditCard instance:
tmp9B279_thumb
There is just one thing to watch out for: If BillingDetails was mapped with lazy=”true” (which is the default), Hibernate would proxy the defaultBilling-Details association target. In this case, you wouldn’t be able to perform a typecast to the concrete class CreditCard at runtime, and even the instanceof operator would behave strangely:
tmp9B280_thumb
In this code, the typecast fails because bd is a proxy instance. When a method is invoked on the proxy, the call is delegated to an instance of CreditCard that is fetched lazily (it’s an instance of a runtime-generated subclass, so instanceof also fails). Until this initialization occurs, Hibernate doesn’t know what the subtype of
After the call to load(), bd and cc refer to two different proxy instances, which both delegate to the same underlying CreditCard instance. However, the second proxy has a different interface, and you can call methods (like getExpiryDate()) that apply only to this interface.
Note that you can avoid these issues by avoiding lazy fetching, as in the following code, using an eager fetch query:
tmp9B281_thumb
Truly object-oriented code shouldn’t use instanceof or numerous typecasts. If you find yourself running into problems with proxies, you should question your design, asking whether there is a more polymorphic approach. Hibernate also offers bytecode instrumentation as an alternative to lazy loading through proxies.
One-to-one associations are handled the same way. What about many-valued associations—for example, the collection of billingDetails for each User?

Polymorphic collections

A User may have references to many BillingDetails, not only a single default (one of the many is the default). You map this with a bidirectional one-to-many association.
In BillingDetails, you have the following:
the given instance is—this would require a database hit, which you try to avoid with lazy loading in the first place. To perform a proxy-safe typecast, use load():
tmp9B282_thumb
tmp9B283_thumb
As usual, addBillingDetails() calls getBillingDetails().add(cc) and cc.set-User(this) to guarantee the integrity of the relationship by setting both pointers.
You may iterate over the collection and handle instances of CreditCard and CheckingAccount polymorphically (you probably don’t want to bill users severa times in the final system, though):
tmp9B284_thumb
In the examples so far, we assumed that BillingDetails is a class mapped explicitly and that the inheritance mapping strategy is table per class hierarchy, or normalized with table per subclass.
However, if the hierarchy is mapped with table per concrete class (implicit polymorphism) or explicitly with table per concrete class with union, this scenario requires a more sophisticated solution.

Polymorphic associations to unions

Hibernate supports the polymorphic many-to-one and one-to-many associations shown in the previous sections even if a class hierarchy is mapped with the table per concrete class strategy. You may wonder how this works, because you may not have a table for the superclass with this strategy; if so, you can’t reference or add a foreign key column to BILLING_DETAILS.
In the Users mapping you have:
tmp9B285_thumb
Adding a CreditCard is easy:
tmp9B286_thumb
Review our discussion of table per concrete class with union. “Table per concrete class with unions.” Pay extra attention to the polymorphic query Hibernate executes when retrieving instances of BillingDetails. Now, consider the following collection of BillingDetails mapped for User:
tmp9B287_thumb
If you want to enable the polymorphic union feature, a requirement for this poly morphic association is that it’s inverse; there must be a mapping on the opposite side. In the mapping of BillingDetails, with <union-subclass>, you have to include a <many-to-one> association:
tmp9B288_thumb
You have two tables for both concrete classes of the hierarchy. Each table has a foreign key column, USER_ID, referencing the USERS table. The schema is shown in figure 7.14.
Now, consider the following data-access code:
tmp9B289_thumb
Two concrete classes mapped to two separate tables
Figure 7.14 Two concrete classes mapped to two separate tables
The FROM-clause subselect is a union of all concrete class tables, and it includes the USER_ID foreign key values for all instances. The outer select now includes a restriction in the WHERE clause to all rows referencing a particular user.
This magic works great for retrieval of data. If you manipulate the collection and association, the noninverse side is used to update the USER_ID column(s) in the concrete table. In other words, the modification of the inverse collection has no effect: The value of the user property of a CreditCard or BankAccount instance is taken.
Now consider the many-to-one association defaultBillingDetails again, mapped with the DEFAULT_BILLING_DETAILS_ID column in the USERS table. Hibernate executes a UNION query that looks similar to the previous query to retrieve this instance, if you access the property. However, instead of a restriction in the WHERE clause to a particular user, the restriction is made on a particular BILLING_DETAILS_ID.
Important: Hibernate cannot and will not create a foreign key constraint for DEFAULT_BILLING_DETAILS_ID with this strategy. The target table of this reference can be any of the concrete tables, which can’t be constrained easily. You should consider writing a custom integrity rule for this column with a database trigger.
Hibernate executes a UNION query to retrieve all instances that are referenced in this collection:
tmp9B291_thumb
One problematic inheritance strategy remains: table per concrete class with implicit polymorphism.

Polymorphic table per concrete class

“Table per concrete class with implicit polymorphism,” we defined the table per concrete class mapping strategy and observed that this mapping strategy makes it difficult to represent a polymorphic association, because you can’t map a foreign key relationship to a table of the abstract superclass. There is no table for the superclass with this strategy; you have tables only for concrete classes. You also can’t create a UNION, because Hibernate doesn’t know what unifies the concrete classes; the superclass (or interface) isn’t mapped anywhere.
Hibernate doesn’t support a polymorphic billingDetails one-to-many collection in User, if this inheritance mapping strategy is applied on the BillingDetails hierarchy. If you need polymorphic many-to-one associations with this strategy, you’ll have to resort to a hack. The technique we’ll show you in this section should be your last choice. Try to switch to a <union-subclass> mapping first.
Suppose that you want to represent a polymorphic many-to-one association from User to BillingDetails, where the BillingDetails class hierarchy is mapped with a table per concrete class strategy and implicit polymorphic behavior in Hibernate. You have a CREDIT_CARD table and a BANK_ACCOUNT table, but no BILLING_DETAILS table. Hibernate needs two pieces of information in the USERS table to uniquely identify the associated default CreditCard or BankAccount:
■ The name of the table in which the associated instance resides
■ The identifier of the associated instance
The USERS table requires a DEFAULT_BILLING_DETAILS_TYPE column in addition to the DEFAULT_BILLING_DETAILS_ID. This extra column works as an additional discriminator and requires a Hibernate <any> mapping in User.hbm.xml:
tmp9B292_thumb
The meta-type attribute specifies the Hibernate type of the DEFAULT_BILLING_ DETAILS_TYPE column; the id-type attribute specifies the type of the DEFAULT_
BILLING_DETAILS_ID column (it’s necessary for CreditCard and BankAccount to have the same identifier type).
The <meta-value> elements tell Hibernate how to interpret the value of the DEFAULT_BILLING_DETAILS_TYPE column. You don’t need to use the full table name here—you can use any value you like as a type discriminator. For example, you can encode the information in two characters:
tmp9B293_thumb
An example of this table structure is shown in figure 7.15.
Here is the first major problem with this kind of association: You can’t add a foreign key constraint to the DEFAULT_BILLING_DETAILS_ID column, because some values refer to the BANK_ACCOUNT table and others to the CREDIT_CARD table. Thus, you need to come up with some other way to ensure integrity (a trigger, for example). This is the same issue you’d face with a <union-subclass> strategy.
Furthermore, it’s difficult to write SQL table joins for this association. In particular, the Hibernate query facilities don’t support this kind of association mapping, nor may this association be fetched using an outer join. We discourage the use of <any> associations for all but the most special cases. Also note that this mapping technique isn’t available with annotations or in Java Persistence (this mapping is so rare that nobody asked for annotation support so far).
Using a discriminator column with an any association
Figure 7.15 Using a discriminator column with an any association
As you can see, as long as you don’t plan to create an association to a class hierarchy mapped with implicit polymorphism, associations are straightforward; you don’t usually need to think about it. You may be surprised that we didn’t show any JPA or annotation example in the previous sections—the runtime behavior is the same, and you don’t need any extra mapping to get it.

Summary

In this topic, you learned how to map more complex entity associations. Many of the techniques we’ve shown are rarely needed and may be unnecessary if you can simplify the relationships between your classes. In particular, many-to-many entity associations are often best represented as two one-to-many associations to an intermediate entity class, or with a collection of components.
Table 7.1 shows a summary you can use to compare native Hibernate features and Java Persistence.
Table 7.1 Hibernate and JPA comparison chart

Hibernate Core Java Persistence and EJB 3.0
Hibernate supports key generation for shared primary key one-to-one association mappings. Standardized one-to-one mapping is supported. Automatic shared primary key generation is possible through a Hibernate extension.
Hibernate supports all entity association mappings across join tables. Standardized association mappings are available across secondary tables.
Hibernate supports mapping of lists with persistent indexes. Persistent indexes require a Hibernate extension annotation.
Hibernate supports fully polymorphic behavior. It provides extra support for any association mappings to an inheritance hierarchy mapped with implicit polymorphism. Fully polymorphic behavior is available, but there is no annotation support for any mappings.


In the next topic, we’ll focus on legacy database integration and how you can customize the SQL that Hibernate generates automatically for you. This topic is interesting not only if you have to work with legacy schemas, but also if you want to improve your new schema with custom DDL, for example.

Next post:

Previous post: