Mapping collections and entity associations (Hibernate)

 

Two important (and sometimes difficult to understand) topics didn’t appear in the previous topics: the mapping of collections, and the mapping of associations between entity classes.

Most developers new to Hibernate are dealing with collections and entity associations for the first time when they try to map a typical parent/child relationship. But instead of jumping right into the middle, we start this topic with basic collection mapping concepts and simple examples. After that, you’ll be prepared for the first collection in an entity association—although we’ll come back to more complicated entity association mappings in the next topic. To get the full picture, we recommend you read both topics.

Sets, bags, lists, and maps of value types

An object of value type has no database identity; it belongs to an entity instance, and its persistent state is embedded in the table row of the owning entity—at least, if an entity has a reference to a single instance of a valuetype. If an entity class has a collection of value types (or a collection of references to value-typed instances), you need an additional table, the so-called collection table.

Before you map collections of value types to collection tables, remember that value-typed classes don’t have identifiers or identifier properties. The lifespan of a value-type instance is bounded by the lifespan of the owning entity instance. A value type doesn’t support shared references.

Java has a rich collection API, so you can choose the collection interface and implementation that best fits your domain model design. Let’s walk through the most common collection mappings.

Suppose that sellers in CaveatEmptor are able to attach images to Items. An image is accessible only via the containing item; it doesn’t need to support associations from any other entity in your system. The application manages the collection of images through the Item class, adding and removing elements. An image object has no life outside of the collection; it’s dependent on an Item entity.

In this case, it isn’t unreasonable to model the image class as a value type. Next. you need to decide what collection to use.

Selecting a collection interface

The idiom for a collection property in the Java domain model is always the same:

tmp9B126_thumb

Use an interface to declare the type of the property, not an implementation. Pick a matching implementation, and initialize the collection right away; doing so avoids uninitialized collections (we don’t recommend initializing collections late, in constructors or setter methods).

If you work with JDK 5.0, you’ll likely code with the generic versions of the JDK collections. Note that this isn’t a requirement; you can also specify the contents of the collection explicitly in mapping metadata. Here’s a typical generic Set with a type parameter:

tmp9B127_thumb

Out of the box, Hibernate supports the most important JDK collection interfaces. In other words, it knows how to preserve the semantics of JDK collections, maps, and arrays in a persistent fashion. Each interface has a matching implementation supported by Hibernate, and it’s important that you use the right combination. Hibernate only wraps the collection object you’ve already initialized on declaration of the field (or sometimes replaces it, if it’s not the right one).

Without extending Hibernate, you can choose from the following collections:

■ A java.util.Set is mapped with a <set> element. Initialize the collection with a java.util.HashSet. The order of its elements isn’t preserved, and duplicate elements aren’t allowed. This is the most common persistent collection in a typical Hibernate application.

■ A java.util.SortedSet can be mapped with <set>, and the sort attribute can be set to either a comparator or natural ordering for in-memory sorting. Initialize the collection with a java.util.TreeSet instance.

■ A java.util.List can be mapped with <list>, preserving the position of each element with an additional index column in the collection table. Initialize with a java.util.ArrayList.

■ A java.util.Collection can be mapped with <bag> or <idbag>. Java doesn’t have a Bag interface or an implementation; however, java.util. Collection allows bag semantics (possible duplicates, no element order is preserved). Hibernate supports persistent bags (it uses lists internally but ignores the index of the elements). Use a java.util.ArrayList to initialize a bag collection.

■ A java.util.Map can be mapped with <map>, preserving key and value pairs. Use a java.util.HashMap to initialize a property.

■ A java.util.SortedMap can be mapped with <map> element, and the sort attribute can be set to either a comparator or natural ordering for in-mem-ory sorting. Initialize the collection with a java.util.TreeMap instance.

■ Arrays are supported by Hibernate with <primitive-array> (for Java primitive value types) and <array> (for everything else). However, they’re rarely used in domain models, because Hibernate can’t wrap array properties. You lose lazy loading without bytecode instrumentation, and optimized dirty checking, essential convenience and performance features for persistent collections.

The JPA standard doesn’t name all these options. The possible standard collection property types are Set, List, Collection, and Map. Arrays aren’t considered.

Furthermore, the JPA specification only specifies that collection properties hold references to entity objects. Collections of value types, such as simple String instances, aren’t standardized. However, the specification document already mentions that future versions of JPA will support collection elements of embeddable classes (in other words, value types). You’ll need vendor-specific support if you want to map collections of value types with annotations. Hibernate Annotations include that support, and we’d expect many other JPA vendors support the same.

If you want to map collection interfaces and implementations not directly supported by Hibernate, you need to tell Hibernate about the semantics of your custom collections. The extension point in Hibernate is called Persistent-Collection; usually you extend one of the existing PersistentSet, Persistent-Bag, or PersistentList classes. Custom persistent collections are not very easy to write and we don’t recommend doing this if you aren’t an experienced Hibernate user. An example can be found in the Hibernate test suite source code, as part of your Hibernate download package.

We now go through several scenarios, always implementing the collection of item images. You map it first in XML and then with Hibernate’s support for collection annotations. For now, assume that the image is stored somewhere on the file-system and that you keep just the filename in the database. How images are stored and loaded with this approach isn’t discussed; we focus on the mapping.  

Mapping a set

The simplest implementation is a Set of String image filenames. First, add a collection property to the Item class:

tmp9B128_thumb

The image filenames are stored in a table named ITEM_IMAGE, the collection table. From the point of view of the database, this table is a separate entity, a separate table, but Hibernate hides this for you. The <key> element declares the foreign key column in the collection table that references the primary key ITEM_ID of the owning entity. The <element> tag declares this collection as a collection of value type instances—in this case, of strings.

A set can’t contain duplicate elements, so the primary key of the ITEM_IMAGE collection table is a composite of both columns in the <set> declaration: ITEM_ID and FILENAME. You can see the schema in figure 6.1.

Table structure and example data for a collection of strings

Figure 6.1

Table structure and example data for a collection of strings

It doesn’t seem likely that you would allow the user to attach the same image more than once, but let’s suppose you did. What kind of mapping would be appropriate in that case?

Mapping an identifier bag

An unordered collection that permits duplicate elements is called a bag. Curiously, the Java Collections framework doesn’t include a bag implementation. However, the java.util.Collection interface has bag semantics, so you only need a matching implementation. You have two choices:

■ Write the collection property with the java.util.Collection interface, and, on declaration, initialize it with an ArrayList of the JDK. Map the collection in Hibernate with a standard <bag> or <idbag> element. Hibernate has a built-in PersistentBag that can deal with lists; however, consistent with the contract of a bag, it ignores the position of elements in the ArrayList. In other words, you get a persistent Collection.

■ Write the collection property with the java.util.List interface, and, on declaration, initialize it with an ArrayList of the JDK. Map it like the previous option, but expose a different collection interface in the domain model class. This approach works but isn’t recommended, because clients using this collection property may think the order of elements is always preserved, which isn’t the case if it’s mapped as a <bag> or <idbag>.

We recommend the first option. Change the type of images in the Item class from Set to Collection, and initialize it with an ArrayList:

tmp9B130_thumb

Note that the setter method accepts a Collection, which can be anything in the JDK collection interface hierarchy. However, Hibernate is smart enough to replace this when persisting the collection. (It also relies on an ArrayList internally, like you did in the declaration of the field.)

You also have to modify the collection table to permit duplicate FILENAMEs; the table needs a different primary key. An <idbag> mapping adds a surrogate key column to the collection table, much like the synthetic identifiers you use for entity classes:

tmp9B131_thumbA surrogate primary key allows duplicate bag elements.

Figure 6.2 A surrogate primary key allows duplicate bag elements.

In this case, the primary key is the generated ITEM_IMAGE_ID, as you can see in figure 6.2. Note that the native generator for primary keys isn’t supported for <idbag> mappings; you have to name a concrete strategy. This usually isn’t a problem, because real-world applications often use a customized identifier generator anyway.

Also note that the ITEM_IMAGE_ID column isn’t exposed to the application in any way. Hibernate manages it internally.

A more likely scenario is one in which you wish to preserve the order in which images are attached to the Item. There are a number of good ways to do this; one way is to use a real list, instead of a bag.

Mapping a list

tmp9B133_thumb

A <list> mapping requires the addition of an index column to the collection table. The index column defines the position of the element in the collection. Thus, Hibernate is able to preserve the ordering of the collection elements. Map the collection as a <list>:

tmp9B134_thumbtmp9B135_thumb

(There is also an index element in the XML DTD, for compatibility with Hibernate 2.x. The new list-index is recommended; it’s less confusing and does the same thing.)

The primary key of the collection table is a composite of ITEM_ID and POSITION. Notice that duplicate elements (FILENAME) are now allowed, which is consistent with the semantics of a list, see figure 6.3.

Mapping a map

Again, make a small change to the Java class:

tmp9B136_thumb

Mapping a <map> (pardon us) is similar to mapping a list.

The collection table preserves the position of each element.

Figure 6.3 The collection table preserves the position of each element.

The index of the persistent list starts at zero. You could change this, for example, with <list-index base=”1″…/> in your mapping. Note that Hibernate adds null elements to your Java list if the index numbers in the database aren’t continuous.

Alternatively, you could map a Java array instead of a list. Hibernate supports this; an array mapping is virtually identical to the previous example, except with different element and attribute names (<array> and <array-index>). However, for reasons explained earlier, Hibernate applications rarely use arrays.

Now, suppose that the images for an item have user-supplied names in addition to the filename. One way to model this in Java is a map, with names as keys and filenames as values of the map.

Tables for a map, using strings as indexes and elements

Figure 6.4 Tables for a map, using strings as indexes and elements

tmp9B139_thumb

The primary key of the collection table is a composite of ITEM_ID and IMAGENAME. The IMAGENAME column holds the keys of the map. Again, duplicate elements are allowed; see figure 6.4 for a graphical view of the tables.

This map is unordered. What if you want to always sort your map by the name of the image?

Sorted and ordered collections

In a startling abuse of the English language, the words sorted and ordered mean different things when it comes to Hibernate persistent collections. A sorted collection is sorted in memory using a Java comparator. An ordered collection is ordered at the database level using an SQL query with an order by clause.

Let’s make the map of images a sorted map. First, you need to change the initialization of the Java property to a java.util.TreeMap and switch to the java.util.SortedMap interface:

tmp9B140_thumb

Hibernate handles this collection accordingly, if you map it as sorted:

tmp9B141_thumb

By specifying sort=”natural”, you tell Hibernate to use a SortedMap and to sort the image names according to the compareTo() method of java.lang.String. If you need some other sort algorithm (for example, reverse alphabetical order), you may specify the name of a class that implements java.util.Comparator in the sort attribute. For example:

tmp9B142_thumb

A java.util.SortedSet (with a java.util.TreeSet implementation) is mapped like this:

tmp9B143_thumb

Bags may not be sorted (there is no TreeBag, unfortunately), nor may lists; the order of list elements is defined by the list index.

Alternatively, instead of switching to the Sorted* interfaces and the (Tree* implementations), you may want to work with a linked map and to sort elements on the database side, not in memory. Keep the Map/HashMap declaration in the Java class, and create the following mapping:

tmp9B144_thumbtmp9B145_thumb

The expression in the order-by attribute is a fragment of an SQL order by clause. In this case, Hibernate orders the collection elements by the IMAGENAME column in ascending order during loading of the collection. You can even include an SQL function call in the order-by attribute:

tmp9B146_thumb

You can order by any column of the collection table. Internally, Hibernate uses a LinkedHashMap, a variation of a map that preserves the insertion order of key elements. In other words, the order that Hibernate uses to add the elements to the collection, during loading of the collection, is the iteration order you see in your application. The same can be done with a set: Hibernate internally uses a LinkedHashSet. In your Java class, the property is a regular Set/HashSet, but Hibernate’s internal wrapping with a LinkedHashSet is again enabled with the order-by attribute:

tmp9B147_thumb

You can also let Hibernate order the elements of a bag for you during collection loading. Your Java collection property is either Collection/ArrayList or List/ ArrayList. Internally, Hibernate uses an ArrayList to implement a bag that preserves insertion-iteration order:

tmp9B148_thumbtmp9B149_thumb

The linked collections Hibernate uses internally for sets and maps are available only in JDK 1.4 or later; older JDKs don’t come with a LinkedHashMap and LinkedHashSet. Ordered bags are available in all JDK versions; internally, an ArrayList is used.

In a real system, it’s likely that you’ll need to keep more than just the image name and filename. You’ll probably need to create an Image class for this extra information. This is the perfect use case for a collection of components.

Collections of components

You could map Image as an entity class and create a one-to-many relationship from Item to Image. However, this isn’t necessary, because Image can be modeled as a value type: Instances of this class have a dependent lifecycle, don’t need their own identity, and don’t have to support shared references.

As a value type, the Image class defines the properties name, filename, sizeX, and sizeY. It has a single association with its owner, the Item entity class, as shown in figure 6.5.

As you can see from the composition association style (the black diamond), Image is a component of Item, and Item is the entity that is responsible for the lifecycle of Image instances. The multiplicity of the association further declares this association as many-valued—that is, many (or zero) Image instances for the same Item instance.

Let’s walk through the implementation of this in Java and through a mapping in XML.

Collection of Image components in Item

Figure 6.5

Collection of Image components in Item

Writing the component class

First, implement the Image class as a regular POJO.  You must implement equals() (and hashCode()) and compare the name, filename, sizeX, and sizeY properties. Hibernate relies on this equality routine to check instances for modifications. A custom implementation of equals() and hashCode() isn’t required for all component classes (we would have mentioned this earlier). However, we recommend it for any component class because the implementation is straightforward, and “better safe than sorry” is a good motto.

The Item class may have a Set of images, with no duplicates allowed. Let’s map this to the database.

Mapping the collection

Collections of components are mapped similarly to collections of JDK value type. The only difference is the use of <composite-element> instead of an <element> tag. An ordered set of images (internally, a LinkedHashSet) can be mapped like this:

tmp9B151_thumb

The tables with example data are shown in figure 6.6.

This is a set, so the primary key of the collection table is a composite of the key column and all element columns: ITEM_ID, IMAGENAME, FILENAME, SIZEX, and SIZEY. Because these columns all appear in the primary key, you needed to declare them with not-null=”true” (or make sure they’re NOT NULL in any existing schema). No column in a composite primary key can be nullable—you can’t identify what you don’t know. This is probably a disadvantage of this particular mapping. Before you improve this (as you may guess, with an identifier bag), let’s enable bidirectional navigation.

Example data tables for a collection of components mapping

Figure 6.6

Example data tables for a collection of components mapping

Enabling bidirectional navigation

The association from Item to Image is unidirectional. You can navigate to the images by accessing the collection through an Item instance and iterating: anItem.getImages().iterator(). This is the only way you can get these image objects; no other entity holds a reference to them (value type again).

On the other hand, navigating from an image back to an item doesn’t make much sense. However, it may be convenient to access a back pointer like anIm-age.getItem() in some cases. Hibernate can fill in this property for you if you add a <parent> element to the mapping:

tmp9B153_thumb

True bidirectional navigation is impossible, however. You can’t retrieve an Image independently and then navigate back to its parent Item. This is an important issue: You can load Image instances by querying for them. But these Image objects won’t have a reference to their owner (the property is null) when you query in HQL or with a Criteria. They’re retrieved as scalar values.

Finally, declaring all properties as not-null is something you may not want. You need a different primary key for the IMAGE collection table, if any of the property columns are nullable.

Avoiding not-null columns

Analogous to the additional surrogate identifier property an <idbag> offers, a surrogate key column would come in handy now. As a side effect, an <idset> would also allow duplicates—a clear conflict with the notion of a set. For this and other reasons (including the fact that nobody ever asked for this feature), Hibernate doesn’t offer an <idset> or any surrogate identifier collection other than an <idbag>. Hence, you need to change the Java property to a Collection with bag semantics:

tmp9B154_thumb

This collection now also allows duplicate Image elements—it’s the responsibility of your user interface, or any other application code, to avoid these duplicate elements if you require set semantics. The mapping adds the surrogate identifier column to the collection table:

tmp9B155_thumb

The primary key of the collection table is now the ITEM_IMAGE_ID column, and it isn’t important that you implement equals() and hashCode() on the Image class (at least, Hibernate doesn’t require it). Nor do you have to declare the properties with not-null=”true”. They may be nullable, as can be seen in figure 6.7.

Collection of Image components using a bag with surrogate key

Figure 6.7 Collection of Image components using a bag with surrogate key

We should point out that there isn’t a great deal of difference between this bag mapping and a standard parent/child entity relationship like the one you map later in this topic. The tables are identical. The choice is mainly a matter of taste. A parent/child relationship supports shared references to the child entity and true bidirectional navigation. The price you’d pay is more complex lifecycles of objects. Value-typed instances can be created and associated with the persistent Item by adding a new element to the collection. They can be disassociated and permanently deleted by removing an element from the collection. If Image would be an entity class that supports shared references, you’d need more code in your application for the same operations, as you’ll see later.

Another way to switch to a different primary key is a map. You can remove the name property from the Image class and use the image name as the key of a map:

tmp9B157_thumb

The primary key of the collection table is now a composite of ITEM_ID and IMAGE-NAME.

A composite element class like Image isn’t limited to simple properties of basic type like filename. It may contain other components, mapped with <nested-composite-element>, and even <many-to-one> associations to entities. It can’t own collections, however. A composite element with a many-to-one association is useful, and we come back to this kind of mapping in the next topic.

This wraps up our discussion of basic collection mappings in XML. As we mentioned at the beginning of this section, mapping collections of value types with annotations is different compared with mappings in XML; at the time of writing, it isn’t part of the Java Persistence standard but is available in Hibernate.

Mapping collections with annotations

The Hibernate Annotations package supports nonstandard annotations for the mapping of collections that contain value-typed elements, mainly org.hiber-nate.annotations.CollectionOfElements. Let’s walk through some of the most common scenarios again.  

Basic collection mapping

The following maps a simple collection of String elements:

tmp9B158_thumb

The collection table ITEM_IMAGE has two columns; together, they form the composite primary key. Hibernate can automatically detect the type of the element if you use generic collections. If you don’t code with generic collections, you need to specify the element type with the targetElement attribute—in the previous example it’s therefore optional.

To map a persistent List, add @org.hibernate.annotations.IndexColumn with an optional base for the index (default is zero):

tmp9B159_thumbtmp9B160_thumb

If you forget the index column, this list would be treated as a bag collection, equivalent to a <bag> in XML.

For collections of value types, you’d usually use <idbag> to get a surrogate primary key on the collection table. A <bag> of value typed elements doesn’t really work; duplicates would be allowed at the Java level, but not in the database. On the other hand, pure bags are great for one-to-many entity associations.

To map a persistent map, use @org.hibernate.annotations.MapKey:

tmp9B161_thumb

If you forget the map key, the keys of this map would be automatically mapped to the column MAPKEY.

If the keys of the map are not simple strings but of an embeddable class, you can specify multiple map key columns that hold the individual properties of the embeddable component. Note that @org.hibernate.annotations.MapKey is a more powerful replacement for @javax.persistence.

Sorted and ordered collections

A collection can also be sorted or ordered with Hibernate annotations:

tmp9B162_thumb

(Note that without the @JoinColumn and/or @Column, Hibernate applies the usual naming conventions and defaults for the schema.) The @Sort annotation supports various SortType attributes, with the same semantics as the XML mapping options. The shown mapping uses a java.util.SortedSet (with a java. util.TreeSet implementation) and natural sort order. If you enable SortType. COMPARATOR, you also need to set the comparator attribute to a class that implements your comparison routine. Maps can also be sorted; however, as in XML mappings, there is no sorted Java bag or a sorted list (which has a persistent ordering of elements, by definition).

Maps, sets, and even bags, can be ordered on load, by the database, through an SQL fragment in the ORDER BY clause:

tmp9B163_thumb

The clause attribute of the Hibernate-specific @OrderBy annotation is an SQL fragment that is passed on directly to the database; it can even contain function calls or any other native SQL keyword. See our explanation earlier for details about the internal implementation of sorting and ordering; the annotations are equivalent to the XML mappings.

Mapping a collection of embedded objects

Finally, you can map a collection of components, of user-defined value-typed elements. Let’s assume that you want to map the same Image component class you’ve seen earlier in this topic, with image names, sizes, and so on.

You need to add the @Embeddable component annotation on that class to enable embedding:

tmp9B164_thumbtmp9B165_thumb

Note that you again map a back pointer with a Hibernate annotation; anIm-age.getItem() can be useful. You can leave out this property if you don’t need this reference. Because the collection table needs all the component columns as the composite primary key, it’s important that you map these columns as NOT NULL. You can now embed this component in a collection mapping and even override column definitions (in the following example you override the name of a single column of the component collection table; all others are named with the default strategy):

tmp9B166_thumb

To avoid the non-nullable component columns you need a surrogate primary key on the collection table, like <idbag> provides in XML mappings. With annotations, use the @CollectionId Hibernate extension:

tmp9B167_thumb

You’ve now mapped all the basic and some more complex collections with XML mapping metadata, and annotations. Switching focus, we now consider collections with elements that aren’t value types, but references to other entity instances. Many Hibernate users try to map a typical parent/children entity relationship, which involves a collection of entity references.

Mapping a parent/children relationship

From our experience with the Hibernate user community, we know that the first thing many developers try to do when they begin using Hibernate is a mapping of a parent/children relationship. This is usually the first time you encounter collections. It’s also the first time you have to think about the differences between entities and value types, or get lost in the complexity of ORM.

Managing the associations between classes and the relationships between tables is at the heart of ORM. Most of the difficult problems involved in implementing an ORM solution relate to association management.

You mapped relationships between classes of value type in the previous section and earlier in the topic, with varying multiplicity of the relationship ends. You map a one multiplicity with a simple <property> or as a <component>. The many association multiplicity requires a collection of value types, with <element> or <composite-element> mappings.

Now you want to map one- and many-valued relationships between entity classes. Clearly, entity aspects such as shared references and independent lifecycle complicate this relationship mapping. We’ll approach these issues step by step; and, in case you aren’t familiar with the term multiplicity, we’ll also discuss that.

The relationship we show in the following sections is always the same, between the Item and Bid entity classes, as can be seen in figure 6.8.

Memorize this class diagram. But first, there’s something we need to explain up front.

If you’ve used EJB CMP 2.0, you’re familiar with the concept of a managed association (or managed relationship). CMP associations are called container managed relationships (CMRs) for a reason. Associations in CMP are inherently bidirectional. A change made to one side of an association is instantly reflected at the other side. For example, if you call aBid.setItem(anItem), the container automatically calls anItem.getBids().add(aBid).

Relationship between Item and Bid

Figure 6.8

Relationship between Item and Bid

POJO-oriented persistence engines such as Hibernate don’t implement managed associations, and POJO standards such as EJB 3.0 and Java Persistence don’t require managed associations. Contrary to EJB 2.0 CMR, Hibernate and JPA associations are all inherently unidirectional. As far as Hibernate is concerned, the association from Bid to Item is a different association than the association from Item to Bid! This is a good thing—otherwise your entity classes wouldn’t be usable outside of a runtime container (CMR was a major reason why EJB 2.1 entities were considered problematic).

Because associations are so important, you need a precise language for classifying them.

Multiplicity

In describing and classifying associations, we’ll almost always use the term multiplicity. In our example, the multiplicity is just two bits of information:

■ Can there be more than one Bid for a particular Item?

■ Can there be more than one Item for a particular Bid?

After glancing at the domain model (see figure 6.8), you can conclude that the association from Bid to Item is a many-to-one association. Recalling that associations are directional, you classify the inverse association from Item to Bid as a one-to-many association.

There are only two more possibilities: many-to-many and one-to-one. We’ll get back to these in the next topic.

In the context of object persistence, we aren’t interested in whether many means two or a maximum of five or unrestricted. And we’re only barely interested in optionality of most associations; we don’t especially care whether an associated instance is required or if the other end in an association can be NULL (meaning zero-to-many and to-zero association) However, these are important aspects in your relational data schema that influence your choice of integrity rules and the constraints you define in SQL DDL.  

The simplest possible association

The association from Bid to Item (and vice versa) is an example of the simplest possible kind of entity association. You have two properties in two classes. One is a collection of references, and the other a single reference.

First, here’s the Java class implementation of Bid:

This mapping is called a unidirectional many-to-one association. (Actually, because it’s unidirectional, you don’t know what is on the other side, and you could just as well call this mapping a unidirectional to-one association mapping.) The column ITEM_ID in the BID table is a foreign key to the primary key of the ITEM table.

You name the class Item, which is the target of this association, explicitly. This is usually optional, because Hibernate can determine the target type with reflection on the Java property.

You added the not-null attribute because you can’t have a bid without an item—a constraint is generated in the SQL DDL to reflect this. The foreign key column ITEM_ID in the BID can never be NULL, the association is not to-zero-or-one. The table structure for this association mapping is shown in figure 6.9.

tmp9B169_thumb

Next, this is the Hibernate mapping for this association:

tmp9B170_thumb

Table relationships and keys for a one-to-many mapping

Figure 6.9

Table relationships and keys for a one-to-many mapping

In JPA, you map this association with the @ManyToOne annotation, either on the field or getter method, depending on the access strategy for the entity (determined by the position of the @Id annotation):

tmp9B172_thumb

There are two optional elements in this mapping. First, you don’t have to include the targetEntity of the association; it’s implicit from the type of the field. An explicit targetEntity attribute is useful in more complex domain models—for example, when you map a @ManyToOne on a getter method that returns a delegate class, which mimics a particular target entity interface.

The second optional element is the @JoinColumn. If you don’t declare the name of the foreign key column, Hibernate automatically uses a combination of the target entity name and the database identifier property name of the target entity. In other words, if you don’t add a @JoinColumn annotation, the default name for the foreign key column is item plus id, separated with an underscore. However, because you want to make the foreign key column NOT NULL, you need the annotation anyway to set nullable = false. If you generate the schema with the Hibernate Tools, the optional=”false” attribute on the @ManyToOne would also result in a NOT NULL constraint on the generated column.

This was easy. It’s critically important to realize that you can write a complete application without using anything else. (Well, maybe a shared primary key one-to-one mapping from time to time, as shown in the next topic.) You don’t need to map the other side of this class association, and you’ve already mapped everything present in the SQL schema (the foreign key column). If you need the Item instance for which a particular Bid was made, call aBid.getItem(), utilizing the entity association you created. On the other hand, if you need all bids that have been made for an item, you can write a query (in whatever language Hibernate supports).

One of the reasons you use a full object/relational mapping tool like Hibernate is, of course, that you don’t want to write that query.

Making the association bidirectional

You want to be able to easily fetch all the bids for a particular item without an explicit query, by navigating and iterating through the network of persistent objects. The most convenient way to do this is with a collection property on Item: anItem.getBids().iterator(). (Note that there are other good reasons to map a collection of entity references, but not many. Always try to think of these kinds of collection mappings as a feature, not a requirement. If it gets too difficult, don’t do it.)

You now map a collection of entity references by making the relationship between Item and Bid bidirectional.

First add the property and scaffolding code to the Item class:

tmp9B173_thumb

You can think of the code in addBid() (a convenience method) as implementing a managed association in the object model!

A basic mapping for this one-to-many association looks like this:

tmp9B174_thumbtmp9B175_thumb

If you compare this with the collection mappings earlier in this topic, you see that you map the content of the collection with a different element, <one-to-many>. This indicates that the collection contains not value type instances, but references to entity instances. Hibernate now knows how to treat shared references and the lifecycle of the associated objects (it disables all the implicit dependent lifecycle of value type instances). Hibernate also knows that the table used for the collection is the same table the target entity class is mapped to—the <set> mapping needs no table attribute.

The column mapping defined by the <key> element is the foreign key column ITEM_ID of the BID table, the same column you already mapped on the other side of the relationship.

Note that the table schema didn’t change; it’s the same as it was before you mapped the many side of the association. There is, however, one difference: The not null=”true” attribute is missing. The problem is that you now have two different unidirectional associations mapped to the same foreign key column. What side controls that column?

At runtime, there are two different in-memory representations of the same foreign key value: the item property of Bid and an element of the bids collection held by an Item. Suppose the application modifies the association, by, for example, adding a bid to an item in this fragment of the addBid() method:

bid.setItem(item);

bids.add(bid);

This code is fine, but in this situation, Hibernate detects two changes to the in-memory persistent instances. From the point of view of the database, only one value has to be updated to reflect these changes: the ITEM_ID column of the BID table.

Hibernate doesn’t transparently detect the fact that the two changes refer to the same database column, because at this point you’ve done nothing to indicate that this is a bidirectional association. In other words, you’ve mapped the same column twice (it doesn’t matter that you did this in two mapping files), and Hibernate always needs to know about this because it can’t detect this duplicate automatically (there is no reasonable default way it could be handled).

You need one more thing in the association mapping to make this a real bidirectional association mapping. The inverse attribute tells Hibernate that the collection is a mirror image of the <many-to-one> association on the other side:

tmp9B176_thumb

Without the inverse attribute, Hibernate tries to execute two different SQL statements, both updating the same foreign key column, when you manipulate the link between two instances. By specifying inverse=”true”, you explicitly tell Hibernate which end of the link it should not synchronize with the database. In this example, you tell Hibernate that it should propagate changes made at the Bid end of the association to the database, ignoring changes made only to the bids collection.

If you only call anItem.getBids().add(bid), no changes are made persistent! You get what you want only if the other side, aBid.setItem(anItem), is set correctly. This is consistent with the behavior in Java without Hibernate: If an association is bidirectional, you have to create the link with pointers on two sides, not just one. It’s the primary reason why we recommend convenience methods such as addBid() —they take care of the bidirectional references in a system without container-managed relationships.

Note that an inverse side of an association mapping is always ignored for the generation of SQL DDL by the Hibernate schema export tools. In this case, the ITEM_ID foreign key column in the BID table gets a NOT NULL constraint, because you’ve declared it as such in the noninverse <many-to-one> mapping.

(Can you switch the inverse side? The <many-to-one> element doesn’t have an inverse attribute, but you can map it with update=”false” and insert=”false” to effectively ignore it for any UPDATE or INSERT statements. The collection side is then noninverse and considered for insertion or updating of the foreign key column. We’ll do this in the next topic.)

The mappedBy attribute is the equivalent of the inverse attribute in XML mappings; however, it has to name the inverse property of the target entity. Note that you don’t specify the foreign key column again here (it’s mapped by the other side), so this isn’t as verbose as the XML.

You now have a working bidirectional many-to-one association (which could also be called a bidirectional one-to-many association). One final option is missing if you want to make it a true parent/children relationship.

Cascading object state

The notion of a parent and a child implies that one takes care of the other. In practice, this means you need fewer lines of code to manage a relationship between a parent and a child, because some things can be taken care of automatically. Let’s explore the options.

Let’s map this inverse collection side again, with JPA annotations:

tmp9B177_thumb

The following code creates a new Item (which we consider the parent) and a new Bid instance (the child):

tmp9B178_thumb

The second call to session.save() seems redundant, if we’re talking about a true parent/children relationship. Hold that thought, and think about entities and value types again: If both classes are entities, their instances have a completely independent lifecycle. New objects are transient and have to be made persistent if you want to store them in the database. Their relationship doesn’t influence their lifecycle, if they’re entities. If Bid would be a value type, the state of a Bid instance is the same as the state of its owning entity. In this case, however, Bid is a separate entity with its own completely independent state. You have three choices:

■ Take care of the independent instances yourself, and execute additional save() and delete() calls on the Bid objects when needed—in addition to the Java code needed to manage the relationship (adding and removing references from collections, and so on).

■ Make the Bid class a value type (a component). You can map the collection with a <composite-element> and get the implicit lifecycle. However, you lose other aspects of an entity, such as possible shared references to an instance.

■ Do you need shared references to Bid objects? Currently, a particular Bid instance isn’t referenced by more than one Item. However, imagine that a User entity also has a collection of bids, made by the user. To support shared references, you have to map Bid as an entity. Another reason you need shared references is the successfulBid association from Item in the full CaveatEmptor model. In this case, Hibernate offers transitive persistence, a feature you can enable to save lines of code and to let Hibernate manage the lifecycle of associated entity instances automatically.

You don’t want to execute more persistence operations than absolutely necessary, and you don’t want to change your domain model—you need shared references to Bid instances. The third option is what you’ll use to simplify this parent/children example.

Transitive persistence

When you instantiate a new Bid and add it to an Item, the bid should become persistent automatically. You’d like to avoid making the Bid persistent explicitly with an extra save() operation.

To enable this transitive state across the association, add a cascade option to the XML mapping:

tmp9B179_thumb

The cascade=”save-update” attribute enables transitive persistence for Bid instances, if a particular Bid is referenced by a persistent Item, in the collection.

The cascade attribute is directional: It applies to only one end of the association. You could also add cascade=”save-update” to the <many-to-one> association in the mapping of Bid, but because bids are created after items, doing so doesn’t make sense.

JPA also supports cascading entity instance state on associations:

tmp9B180_thumb

Cascading options are per operation you’d like to be transitive. For native Hibernate, you cascade the save and update operations to associated entities with cascade=”save-update”. Hibernate’s object state management always bundles these two things together, as you’ll learn in future topics. In JPA, the (almost) equivalent operations are persist and merge.

You can now simplify the code that links and saves an Item and a Bid, in native Hibernate:

tmp9B181_thumb

Don’t worry about the update and merge operations for now; we’ll come back to them later in the topic.

tmp9B182_thumb

All entities in the bids collection are now persistent as well, just as they would be if you called save() on each Bid manually. With the JPA EntityManager API, the equivalent to a Session, the code is as follows:

FAQ What is the effect of cascade on inverse ? Many new Hibernate users ask this question. The answer is simple: The cascade attribute has nothing to do with the inverse attribute. They often appear on the same collection mapping. If you map a collection of entities as inverse=”true”, you’re controlling the generation of SQL for a bidirectional association mapping. It’s a hint that tells Hibernate you mapped the same foreign key column twice. On the other hand, cascading is used as a convenience feature. If you decide to cascade operations from one side of an entity relationship to associated entities, you save the lines of code needed to manage the state of the other side manually. We say that object state becomes transitive. You can cascade state not only on collections of entities, but on all entity association mappings. cascade and inverse have in common the fact that they don’t appear on collections of value types or on any other value-type mappings. The rules for these are implied by the nature of value types.

Are you finished now? Well, perhaps not quite. Cascading deletion

With the previous mapping, the association between Bid and Item is fairly loose. So far, we have only considered making things persistent as a transitive state. What about deletion?

It seems reasonable that deletion of an item implies deletion of all bids for the item. In fact, this is what the composition (the filled out diamond) in the UML diagram means. With the current cascading operations, you have to write the following code to make that happen:

tmp9B183_thumb

First you remove the references to the bids by iterating the collection. You delete each Bid instance in the database. Finally, the Item is deleted. Iterating and removing the references in the collection seems unnecessary; after all, you’ll delete the Item at the end anyway. If you can guarantee that no other object (or row in any other table) holds a reference to these bids, you can make the deletion transitive.

Hibernate (and JPA) offer a cascading option for this purpose. You can enable cascading for the delete operation:

tmp9B184_thumb

The operation you cascade in JPA is called remove:

tmp9B185_thumb

The same code to delete an item and all its bids is reduced to the following, in Hibernate or with JPA:

tmp9B186_thumb

The delete operation is now cascaded to all entities referenced in the collection. You no longer have to worry about removal from the collection and manually deleting those entities one by one.

Let’s consider one further complication. You may have shared references to the Bid objects. As suggested earlier, a User may have a collection of references to the Bid instances they made. You can’t delete an item and all its bids without removing these references first. You may get an exception if you try to commit this transaction, because a foreign key constraint may be violated.

You have to chase the pointers. This process can get ugly, as you can see in the following code, which removes all references from all users who have references before deleting the bids and finally the item:

tmp9B187_thumbtmp9B188_thumb

Obviously, the additional query (in fact, many queries) isn’t what you want. However, in a network object model, you don’t have any choice other than executing code like this if you want to correctly set pointers and references—there is no persistent garbage collector or other automatic mechanism. No Hibernate cascading option helps you; you have to chase all references to an entity before you finally delete it.

(This isn’t the whole truth: Because the BIDDER_ID foreign key column that represents the association from User to Bid is in the BID table, these references are automatically removed at the database level if a row in the BID table is deleted. This doesn’t affect any objects that are already present in memory in the current unit of work, and it also doesn’t work if BIDDER_ID is mapped to a different (intermediate) table. To make sure all references and foreign key columns are nulled out, you need to chase pointers in Java.)

On the other hand, if you don’t have shared references to an entity, you should rethink your mapping and map the bids as a collection components (with the Bid as a <composite-element>). With an <idbag> mapping, even the tables look the same:

tmp9B189_thumbtmp9B190_thumb

The separate mapping for Bid is no longer needed.

If you really want to make this a one-to-many entity association, Hibernate offers another convenience option you may be interested in.

Enabling orphan deletion

The cascading option we explain now is somewhat difficult to understand. If you followed the discussion in the previous section, you should be prepared.

Imagine you want to delete a Bid from the database. Note that you aren’t deleting the parent (the Item) in this case. The goal is to remove a row in the BID table. Look at this code:

anItem.getBids().remove(aBid);

If the collection has the Bid mapped as a collection of components, as in the previous section, this code triggers several operations:

■ The aBid instance is removed from the collection Item.bids.

■ Because Bid is mapped as a value type, and no other object can hold a reference to the aBid instance, the row representing this bid is deleted from the BID table by Hibernate.

In other words, Hibernate assumes that aBid is an orphan if it’s removed from its owning entity’s collection. No other in-memory persistent object is holding a reference to it. No foreign key value that references this row can be present in the database. Obviously, you designed your object model and mapping this way by making the Bid class an embeddable component.

However, what if Bid is mapped as an entity and the collection is a <one-to-many>? The code changes to and the row can be removed safely. You may have removed all other references manually. Or, if you didn’t, the database constraints prevent any inconsistency, and you see a foreign key constraint exception.

tmp9B191_thumb

The aBid instance has its own lifecycle, so it can exist outside of the collection. By deleting it manually, you guarantee that nobody else will hold a reference to it,

Hibernate offers you a way to declare this guarantee for collections of entity references. You can tell Hibernate, “If I remove an element from this collection, it will be an entity reference, and it’s going to be the only reference to that entity instance. You can safely delete it.” The code that worked for deletion with a collection of components works with collections of entity references.

This option is called cascade orphan delete. You can enable it on a collection mapping in XML as follows:

tmp9B192_thumb

With annotations, this feature is available only as a Hibernate extension:

tmp9B193_thumb

Also note that this trick works only for collections of entity references in a one-to-many association; conceptually, no other entity association mapping supports it. You should ask yourself at this point, with so many cascading options set on your collection, whether a simple collection of components may be easier to handle. After all, you’ve enabled a dependent lifecycle for objects referenced in this collection, so you may as well switch to the implicit and fully dependent lifecycle of components.

Finally, let’s look at the mapping in a JPA XML descriptor:

tmp9B194_thumbtmp9B195_thumb

Note that the Hibernate extension for cascade orphan deletion isn’t available in this case.

Summary

You’re probably a little overwhelmed by all the new concepts we introduced in this topic. You may have to read it a few times, and we encourage you to try the code (and watch the SQL log). Many of the strategies and techniques we’ve shown in this topic are key concepts of object/relational mapping. If you master collection mappings, and once you’ve mapped your first parent/children entity association, you’ll have the worst behind you. You’ll already be able to build entire applications!

Table 6.1 summarizes the differences between Hibernate and Java Persistence related to concepts discussed in this topic.

Table 6.1 Hibernate and JPA comparison chart


Hibernate Core

Java Persistence and EJB 3.0

Hibernate provides mapping support for sets, lists, maps, bags, identifier bags, and arrays. All JDK collection interfaces are supported, and extension points for custom persistent collections are available.

Standardized persistent sets, lists, maps, and bags are supported.

Collections of value types and components are supported.

Hibernate Annotations is required for collections of value types and embeddable objects.

Parent/children entity relationships are supported, with transitive state cascading on associations per operation.

You can map entity associations and enable transitive state cascading on associations per operation.

Table 6.1 Hibernate and JPA comparison chart  (continued)

Hibernate Core

Java Persistence and EJB 3.0

Automatic deletion of orphaned entity instances is built in.

Hibernate Annotations is required for automatic deletion of orphaned entity instances.

We’ve covered only a tiny subset of the entity association options in this topic. The remaining options we explore in detail in the next topic are either rare or variations of the techniques we’ve just described.

Next post:

Previous post: