6.3. Mapping collections with annotations

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

6.3.1. Basic collection mapping

The following maps a simple collection of String elements:

@org.hibernate.annotations.CollectionOfElements(
    targetElement = java.lang.String.class
)
@JoinTable(
    name = "ITEM_IMAGE",
    joinColumns = @JoinColumn(name = "ITEM_ID")
)
@Column(name = "FILENAME", nullable = false)
private Set<String> images = new HashSet<String>();

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):

@org.hibernate.annotations.CollectionOfElements
@JoinTable(
     name = "ITEM_IMAGE",
     joinColumns = @JoinColumn(name = "ITEM_ID")
)
@org.hibernate.annotations.IndexColumn(
     name="POSITION", base = 1

)
@Column(name = "FILENAME")
private List<String> images = new ArrayList<String>();

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, as you'll see in chapter 7.

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

@org.hibernate.annotations.CollectionOfElements
@JoinTable(
     name = "ITEM_IMAGE",
     joinColumns = @JoinColumn(name = "ITEM_ID")
)
@org.hibernate.annotations.MapKey(
     columns = @Column(name="IMAGENAME")
)
@Column(name = "FILENAME")
private Map<String, String> images = new HashMap<String, String>();

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.MapKey, which isn't very useful (see chapter 7, section 7.2.4 "Mapping maps").

6.3.2. Sorted and ordered collections

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

@org.hibernate.annotations.CollectionOfElements
@JoinTable(
    name = "ITEM_IMAGE",
    joinColumns = @JoinColumn(name = "ITEM_ID")
)
@Column(name = "FILENAME", nullable = false)
@org.hibernate.annotations.Sort(
    type = org.hibernate.annotations.SortType.NATURAL
)
private SortedSet<String> images = new TreeSet<String>();

(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:

@org.hibernate.annotations.CollectionOfElements
@JoinTable(
    name = "ITEM_IMAGE",
    joinColumns = @JoinColumn(name = "ITEM_ID")
)
@Column(name = "FILENAME", nullable = false)
@org.hibernate.annotations.OrderBy(
    clause = "FILENAME asc"
)
private Set<String> images = new HashSet<String>();

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.

6.3.3. 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 chapter, with image names, sizes, and so on.

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

@Embeddable
public class Image {

    @org.hibernate.annotations.Parent
    Item item;

    @Column(length = 255, nullable = false)
    private String name;


    @Column(length = 255, nullable = false)
    private String filename;

    @Column(nullable = false)
    private int sizeX;

    @Column(nullable = false)
    private int sizeY;

    ... // Constructor, accessor methods, equals()/hashCode()
}

Note that you again map a back pointer with a Hibernate annotation; anImage.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):

@org.hibernate.annotations.CollectionOfElements
@JoinTable(
    name = "ITEM_IMAGE",
    joinColumns = @JoinColumn(name = "ITEM_ID")
)
@AttributeOverride(
    name = "element.name",
    column = @Column(name = "IMAGENAME",
                     length = 255,
                     nullable = false)
)
private Set<Image> images = new HashSet<Image>();

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:

@org.hibernate.annotations.CollectionOfElements
@JoinTable(
    name = "ITEM_IMAGE",
    joinColumns = @JoinColumn(name = "ITEM_ID")
)
@CollectionId(
    columns = @Column(name = "ITEM_IMAGE_ID"),
    type = @org.hibernate.annotations.Type(type = "long"),
    generator = "sequence"
)
private Collection<Image> images = new ArrayList<Image>();

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset