Hibernate.orgCommunity Documentation
La notion de composants est réutilisée dans différents contextes, avec différents objectifs, à travers Hibernate.
Le composant est un objet inclus dans un autre objet, sauvegardé en tant que type valeur, et non en tant que référence entité. Le terme "composant" fait référence à la notion (au sens objet) de composition et non pas de composant au sens d'architecture de composants. Par exemple, on pourrait modéliser l'objet personne de la façon suivante :
public class Person {
private java.util.Date birthday;
private Name name;
private String key;
public String getKey() {
return key;
}
private void setKey(String key) {
this.key=key;
}
public java.util.Date getBirthday() {
return birthday;
}
public void setBirthday(java.util.Date birthday) {
this.birthday = birthday;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
......
......
}
public class Name {
char initial;
String first;
String last;
public String getFirst() {
return first;
}
void setFirst(String first) {
this.first = first;
}
public String getLast() {
return last;
}
void setLast(String last) {
this.last = last;
}
public char getInitial() {
return initial;
}
void setInitial(char initial) {
this.initial = initial;
}
}
Maintenant Name
pourra être sauvegardé en tant que composant de Person
. Remarquez que Name
définit des méthodes getter et setter pour ses propriétés persistantes, mais ne doit déclarer aucune interface ou propriété d'identification.
Dans Hibernate le mappage du composant serait :
<class name="eg.Person" table="person">
<id name="Key" column="pid" type="string">
<generator class="uuid"/>
</id>
<property name="birthday" type="date"/>
<component name="Name" class="eg.Name"
> <!-- class attribute optional -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</component>
</class
>
La table "person" aurait les colonnes pid
, birthday
, initial
, first
et last
.
Comme tous les types valeurs, les composants ne supportent pas les références partagées. En d'autres termes, deux instances de person peuvent avoir un même nom, mais ces noms sont indépendants, ils peuvent être identiques si on les compare par valeur mais ils représentent deux objets distincts en mémoire. La sémantique de la valeur null d'un composant est ad hoc. Quand il recharge l'objet qui contient le composant, Hibernate suppose que si toutes les colonnes de composants sont nulles, le composant est positionné à la valeur null. Ce choix programmatif devrait être satisfaisant dans la plupart des cas.
Les propriétés d'un composant peuvent être de tous les types habituellement supportés par Hibernate (collections, associations plusieurs-à-un, autres composants, etc). Les composants imbriqués ne doivent pas être vus comme quelque chose d'exotique. Hibernate a été conçu pour supporter un modèle d'objet finement granulé.
L'élément <component>
permet de déclarer un sous-élément <parent>
qui associe une propriété de la classe composant comme une référence arrière vers l'entité contenante.
<class name="eg.Person" table="person">
<id name="Key" column="pid" type="string">
<generator class="uuid"/>
</id>
<property name="birthday" type="date"/>
<component name="Name" class="eg.Name" unique="true">
<parent name="namedPerson"/> <!-- reference back to the Person -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</component>
</class
>
Les collections d'objets dépendants sont supportées (exemple: un tableau de type Name
). Déclarez votre collection de composants en remplaçant la balise <element>
par la balise <composite-element>
:
<set name="someNames" table="some_names" lazy="true">
<key column="id"/>
<composite-element class="eg.Name"
> <!-- class attribute required -->
<property name="initial"/>
<property name="first"/>
<property name="last"/>
</composite-element>
</set
>
Remarque : si vous définissez un Set
d'éléments composites, il est très important d'implémenter les méthodes equals()
et hashCode()
correctement.
Les éléments composites peuvent aussi contenir des composants mais pas des collections. Si votre élément composite contient aussi des composants, utilisez la balise <nested-composite-element>
. Une collection de composants qui contiennent eux-mêmes des composants est un cas très exotique. A ce stade, demandez-vous si une association un-à-plusieurs ne serait pas plus appropriée. Essayez de remodeler votre élément composite comme une entité - remarquez que si le modèle Java est le même, toutefois le modèle relationnel et la sémantique de persistance diffèrent quelque peu.
Remarquez que le mappage d'éléments composites ne supporte pas la nullité des propriétés lorsqu'on utilise un <set>
. Hibernate lorsqu'il supprime un objet, utilise chaque colonne pour identifier un objet (il n'y a pas de colonne distincte de clés primaires dans la table d'éléments composites), ce qui n'est pas possible avec des valeurs nulles. Vous devez donc choisir d'interdire la nullité des propriétés d'un élément composite ou choisir un autre type de collection comme : <list>
, <map>
, <bag>
ou <idbag>
.
Un cas particulier d'élément composite est un élément composite qui inclut un élément imbriqué <many-to-one>
. Un mappage comme celui-ci vous permet d'associer des colonnes supplémentaires d'une table d'association plusieurs à plusieurs à la classe de l'élément composite. L'exemple suivant est une association plusieurs à plusieurs de Order
à Item
où purchaseDate
, price
et quantity
sont des propriétés de l'association :
<class name="eg.Order" .... >
....
<set name="purchasedItems" table="purchase_items" lazy="true">
<key column="order_id">
<composite-element class="eg.Purchase">
<property name="purchaseDate"/>
<property name="price"/>
<property name="quantity"/>
<many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional -->
</composite-element>
</set>
</class
>
Par ailleurs, on ne peut évidemment pas faire référence à l'achat (purchase), pour pouvoir naviguer de façon bidirectionnelle dans l'association. N'oubliez pas que les composants sont de type valeurs et n'autorisent pas les références partagées. Un Purchase
unique peut être dans le set d'un Order
, mais ne peut pas être référencé par Item
simultanément.
Même les associations ternaires, quaternaires ou autres sont possibles :
<class name="eg.Order" .... >
....
<set name="purchasedItems" table="purchase_items" lazy="true">
<key column="order_id">
<composite-element class="eg.OrderLine">
<many-to-one name="purchaseDetails class="eg.Purchase"/>
<many-to-one name="item" class="eg.Item"/>
</composite-element>
</set>
</class
>
Des éléments composites peuvent apparaître dans les requêtes en utilisant la même syntaxe que les associations vers d'autres entités.
L'élément <composite-map-key>
vous permet de mapper une classe de composant comme indice d'une Map
. Assurez-vous de surcharger correctement hashCode()
et equals()
dans la classe du composant.
Vous pouvez utiliser un composant comme identifiant d'une classe entité. À cet effet, votre classe de composant doit respecter certaines exigences :
Elle doit implémenter java.io.Serializable
.
Elle doit redéfinir equals()
et hashCode()
, de façon cohérente avec la notion d'égalité de clé composite de la base de données.
Avec Hibernate3, la seconde exigence n'est plus absolument nécessaire, néanmoins continuez de l'effectuer.
Vous ne pouvez pas utiliser de IdentifierGenerator
pour générer des clés composites, par contre l'application doit assigner ses propres identifiants.
Utiliser la balise <composite-id>
(avec les éléments imbriqués <key-property>
) à la place de l'habituel déclaration <id>
. Par exemple, la classe OrderLine
possède une clé primaire qui dépend de la clé primaire (composite) de Order
.
<class name="OrderLine">
<composite-id name="id" class="OrderLineId">
<key-property name="lineId"/>
<key-property name="orderId"/>
<key-property name="customerId"/>
</composite-id>
<property name="name"/>
<many-to-one name="order" class="Order"
insert="false" update="false">
<column name="orderId"/>
<column name="customerId"/>
</many-to-one>
....
</class
>
Toutes les clés étrangères référençant la table OrderLine
sont également composites. Vous devez en tenir compte lorsque vous écrivez vos mappage d'association pour les autres classes. Une association à OrderLine
sera mappée de la façon suivante :
<many-to-one name="orderLine" class="OrderLine">
<!-- the "class" attribute is optional, as usual -->
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-one
>
The column
element is an alternative to the column
attribute everywhere. Using the column
element just gives more declaration options, which are mostly useful when utilizing hbm2ddl
Une association plusieurs-à-plusieurs
à OrderLine
utilisera aussi une clé étrangère composite :
<set name="undeliveredOrderLines">
<key column name="warehouseId"/>
<many-to-many class="OrderLine">
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-many>
</set
>
La collection des OrderLine
s dans Order
utilisera :
<set name="orderLines" inverse="true">
<key>
<column name="orderId"/>
<column name="customerId"/>
</key>
<one-to-many class="OrderLine"/>
</set
>
Comme d'habitude, l'élément <one-to-many>
ne déclare pas de colonne.
Si OrderLine
lui-même possède une collection, il possédera de même une clé composite étrangère.
<class name="OrderLine">
....
....
<list name="deliveryAttempts">
<key
> <!-- a collection inherits the composite key type -->
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</key>
<list-index column="attemptId" base="1"/>
<composite-element class="DeliveryAttempt">
...
</composite-element>
</set>
</class
>
Vous pouvez également mapper une propriété de type Map
:
<dynamic-component name="userAttributes">
<property name="foo" column="FOO" type="string"/>
<property name="bar" column="BAR" type="integer"/>
<many-to-one name="baz" class="Baz" column="BAZ_ID"/>
</dynamic-component
>
La sémantique de l'association à un <dynamic-component>
est identique à celle que l'on utilise pour le <component>
. L'avantage de ce type de mappage est qu'il permet de déterminer les véritables propriétés du bean au moment du déploiement, en éditant simplement le document de mappage. La manipulation du document de mappage pendant l'exécution de l'application est aussi possible en utilisant un parser DOM. Il y a même mieux, vous pouvez accéder (et changer) le métamodèle de configuration-temps de Hibernate en utilisant l'objet Configuration
.
Copyright © 2004 Red Hat, Inc.