Component Mapping
The notion of a component is re-used in several different contexts,
for different purposes, throughout Hibernate.
Dependent objects
A component is a contained object that is persisted as a value type, not an entity.
The term "component" refers to the object-oriented notion of composition
(not to architecture-level components). For example, you might model a person like this:
Now Name may be persisted as a component of
Person. Notice that Name defines getter
and setter methods for its persistent properties, but doesn't need to declare
any interfaces or identifier properties.
Our Hibernate mapping would look like:
]]>
The person table would have the columns pid,
birthday,
initial,
first and
last.
Like all value types, components do not support shared references. The null
value semantics of a component are ad hoc. When reloading the
containing object, Hibernate will assume that if all component columns are
null, then the entire component is null. This should be okay for most purposes.
The properties of a component may be of any Hibernate type (collections, many-to-one
associations, other components, etc). Nested components should not
be considered an exotic usage. Hibernate is intended to support a very fine-grained
object model.
The <component> element allows a <parent>
subelement that maps a property of the component class as a reference back to the
containing entity.
]]>
Collections of dependent objects
Collections of components are supported (eg. an array of type
Name). Declare your component collection by
replacing the <element> tag with a
<composite-element> tag.
]]>
Note: if you define a Set of composite elements, it is
very important to implement equals() and
hashCode() correctly.
Composite elements may contain components but not collections. If your
composite element itself contains
components, use the <nested-composite-element>
tag. This is a pretty exotic case - a collection of components which
themselves have components. By this stage you should be asking yourself
if a one-to-many association is more appropriate. Try remodelling the
composite element as an entity - but note that even though the Java model
is the same, the relational model and persistence semantics are still
slightly different.
Please note that a composite element mapping doesn't support null-able properties
if you're using a <set>. Hibernate
has to use each columns value to identify a record when deleting objects
(there is no separate primary key column in the composite element table),
which is not possible with null values. You have to either use only
not-null properties in a composite-element or choose a
<list>, <map>,
<bag> or <idbag>.
A special case of a composite element is a composite element with a nested
<many-to-one> element. A mapping like this allows
you to map extra columns of a many-to-many association table to the
composite element class. The following is a many-to-many association
from Order to Item where
purchaseDate, price and
quantity are properties of the association:
....
]]>
Even ternary (or quaternary, etc) associations are possible:
....
]]>
Composite elements may appear in queries using the same syntax as
associations to other entities.
Components as Map indices
The <composite-index> element lets you map a
component class as the key of a Map. Make sure you override
hashCode() and equals() correctly on
the component class.
Components as composite identifiers
You may use a component as an identifier of an entity class. Your component
class must satisfy certain requirements:
It must implement java.io.Serializable.
It must re-implement equals() and
hashCode(), consistently with the database's
notion of composite key equality.
You can't use an IdentifierGenerator to generate composite keys.
Instead the application must assign its own identifiers.
Since a composite identifier must be assigned to the object before saving it,
we can't use unsaved-value of the identifier to distinguish
between newly instantiated instances and instances saved in a previous session.
You may instead implement Interceptor.isUnsaved() if
you wish to use saveOrUpdate() or cascading save / update.
As an alternative, you may also set the unsaved-value
attribute on a <version> (or
<timestamp>) element to specify a
value that indicates a new transient instance. In this case, the version
of the entity is used instead of the (assigned) identifier and you don't have
to implement Interceptor.isUnsaved() yourself.
Use the <composite-id> tag (same attributes and
elements as <component>) in place of
<id> for the declaration of a composite identifier
class:
....
]]>
Now, any foreign keys into the table FOOS are also composite.
You must declare this in your mappings for other classes. An association to
Foo would be declared like this:
]]>
This new <column> tag is also used by multi-column custom types.
Actually it is an alternative to the column attribute everywhere. A
collection with elements of type Foo would use:
]]>
On the other hand, <one-to-many>, as usual, declares no columns.
If Foo itself contains collections, they will also need a
composite foreign key.
....
....
]]>
Dynamic components
You may even map a property of type Map:
]]>
The semantics of a <dynamic-component> mapping are identical
to <component>. The advantage of this kind of mapping is
the ability to determine the actual properties of the bean at deployment time, just
by editing the mapping document. (Runtime manipulation of the mapping document is
also possible, using a DOM parser.)