Hibernate supports collections implementing java.util.SortedMap
and
java.util.SortedSet
. You must specify a comparator in the mapping file:
<set name="aliases" table="person_aliases" sort="natural"> <key column="person"/> <element column="name" type="string"/> </set> <map name="holidays" sort="my.custom.HolidayComparator"> <key column="year_id"/> <map-key column="hol_name" type="string"/> <element column="hol_date" type="date"/> </map>
Allowed values of the sort
attribute are unsorted
,
natural
and the name of a class implementing
java.util.Comparator
.
Sorted collections actually behave like java.util.TreeSet
or
java.util.TreeMap
.
If you want the database itself to order the collection elements use the
order-by
attribute of set
, bag
or map
mappings. This solution is only available under
JDK 1.4 or higher (it is implemented using LinkedHashSet
or
LinkedHashMap
). This performs the ordering in the SQL query,
not in memory.
<set name="aliases" table="person_aliases" order-by="lower(name) asc"> <key column="person"/> <element column="name" type="string"/> </set> <map name="holidays" order-by="hol_date, hol_name"> <key column="year_id"/> <map-key column="hol_name" type="string"/> <element column="hol_date type="date"/> </map>
Note that the value of the order-by
attribute is an SQL ordering, not
a HQL ordering!
Associations may even be sorted by some arbitrary criteria at runtime using a collection
filter()
.
sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();
A bidirectional association allows navigation from both "ends" of the association. Two kinds of bidirectional association are supported:
set or bag valued at one end, single-valued at the other
set or bag valued at both ends
You may specify a bidirectional many-to-many association simply by mapping two many-to-many associations to the same database table and declaring one end as inverse (which one is your choice, but it can not be an indexed collection).
Here's an example of a bidirectional many-to-many association; each category can have many items and each item can be in many categories:
<class name="Category"> <id name="id" column="CATEGORY_ID"/> ... <bag name="items" table="CATEGORY_ITEM"> <key column="CATEGORY_ID"/> <many-to-many class="Item" column="ITEM_ID"/> </bag> </class> <class name="Item"> <id name="id" column="ITEM_ID"/> ... <!-- inverse end --> <bag name="categories" table="CATEGORY_ITEM" inverse="true"> <key column="ITEM_ID"/> <many-to-many class="Category" column="CATEGORY_ID"/> </bag> </class>
Changes made only to the inverse end of the association are not persisted. This means that Hibernate has two representations in memory for every bidirectional association, one link from A to B and another link from B to A. This is easier to understand if you think about the Java object model and how we create a many-to-many relationship in Java:
category.getItems().add(item); // The category now "knows" about the relationship item.getCategories().add(category); // The item now "knows" about the relationship session.persist(item); // The relationship won't be saved! session.persist(category); // The relationship will be saved
The non-inverse side is used to save the in-memory representation to the database.
You may define a bidirectional one-to-many association by mapping a one-to-many association
to the same table column(s) as a many-to-one association and declaring the many-valued
end inverse="true"
.
<class name="Parent"> <id name="id" column="parent_id"/> .... <set name="children" inverse="true"> <key column="parent_id"/> <one-to-many class="Child"/> </set> </class> <class name="Child"> <id name="id" column="child_id"/> .... <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/> </class>
Mapping one end of an association with inverse="true"
doesn't
affect the operation of cascades, these are orthogonal concepts!
A bidirectional association where one end is represented as a <list>
or <map>
requires special consideration. If there is a property of
the child class which maps to the index column, no problem, we can continue using
inverse="true"
on the collection mapping:
<class name="Parent"> <id name="id" column="parent_id"/> .... <map name="children" inverse="true"> <key column="parent_id"/> <map-key column="name" type="string"/> <one-to-many class="Child"/> </map> </class> <class name="Child"> <id name="id" column="child_id"/> .... <property name="name" not-null="true"/> <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/> </class>
But, if there is no such property on the child class, we can't think of the association as
truly bidirectional (there is information available at one end of the association that is
not available at the other end). In this case, we can't map the collection
inverse="true"
. Instead, we could use the following mapping:
<class name="Parent"> <id name="id" column="parent_id"/> .... <map name="children"> <key column="parent_id" not-null="true"/> <map-key column="name" type="string"/> <one-to-many class="Child"/> </map> </class> <class name="Child"> <id name="id" column="child_id"/> .... <many-to-one name="parent" class="Parent" column="parent_id" insert="false" update="false" not-null="true"/> </class>
Note that in this mapping, the collection-valued end of the association is responsible for updates to the foreign key. TODO: Does this really result in some unnecessary update statements?
There are three possible approaches to mapping a ternary association. One is to use a
Map
with an association as its index:
<map name="contracts"> <key column="employer_id" not-null="true"/> <map-key-many-to-many column="employee_id" class="Employee"/> <one-to-many class="Contract"/> </map>
<map name="connections"> <key column="incoming_node_id"/> <map-key-many-to-many column="outgoing_node_id" class="Node"/> <many-to-many column="connection_id" class="Connection"/> </map>
A second approach is to simply remodel the association as an entity class. This is the approach we use most commonly.
A final alternative is to use composite elements, which we will discuss later.
If you've fully embraced our view that composite keys are a bad thing and that entities should have synthetic identifiers (surrogate keys), then you might find it a bit odd that the many to many associations and collections of values that we've shown so far all map to tables with composite keys! Now, this point is quite arguable; a pure association table doesn't seem to benefit much from a surrogate key (though a collection of composite values might). Nevertheless, Hibernate provides a feature that allows you to map many to many associations and collections of values to a table with a surrogate key.
The <idbag>
element lets you map a List
(or Collection
) with bag semantics.
<idbag name="lovers" table="LOVERS"> <collection-id column="ID" type="long"> <generator class="sequence"/> </collection-id> <key column="PERSON1"/> <many-to-many column="PERSON2" class="Person" fetch="join"/> </idbag>
As you can see, an <idbag>
has a synthetic id generator,
just like an entity class! A different surrogate key is assigned to each collection
row. Hibernate does not provide any mechanism to discover the surrogate key value
of a particular row, however.
Note that the update performance of an <idbag>
is
much better than a regular <bag>
!
Hibernate can locate individual rows efficiently and update or delete them
individually, just like a list, map or set.
In the current implementation, the native
identifier generation
strategy is not supported for <idbag>
collection identifiers.