Chapitre 5. Mapping O/R basique

5.1. Déclaration de Mapping

Les mappings Objet/relationnel sont généralement définis dans un document XML. Le document de mapping est conçu pour être lisible et éditable à la main. Le langage de mapping est Java-centrique, c'est à dire que les mappings sont construits à partir des déclarations des classes persistantes et non des déclarations des tables.

Remarquez que même si beaucoup d'utilisateurs de Hibernate préfèrent écrire les fichiers de mappings à la main, plusieurs outils existent pour générer ce document, notamment XDoclet, Middlegen et AndroMDA.

Démarrons avec un exemple de mapping :

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
      "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="eg">

        <class name="Cat" 
            table="cats"
            discriminator-value="C">
                
                <id name="id">
                        <generator class="native"/>
                </id>

                <discriminator column="subclass" 
                     type="character"/>

                <property name="weight"/>

                <property name="birthdate"
                    type="date" 
                    not-null="true" 
                    update="false"/>

                <property name="color"
                    type="eg.types.ColorUserType"
                    not-null="true"
                    update="false"/>

                <property name="sex"
                    not-null="true" 
                    update="false"/>

                <property name="litterId"
                    column="litterId"
                    update="false"/>

                <many-to-one name="mother"
                    column="mother_id"
                    update="false"/>

                <set name="kittens"
                    inverse="true"
                    order-by="litter_id">
                        <key column="mother_id"/>
                        <one-to-many class="Cat"/>
                </set>

                <subclass name="DomesticCat"
                    discriminator-value="D">

                        <property name="name" 
                            type="string"/>

                </subclass>

        </class>

        <class name="Dog">
                <!-- mapping for Dog could go here -->
        </class>

</hibernate-mapping>

Etudions le contenu du document de mapping. Nous décrirons uniquement les éléments et attributs du document utilisés par Hibernate à l'exécution. Le document de mapping contient aussi des attributs et éléments optionnels qui agissent sur le schéma de base de données exporté par l'outil de génération de schéma. (Par exemple l'attribut not-null.)

5.1.1. Doctype

Tous les mappings XML devraient utiliser le doctype indiqué. Ce fichier est présent à l'URL ci-dessus, dans le répertoire hibernate-x.x.x/src/org/hibernate ou dans hibernate3.jar. Hibernate va toujours chercher la DTD dans son classpath en premier lieu. Si vous constatez des recherches de la DTD sur Internet, vérifiez votre déclaration de DTD par rapport au contenu de votre classpath.

5.1.1.1. EntityResolver

Comme cité précédemment, Hibernate tentera de trouver les DTDs d'abord dans son classpath. Il réussit à faire cela en utilisant une implémentation particulière de org.xml.sax.EntityResolver avec le SAXReader qu'il utilise pour lire les fichiers xml. Cet EntityResolver particulier reconnait deux espaces de nommage systemId différents.

  • un espace de nommage hibernate est reconnu dès qu'un systemId commence par http://hibernate.sourceforge.net/; alors ces entités sont résolues via le classloader qui a chargé les classes Hibernate.

  • un espace de nommage utilisateur est reconnu dès qu'un systemId utilise un protocol URL classpath://. Le résolveur tentera de résoudre ces entités via (1) le classloader du contexte du thread courant et (2) le classloader qui a chargé les classes Hibernate.

Un exemple d'utilisation de l'espace de nommage utilisateur:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" [
    <!ENTITY types SYSTEM "classpath://your/domain/types.xml">
]>

<hibernate-mapping package="your.domain">
    <class name="MyEntity">
        <id name="id" type="my-custom-id-type">
            ...
        </id>
    <class>
    &types;
</hibernate-mapping>

types.xml est une ressource dans le package your.domain et qui contient un Section 5.2.3, « Types de valeur définis par l'utilisateur » particulier.

5.1.2. hibernate-mapping

Cet élément a plusieurs attributs optionnels. Les attributs schema et catalog indiquent que les tables référencées par ce mapping appartiennent au schéma nommé et/ou au catalogue. S'ils sont spécifiés, les noms de tables seront qualifiés par les noms de schéma et catalogue. L'attribut default-cascade indique quel type de cascade sera utlisé par défaut pour les propriétés et collections qui ne précisent pas l'attribut cascade. L'attribut auto-import nous permet d'utiliser par défaut des noms de classes non qualifiés dans le langage de requête.

<hibernate-mapping
         schema="schemaName"                          (1)
         catalog="catalogName"                        (2)
         default-cascade="cascade_style"              (3)
         default-access="field|property|ClassName"    (4)
         default-lazy="true|false"                    (5)
         auto-import="true|false"                     (6)
         package="package.name"                       (7)
 />
(1)

schema (optionnel) : Le nom d'un schéma de base de données.

(2)

catalog (optionnel) : Le nom d'un catalogue de base de données.

(3)

default-cascade (optionnel - par défaut vaut : none) : Un type de cascade par défaut.

(4)

default-access (optionnel - par défaut vaut : property) : Comment hibernate accèdera aux propriétés. On peut aussi redéfinir sa propre implémentation de PropertyAccessor.

(5)

default-lazy (optionnel - par défaut vaut : true) : Valeur par défaut pour un attribut lazy non spécifié : celui des mappings de classes et de collection.

(6)

auto-import (optionnel - par défaut vaut : true) : Spécifie si l'on peut utiliser des noms de classes non qualifiés (des classes de ce mapping) dans le langage de requête.

(7)

package (optionnel) : Préfixe de package par défaut pour les noms de classe non qualifiés du document de mapping.

Si deux classes possèdent le même nom de classe (non qualifié), vous devez indiquer auto-import="false". Hibernate lancera une exception si vous essayez d'assigner à deux classes le même nom importé.

Notez que l'élément hibernate-mapping vous permet d'imbriquer plusieurs mappings de <class> persistantes, comme dans l'exemple ci-dessus. Cependant la bonne pratique (ce qui est attendu par certains outils) est de mapper une seule classe (ou une seule hiérarchie de classes) par fichier de mapping et de nommer ce fichier d'après le nom de la superclasse, par exemple Cat.hbm.xml, Dog.hbm.xml, ou en cas d'héritage, Animal.hbm.xml.

5.1.3. class

Déclarez une classe persistante avec l'élément class :

<class
        name="ClassName"                              (1)
        table="tableName"                             (2)
        discriminator-value="discriminator_value"     (3)
        mutable="true|false"                          (4)
        schema="owner"                                (5)
        catalog="catalog"                             (6)
        proxy="ProxyInterface"                        (7)
        dynamic-update="true|false"                   (8)
        dynamic-insert="true|false"                   (9)
        select-before-update="true|false"             (10)
        polymorphism="implicit|explicit"              (11)
        where="arbitrary sql where condition"         (12)
        persister="PersisterClass"                    (13)
        batch-size="N"                                (14)
        optimistic-lock="none|version|dirty|all"      (15)
        lazy="true|false"                             (16)
        entity-name="EntityName"                      (17)
        catalog="catalog"                             (18)
        check="arbitrary sql check condition"         (19)
        rowid="rowid"                                 (20)
        subselect="SQL expression"                    (21)
        abstract="true|false"
        entity-name="EntityName"
/>
(1)

name (optionnel) : Le nom Java complet de la classe (ou interface) persistante. Si cet attribut est absent, il est supposé que ce mapping ne se rapporte pas à une entité POJO.

(2)

table (optionnel - par défaut le nom (non-qualifié) de la classe) : Le nom de sa table en base de données.

(3)

discriminator-value (optionnel - par défaut le nom de la classe) : Une valeur permettant de distinguer les sous-classes dans le cas de l'utilisation du polymorphisme. Les valeurs null et not null sont autorisées.

(4)

mutable (optionnel, vaut true par défaut) : Spécifie que des instances de la classe sont (ou non) immuables.

(5)

schema (optionnel) : Surcharge le nom de schéma spécifié par l'élément racine <hibernate-mapping>.

(6)

catalog (optionnel) : Surcharge le nom du catalogue spécifié par l'élément racine <hibernate-mapping>.

(7)

proxy (optionnel) : Spécifie une interface à utiliser pour l'initialisation différée (lazy loading) des proxies. Vous pouvez indiquer le nom de la classe elle-même.

(8)

dynamic-update (optionnel, par défaut à false) : Spécifie que les UPDATE SQL doivent être générés à l'exécution et contenir uniquement les colonnes dont les valeurs ont été modifiées.

(9)

dynamic-insert (optionnel, par défaut à false): Spécifie que les INSERT SQL doivent être générés à l'exécution et ne contenir que les colonnes dont les valeurs sont non nulles.

(10)

select-before-update (optionnel, par défaut à false): Spécifie que Hibernate ne doit jamais exécuter un UPDATE SQL sans être certain qu'un objet a été réellement modifié. Dans certains cas, (en réalité, seulement quand un objet transient a été associé à une nouvelle session par update()), cela signifie que Hibernate exécutera un SELECT SQL pour s'assurer qu'un UPDATE SQL est véritablement nécessaire.

(11)

polymorphism (optionnel, vaut implicit par défaut) : Détermine si, pour cette classe, une requête polymorphique implicite ou explicite est utilisée.

(12)

where (optionnel) spécifie une clause SQL WHERE à utiliser lorsque l'on récupère des objets de cette classe.

(13)

persister (optionnel) : Spécifie un ClassPersister particulier.

(14)

batch-size (optionnel, par défaut = 1) : spécifie une taille de batch pour remplir les instances de cette classe par identifiant en une seule requête.

(15)

optimistic-lock (optionnel, par défaut = version) : Détermine la stratégie de verrou optimiste.

(16)

lazy (optionnel) : Déclarer lazy="true" est un raccourci pour spécifier le nom de la classe comme étant l'interface proxy.

(17)

entity-name (optionnel) : Hibernate3 permet à une classe d'être mappée plusieurs fois (potentiellement à plusieurs tables), et permet aux mappings d'entité d'être représentés par des Maps ou du XML au niveau Java. Dans ces cas, vous devez indiquer un nom explicite arbitraire pour les entités. Voir Section 4.4, « Modèles dynamiques » et Chapitre 18, Mapping XML pour plus d'informations.

(18)

catalog (optionnel) : The name of a database catalog used for this class and its table.

(19)

check (optionnel) : expression SQL utilisée pour générer une contrainte de vérification multi-lignes pour la génération automatique de schéma.

(20)

rowid (optionnel) : Hibernate peut utiliser des ROWID sur les bases de données qui utilisent ce mécanisme. Par exemple avec Oracle, Hibernate peut utiliser la colonne additionnelle rowid pour des mises à jour rapides si cette option vaut rowid. Un ROWID représente la localisation physique d'un tuple enregistré.

(21)

subselect (optionnel) : Permet de mapper une entité immuable en lecture-seule sur un sous-select de base de données. Utile pour avoir une vue au lieu d'une table en base, mais à éviter. Voir plus bas pour plus d'information.

???

abstract (optionnel) : Utilisé pour marquer des superclasses abstraites dans des hiérarchies de <union-subclass>.

Il est tout à fait possible d'utiliser une interface comme nom de classe persistante. Vous devez alors déclarer les classes implémentant cette interface en utilisant l'élément <subclass>. Vous pouvez faire persister toute classe interne static. Vous devez alors spécifier le nom de la classe par la notation habituelle des classes internes c'est à dire eg.Foo$Bar.

Les classes immuables, mutable="false", ne peuvent pas être modifiées ou supprimées par l'application. Cela permet à Hibernate de faire quelques optimisations mineures sur les performances.

L'attribut optionnnel proxy permet les intialisations différées des instances persistantes de la classe. Hibernate retournera initialement des proxies CGLIB qui implémentent l'interface nommée. Le véritable objet persistant ne sera chargé que lorsque une méthode du proxy sera appelée. Voir plus bas le paragraphe abordant les proxies et le chargement différé (lazy initialization).

Le polymorphisme implicite signifie que les instances de la classe seront retournées par une requête qui utilise les noms de la classe ou de chacune de ses superclasses ou encore des interfaces implémentées par cette classe ou ses superclasses. Les instances des classes filles seront retournées par une requête qui utilise le nom de la classe elle même. Le polymorphisme explicite signifie que les instances de la classe ne seront retournées que par une requête qui utilise explicitement son nom et que seules les instances des classes filles déclarées dans les éléments <subclass> ou <joined-subclass> seront retournées. Dans la majorités des cas la valeur par défaut, polymorphism="implicit", est appropriée. Le polymorphisme explicite est utile lorsque deux classes différentes sont mappées à la même table (ceci permet d'écrire une classe "légère" qui ne contient qu'une partie des colonnes de la table - voir la partie design pattern du site communautaire).

L'attribut persister vous permet de customiser la stratégie utilisée pour la classe. Vous pouvez, par exemple, spécifier votre propre sous-classe de org.hibernate.persister.EntityPersister ou vous pourriez aussi créer une nouvelle implémentation de l'interface org.hibernate.persister.ClassPersister qui proposerait une persistance via, par exemple, des appels de procédures stockées, de la sérialisation vers des fichiers plats ou un annuaire LDAP. Voir org.hibernate.test.CustomPersister pour un exemple simple (d'une "persistance" vers une Hashtable).

Notez que les paramètres dynamic-update et dynamic-insert ne sont pas hérités par les sous-classes et peuvent donc être spécifiés pour les éléments <subclass> ou <joined-subclass> Ces paramètres peuvent améliorer les performances dans certains cas, mais peuvent aussi les amoindrir. A utiliser en connaissance de causes.

L'utilisation de select-before-update va généralement faire baisser les performances. Ce paramètre est pratique pour prévenir l'appel inutile d'un trigger sur modification quand on réattache un graphe d'instances à une Session.

Si vous utilisez le dynamic-update, les différentes stratégies de verrouillage optimiste (optimistic locking) sont les suivantes:

  • version vérifie les colonnes version/timestamp

  • all vérifie toutes les colonnes

  • dirty vérifie les colonnes modifiées, permettant des updates concurrents

  • none pas de verrouillage optimiste

Nous encourageons très fortement l'utilisation de colonnes de version/timestamp pour le verrouillage optimiste avec Hibernate. C'est la meilleure stratégie en regard des performances et la seule qui gère correctement les modifications sur les objets détachés (c'est à dire lorsqu'on utilise Session.merge()).

Il n'y a pas de différence entre table et vue pour le mapping Hibernate, tant que c'est transparent au niveau base de données (remarquez que certaines BDD ne supportent pas les vues correctement, notamment pour les updates). Vous rencontrerez peut-être des cas où vous souhaitez utiliser une vue mais ne pouvez pas en créer sur votre BDD (par exemple à cause de schémas anciens et figés). Dans ces cas, vous pouvez mapper une entité immuable en lecture seule sur un sous-select SQL donné:

<class name="Summary">
    <subselect>
        select item.name, max(bid.amount), count(*)
        from item
        join bid on bid.item_id = item.id
        group by item.name
    </subselect>
    <synchronize table="item"/>
    <synchronize table="bid"/>
    <id name="name"/>
    ...
</class>

Déclarez les tables à synchroniser avec cette entité pour assurer que le flush automatique se produise correctement, et pour que les requêtes sur l'entité dérivée ne renvoient pas des données périmées. Le litéral <subselect> est disponible comme attribut ou comme élément de mapping.

5.1.4. id

Les classes mappées doivent déclarer la clef primaire de la table en base de données. La plupart des classes auront aussi une propriété de type javabean présentant l'identifiant unique d'une instance. L'élément <id> sert à définir le mapping entre cette propriété et la clef primaire en base.

<id
        name="propertyName"                                     (1)
        type="typename"                                         (2)
        column="column_name"                                    (3)
        unsaved-value="null|any|none|undefined|id_value"        (4)
        access="field|property|ClassName">                      (5)

        <generator class="generatorClass"/>
</id>
(1)

name (optionnel) : Nom de la propriété qui sert d'identifiant.

(2)

type (optionnel) : Nom indiquant le type Hibernate.

(3)

column (optionnel - le nom de la propriété est pris par défaut) : Nom de la clef primaire.

(4)

unsaved-value (optionnel - par défaut une valeur "bien choisie") : Une valeur de la propriété d'identifiant qui indique que l'instance est nouvellement instanciée (non sauvegardée), et qui la distingue des instances transients qui ont été sauvegardées ou chargées dans une session précédente.

(5)

access (optionnel - par défaut property) : La stratégie que doit utiliser Hibernate pour accéder aux valeurs des propriétés.

Si l'attribut name est absent, Hibernate considère que la classe ne possède pas de propriété identifiant.

L'attribut unsaved-value est important ! Si l'identifiant de votre classe n'a pas une valeur par défaut compatible avec le comportement standard de Java (zéro ou null), vous devez alors préciser la valeur par défaut.

La déclaration alternative <composite-id> permet l'acccès aux données d'anciens systèmes qui utilisent des clefs composées. Son utilisation est fortement déconseillée pour d'autres cas.

5.1.4.1. Generator

L'élément fils <generator> nomme une classe Java utilisée pour générer les identifiants uniques pour les instances des classes persistantes. Si des paramètres sont requis pour configurer ou initialiser l'instance du générateur, ils sont passés en utilisant l'élément <param>.

<id name="id" type="long" column="cat_id">
        <generator class="org.hibernate.id.TableHiLoGenerator">
                <param name="table">uid_table</param>
                <param name="column">next_hi_value_column</param>
        </generator>
</id>

Tous les générateurs doivent implémenter l'interface org.hibernate.id.IdentifierGenerator. C'est une interface très simple ; certaines applications peuvent proposer leur propre implémentations spécialisées. Cependant, Hibernate propose une série d'implémentations intégrées. Il existe des noms raccourcis pour les générateurs intégrés :

increment

Génère des identifiants de type long, short ou int qui ne sont uniques que si aucun autre processus n'insère de données dans la même table. Ne pas utiliser en environnement clusterisé.

identity

Utilisation de la colonne identity de DB2, MySQL, MS SQL Server, Sybase et HypersonicSQL. L'identifiant renvoyé est de type long, short ou int.

sequence

Utilisation des séquences dans DB2, PostgreSQL, Oracle, SAP DB, McKoi ou d'un générateur dans Interbase. L'identifiant renvoyé est de type long, short ou int

hilo

Utilise un algorithme hi/lo pour générer de façon efficace des identifiants de type long, short ou int, en prenant comme source de valeur "hi" une table et une colonne (par défaut hibernate_unique_key et next_hi respectivement). L'algorithme hi/lo génère des identifiants uniques pour une base de données particulière seulement.

seqhilo

Utilise un algorithme hi/lo pour générer efficacement des identifiants de type long, short ou int, étant donné un nom de séquence en base.

uuid

Utilise un algorithme de type UUID 128 bits pour générer des identifiants de type string, unique au sein d'un réseau (l'adresse IP est utilisée). Le UUID en codé en une chaîne de nombre héxadécimaux de longueur 32.

guid

Utilise une chaîne GUID générée par la base pour MS SQL Server et MySQL.

native

Choisit identity, sequence ou hilo selon les possibilités offertes par la base de données sous-jacente.

assigned

Laisse l'application affecter un identifiant à l'objet avant que la métode save() soit appelée. Il s'agit de la stratégie par défaut si aucun <generator> n'est spécifié.

select

Récupère une clef primaire assignée par un trigger en sélectionnant la ligne par une clef unique quelconque.

foreign

Utilise l'identifiant d'un objet associé. Habituellement utilisé en conjonction avec une association <one-to-one> sur la clef primaire.

5.1.4.2. algorithme Hi/lo

Les générateurs hilo et seqhilo proposent deux implémentations alternatives de l'algorithme hi/lo, une approche largement utilisée pour générer des identifiants. La première implémentation nécessite une table "spéciale" en base pour héberger la prochaine valeur "hi" disponible. La seconde utilise une séquence de type Oracle (quand la base sous-jacente le propose).

<id name="id" type="long" column="cat_id">
        <generator class="hilo">
                <param name="table">hi_value</param>
                <param name="column">next_value</param>
                <param name="max_lo">100</param>
        </generator>
</id>
<id name="id" type="long" column="cat_id">
        <generator class="seqhilo">
                <param name="sequence">hi_value</param>
                <param name="max_lo">100</param>
        </generator>
</id>

Malheureusement, vous ne pouvez pas utilisez hilo quand vous apportez votre propre Connection à Hibernate. Quand Hibernate utilise une datasource du serveur d'application pour obtenir des connexions inscrites avec JTA, vous devez correctement configurer hibernate.transaction.manager_lookup_class.

5.1.4.3. UUID algorithm

Le contenu du UUID est : adresse IP, date de démarrage de la JVM (précis au quart de seconde), l'heure système et un compteur (unique au sein de la JVM). Il n'est pas possible d'obtenir l'adresse MAC ou une adresse mémoire à partir de Java, c'est donc le mieux que l'on puisse faire sans utiliser JNI.

5.1.4.4. Colonnes identifiantes et séquences

Pour les bases qui implémentent les colonnes "identité" (DB2, MySQL, Sybase, MS SQL), vous pouvez utiliser la génération de clef par identity. Pour les bases qui implémentent les séquences (DB2, Oracle, PostgreSQL, Interbase, McKoi, SAP DB) vous pouvez utiliser la génération de clef par sequence. Ces deux méthodes nécessitent deux requêtes SQL pour insérer un objet.

<id name="id" type="long" column="person_id">
        <generator class="sequence">
                <param name="sequence">person_id_sequence</param>
        </generator>
</id>
<id name="id" type="long" column="person_id" unsaved-value="0">
        <generator class="identity"/>
</id>

Pour le développement multi-plateformes, la stratégie native choisira entre les méthodes identity, sequence et hilo, selon les possibilités offertes par la base sous-jacente.

5.1.4.5. Identifiants assignés

Si vous souhaitez que l'application assigne des identifiants (par opposition à la génération par Hibernate), vous pouvez utiliser le générateur assigned. Ce générateur spécial utilisera une valeur d'identifiant déjà utilisé par la propriété identifiant l'objet. Ce générateur est utilisé quand la clef primaire est une clef naturelle plutôt qu'une clef secondaire. C'est le comportement par défaut si vous ne précisez pas d'élément <generator>.

Choisir le générateur assigned fait utiliser unsaved-value="undefined" par Hibernate, le forçant à interroger la base pour déterminer si l'instance est transiente ou détachée, à moins d'utiliser une propriété version ou timestamp, ou alors de définir Interceptor.isUnsaved().

5.1.4.6. Clefs primaires assignées par trigger

Pour les schémas de base hérités d'anciens systèmes uniquement (Hibernate ne génère pas de DDL avec des triggers)

<id name="id" type="long" column="person_id">
        <generator class="select">
                <param name="key">socialSecurityNumber</param>
        </generator>
</id>

Dans l'exemple ci-dessus, socialSecurityNumber a une valeur unique définie par la classe en tant que clef naturelle et person_id est une clef secondaire dont la valeur est générée par trigger.

5.1.5. composite-id

<composite-id
        name="propertyName"
        class="ClassName"
        mapped="true|false"
        access="field|property|ClassName">
        node="element-name|."

        <key-property name="propertyName" type="typename" column="column_name"/>
        <key-many-to-one name="propertyName class="ClassName" column="column_name"/>
        ......
</composite-id>

Pour une table avec clef composée, vous pouvez mapper plusieurs attributs de la classe comme propriétés identifiantes. L'élement <composite-id> accepte les mappings de propriétés <key-property> et les mappings <key-many-to-one> comme fils.

<composite-id>
        <key-property name="medicareNumber"/>
        <key-property name="dependent"/>
</composite-id>

Vos classes persistantes doivent surcharger les méthodes equals() et hashCode() pour implémenter l'égalité d'identifiant composé. Elles doivent aussi implenter l'interface Serializable.

Malheureusement cette approche sur les identifiants composés signifie qu'un objet persistant est son propre identifiant. Il n'y a pas d'autre moyen pratique de manipuler l'objet que par l'objet lui-même. Vous devez instancier une instance de la classe persistante elle-même et peupler ses attributs identifiants avant de pouvoir appeler la méthode load() pour charger son état persistant associé à une clef composée. Nous appelons cette approche "identifiant composé embarqué" et ne la recommandons pas pour des applications complexes.

Une seconde approche, appelée identifiant composé mappé, consiste à encapsuler les propriétés identifiantes (celles contenues dans <composite-id>) dans une classe particulière.

<composite-id class="MedicareId" mapped="true">
        <key-property name="medicareNumber"/>
        <key-property name="dependent"/>
</composite-id>

Dans cet exemple, la classe d'identifiant composée,MedicareId et la classe mappée elle-même, possèdent les propriétés medicareNumber et dependent. La classe identifiante doit redéfinir equals() et hashCode() et implémenter Serializable. Le désavantage de cette approche est la duplication du code.

Les attributs suivants servent à configurer un identifiant composé mappé :

  • mapped (optionnel, défaut à false) : indique qu'un identifiant composé mappé est utilisé, et que les propriétés contenues font référence aux deux classes (celle mappée et la classe identifiante).

  • class (optionnel, mais requis pour un identifiant composé mappé) : La classe composant utilisée comme identifiant composé.

Nous décrirons une troisième approche beaucoup plus efficace ou l'identifiant composé est implémenté comme une classe composant dans Section 8.4, « Utiliser un composant comme identifiant ». Les attributs décrits ci dessous, ne s'appliquent que pour cette dernière approche :

  • name (optionnel, requis pour cette approche) : une propriété de type composant qui contient l'identifiant composé (voir chapitre 9).

  • access (optionnel - défaut à property) : La stratégie qu'Hibernate utilisera pour accéder à la valeur de la propriété.

  • class (optionnel - défaut au type de la propriété déterminé par réflexion) : La classe composant utilisée comme identifiant (voir prochaine section).

Cette dernière approche est celle que nous recommandons pour toutes vos applications.

5.1.6. discriminator

L'élément <discriminator> est nécessaire pour la persistance polymorphique qui utilise la stratégie de mapping de table par hiérarchie de classe. La colonne discriminante contient une valeur marqueur qui permet à la couche de persistance de savoir quelle sous-classe instancier pour une ligne particulière de table en base. Un nombre restreint de types peuvent être utilisés : string, character, integer, byte, short, boolean, yes_no, true_false.

<discriminator
        column="discriminator_column"                      (1)
        type="discriminator_type"                          (2)
        force="true|false"                                 (3)
        insert="true|false"                                (4)
        formula="arbitrary sql expression"                 (5)
/>
(1)

column (optionnel - par défaut à class) le nom de la colonne discriminante.

(2)

type (optionnel - par défaut à string) un nom indiquant le type Hibernate.

(3)

force (optionnel - par défaut à false) "oblige" Hibernate à spécifier une valeur discriminante autorisée même quand on récupère toutes les instances de la classe de base.

(4)

insert (optionnel - par défaut à true) à passer à false si la colonne discriminante fait aussi partie d'un identifiant composé mappé (Indique à Hibernate de ne pas inclure la colonne dans les INSERT SQL).

(5)

formula (optionnel) une expression SQL arbitraire qui est exécutée quand un type doit être évalué. Permet la discrimination basée sur le contenu.

Les véritables valeurs de la colonne discriminante sont spécifiées par l'attribut discriminator-value des éléments <class> et <subclass>.

L'attribut force n'est utile que si la table contient des lignes avec des valeurs "extra" discriminantes qui ne sont pas mappées à une classe persistante. Ce ne sera généralement pas le cas.

En utilisant l'attribut formula vous pouvez déclarer une expression SQL arbitraire qui sera utilisée pour évaluer le type d'une ligne :

<discriminator
    formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end"
    type="integer"/>

5.1.7. version (optionnel)

L'élément <version> est optionnel et indique que la table contient des données versionnées. C'est particulièrement utile si vous avez l'intention d'utiliser des transactions longues (voir plus-bas).

<version
        column="version_column"                            (1)
        name="propertyName"                                (2)
        type="typename"                                    (3)
        access="field|property|ClassName"                  (4)
        unsaved-value="null|negative|undefined"            (5)
        generated="never|always"                                     (6)
        insert="true|false"                                          (7)
        node="element-name|@attribute-name|element/@attribute|."
/>
(1)

column (optionnel - par défaut égal au nom de la propriété) : Le nom de la colonne contenant le numéro de version.

(2)

name : Le nom d'un attribut de la classe persistante.

(3)

type (optionnel - par défaut à integer) : Le type du numéro de version.

(4)

access (optionnel - par défaut à property) : La stratégie à utiliser par Hibernate pour accéder à la valeur de la propriété.

(5)

unsaved-value (optionnel - par défaut à undefined) : Une valeur de la propriété d'identifiant qui indique que l'instance est nouvellement instanciée (non sauvegardée), et qui la distingue des instances détachées qui ont été sauvegardées ou chargées dans une session précédente (undefined indique que la valeur de l'atribut identifiant devrait être utilisé).

(6)

generated (optional - défaut à never) : Indique que la valeur de la propriété version est générée par la base de données cf. Section 5.6, « Propriétés générées ».

(7)

insert (optionnel - défaut à true) : Indique si la colonne de version doit être incluse dans les ordres insert. Peut être à false si et seulement si la colonne de la base de données est définie avec une valeur par défaut à 0.

Les numéros de version doivent avoir les types Hibernate long, integer, short, timestamp ou calendar.

Une propriété de version ou un timestamp ne doit jamais être null pour une instance détachée, ainsi Hibernate pourra détecter toute instance ayant une version ou un timestamp null comme transient, quelles que soient les stratégies unsaved-value spécifiées. Déclarer un numéro de version ou un timestamp "nullable" est un moyen pratique d'éviter tout problème avec les réattachements transitifs dans Hibernate, particulièrement utile pour ceux qui utilisent des identifiants assignés ou des clefs composées !

5.1.8. timestamp (optionnel)

L'élément optionnel <timestamp> indique que la table contient des données horodatées (timestamp). Cela sert d'alternative à l'utilisation de numéros de version. Les timestamps (ou horodatage) sont par nature une implémentation moins fiable pour l'optimistic locking. Cependant, l'application peut parfois utiliser l'horodatage à d'autres fins.

<timestamp
        column="timestamp_column"           (1)
        name="propertyName"                 (2)
        access="field|property|ClassName"   (3)
        unsaved-value="null|undefined"      (4)
        source="vm|db"                                               (5)
        generated="never|always"                                     (6)
        node="element-name|@attribute-name|element/@attribute|."
/>
(1)

column (optionnel - par défaut à le nom de la propriété) : Le nom d'une colonne contenant le timestamp.

(2)

name : Le nom d'une propriété au sens JavaBean de type Date ou Timestamp de la classe persistante.

(3)

access (optionnel - par défaut à property) : La stratégie à utiliser par Hibernate pour accéder à la valeur de la propriété.

(4)

unsaved-value (optionnel - par défaut à null) : Propriété dont la valeur est un numéro de version qui indique que l'instance est nouvellement instanciée (non sauvegardée), et qui la distingue des instances détachées qui ont été sauvegardées ou chargées dans une session précédente (undefined indique que la valeur de l'attribut identifiant devrait être utilisée).

(5)

source (optionnel - par défaut à vm) : D'où Hibernate doit-il récupérer la valeur du timestamp? Depuis la base de données ou depuis la JVM d'exécution? Les valeurs de timestamp de la base de données provoquent une surcharge puisque Hibernate doit interroger la base pour déterminer la prochaine valeur mais cela est plus sûr lorsque vous fonctionnez dans un cluster. Remarquez aussi que certains des dialectes ne supportent pas cette fonction, et que d'autres l'implémentent mal, provoquant des erreurs de précision (Oracle 8 par exemple).

(6)

generated (optional - défaut à never) : Indique que la valeur de ce timestamp est générée par la base de données cf. Section 5.6, « Propriétés générées ».

Notez que <timestamp> est équivalent à <version type="timestamp"> et <timestamp source="db"> équivaut à <version type="dbtimestamp">.

5.1.9. property

L'élément <property> déclare une propriété de la classe au sens JavaBean.

<property
        name="propertyName"                 (1)
        column="column_name"                (2)
        type="typename"                     (3)
        update="true|false"                 (4)
        insert="true|false"                 (4)
        formula="arbitrary SQL expression"  (5)
        access="field|property|ClassName"   (6)
        lazy="true|false"                   (7)
        unique="true|false"                 (8)
        not-null="true|false"               (9)
        optimistic-lock="true|false"        (10)
        generated="never|insert|always"                              (11)
        node="element-name|@attribute-name|element/@attribute|."
        index="index_name"
        unique_key="unique_key_id"
        length="L"
        precision="P"
        scale="S"
/>
(1)

name : nom de la propriété, avec une lettre initiale en minuscule.

(2)

column (optionnel - par défaut au nom de la propriété) : le nom de la colonne mappée. Cela peut aussi être indiqué dans le(s) sous-élément(s) <column>.

(3)

type (optionnel) : nom indiquant le type Hibernate.

(4)

update, insert (optionnel - par défaut à true) : indique que les colonnes mappées devraient être incluses dans des UPDATE SQL et/ou des INSERT. Mettre les deux à false empêche la propagation en base de données (utile si vous savez qu'un trigger affectera la valeur à la colonne).

(5)

formula (optionnel) : une expression SQL qui définit la valeur pour une propriété calculée. Les propriétés calculées ne possède pas leur propre mapping.

(6)

access (optionnel - par défaut à property): Stratégie que Hibernate doit utiliser pour accéder à cette valeur.

(7)

lazy (optionnel - par défaut à false): Indique que cette propriété devrait être chargée en différé (lazy loading) quand on accède à la variable d'instance pour la première fois.

(8)

unique (optionnel): Génère le DDL d'une contrainte d'unicité pour les colonnes. Permet aussi d'en faire la cible d'un property-ref.

(9)

not-null (optionnel): Génère le DDL d'une contrainte de non nullité pour les colonnes.

(10)

optimistic-lock (optionnel - par défaut à true): Indique que les mises à jour de cette propriété peuvent ou non nécessiter l'acquisition d'un verrou optimiste. En d'autres termes, cela détermine s'il est nécessaire d'incrémenter un numéro de version quand cette propriété est marquée obsolète (dirty).

(11)

generated (optional - défaut ànever): Indique que la valeur de ce timestamp est générée par la base de données cf. Section 5.6, « Propriétés générées ».

typename peut être:

  1. Nom d'un type basique Hibernate (ex: integer, string, character, date, timestamp, float, binary, serializable, object, blob).

  2. Nom d'une classe Java avec un type basique par défaut (ex: int, float, char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob).

  3. Nom d'une classe Java sérialisable.

  4. Nom d'une classe ayant un type spécifique (ex: com.illflow.type.MyCustomType).

Si vous n'indiquez pas un type, Hibernate utlisera la réflexion sur le nom de la propriété pour tenter de trouver le type Hibernate correct. Hibernate essayera d'interprêter le nom de la classe retournée par le getter de la propriété en utilisant les régles 2, 3, 4 dans cet ordre. Cependant, ce n'est pas toujours suffisant. Dans certains cas vous aurez encore besoin de l'attribut type (Par exemple, pour distinguer Hibernate.DATE et Hibernate.TIMESTAMP, ou pour préciser un type spécifique).

L'attribut access permet de contrôler comment Hibernate accèdera à la propriété à l'exécution. Par défaut, Hibernate utilisera les méthodes set/get. Si vous indiquez access="field", Hibernate ignorera les getter/setter et accèdera à la propriété directement en utilisant la réflexion. Vous pouvez spécifier votre propre stratégie d'accès aux propriété en donnant une classe qui implémente l'interface org.hibernate.property.PropertyAccessor.

Une fonctionnalité particulièrement intéressante est les propriétés dérivées. Ces propriétés sont par définition en lecture seule, la valeur de la propriété est calculée au chargement. Le calcul est déclaré comme une expression SQL, qui se traduit par une sous-requête SELECT dans la requête SQL qui charge une instance :

<property name="totalPrice"
    formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
                WHERE li.productId = p.productId
                AND li.customerId = customerId
                AND li.orderNumber = orderNumber )"/>

Remarquez que vous pouvez référencer la propre table des entités en ne déclarant pas un alias sur une colonne particulière (customerId dans l'exemple donné). Notez aussi que vous pouvez utiliser le sous-élément de mapping <formula> plutôt que d'utiliser l'attribut si vous le souhaitez.

5.1.10. many-to-one

Une association ordinaire vers une autre classe persistante est déclarée en utilisant un élément many-to-one. Le modèle relationnel est une association de type many-to-one : une clef étrangère dans une table référence la ou les clef(s) primaire(s) dans la table cible.

<many-to-one
        name="propertyName"                                (1)
        column="column_name"                               (2)
        class="ClassName"                                  (3)
        cascade="cascade_style"                            (4)
        fetch="join|select"                                (5)
        update="true|false"                                (6)
        insert="true|false"                                (6)
        property-ref="propertyNameFromAssociatedClass"     (7)
        access="field|property|ClassName"                  (8)
        unique="true|false"                                (9)
        not-null="true|false"                              (10)
        optimistic-lock="true|false"                       (11)
        lazy="proxy|no-proxy|false"                        (12)
        not-found="ignore|exception"                       (13)          (14)
        entity-name="EntityName"                                     (15)
        formula="arbitrary SQL expression"                           (16)
        node="element-name|@attribute-name|element/@attribute|."
        embed-xml="true|false"
        index="index_name"
        unique_key="unique_key_id"
        foreign-key="foreign_key_name"
/>
(1)

name : Nom de la propriété.

(2)

column (optionnel) : Le nom de la clef étrangère. Cela peut être aussi indiqué avec le sous-élément <column>.

(3)

class (optionnel - par défaut le type de la propriété déterminé par réflexion) : Le nom de la classe associée.

(4)

cascade (optionnel) : Indique quelles opérations doivent être propagées de l'objet père vers les objets associés.

(5)

fetch (optionnel - par défaut à select) : Choisit entre le chargement de type outer-join ou le chargement par select successifs.

(6)

update, insert (optionnel - par défaut à true) : indique que les colonnes mappées devraient être incluses dans des UPDATE SQL et/ou des INSERT. Mettre les deux à false empêche la propagation en base de données (utile si vous savez qu'un trigger affectera la valeur à la colonne).

(7)

property-ref : (optionnel) Le nom d'une propriété de la classe associée qui est liée à cette clef étrangère. Si ce n'est pas spécifié, la clef primaire de la classe associée est utilisée.

(8)

access (optionnel - par défaut à property) : La stratégie à utiliser par Hibernate pour accéder à la valeur de cette propriété.

(9)

unique (optionnel) : Génère le DDL d'une contrainte d'unicité pour la clef étrangère. Permet aussi d'en faire la cible d'un property-ref. Cela permet de créer une véritable association one-to-one.

(10)

not-null (optionnel) : Génère le DDL pour une contrainte de non nullité pour la clef étrangère.

(11)

optimistic-lock (optionnel - par défaut à true) : Indique que les mises à jour de cette propriété requièrent ou non l'acquisition d'un verrou optimiste. En d'autres termes, détermine si un incrément de version doit avoir lieu quand la propriété est marquée obsolète (dirty).

(12)

lazy (optionnel - par défaut à false) : Indique que cette propriété doit être chargée en différé (lazy loading) au premier accès à la variable d'instance (nécessite une instrumentation du bytecode lors de la phase de construction). Remarquez que cela n'influence pas le comportement du proxy Hibernate - comme l'attribut lazy sur des classes ou des mappings de collections, mais utilise l'interception pour le chargement différé. lazy="false" indique que l'association sera toujours chargée.

(13)

not-found (optionnel - par défaut à exception) : Indique comment les clefs étrangères qui référencent des lignes manquantes doivent être manipulées : ignore traitera une ligne manquante comme une association nulle.

(15)

entity-name (optionnel) : Le nom de l'entité de la classe associée.

(16)

formula (optionnel) : une expression SQL qui définit la valeur pour une clé étrangère calculée.

Donner une valeur significative à l'attribut cascade autre que none propagera certaines opérations à l'objet associé. Les valeurs significatives sont les noms des opérations Hibernate basiques, persist, merge, delete, save-update, evict, replicate, lock, refresh, ainsi que les valeurs spéciales delete-orphan et all et des combinaisons de noms d'opérations séparées par des virgules, comme par exemple cascade="persist,merge,evict" ou cascade="all,delete-orphan". Voir Section 10.11, « Persistance transitive » pour une explication complète. Notez que les assocations many-to-one et one-to-one ne supportent pas orphan delete.

Une déclaration many-to-one typique est aussi simple que :

<many-to-one name="product" class="Product" column="PRODUCT_ID"/>

L'attribut property-ref devrait être utilisé pour mapper seulement des données provenant d'un ancien système où les clefs étrangères font référence à une clef unique de la table associée et qui n'est pas la clef primaire. C'est un cas de mauvaise conception relationnelle. Par exemple, supposez que la classe Product a un numéro de série unique qui n'est pas la clef primaire. (L'attribut unique contrôle la génération DDL par Hibernate avec l'outil SchemaExport.)

<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>

Ainsi le mapping pour OrderItem peut utiliser :

<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>

bien que ce ne soit certainement pas encouragé.

Si la clef unique référencée comprend des propriétés multiples de l'entité associée, vous devez mapper ces propriétés à l'intérieur d'un élément <properties>.

5.1.11. one-to-one

Une association one-to-one vers une autre classe persistante est déclarée avec l'élément one-to-one.

<one-to-one
        name="propertyName"                                (1)
        class="ClassName"                                  (2)
        cascade="cascade_style"                            (3)
        constrained="true|false"                           (4)
        fetch="join|select"                                (5)
        property-ref="propertyNameFromAssociatedClass"     (6)
        access="field|property|ClassName"                  (7)
        formula="any SQL expression"                       (8)
        entity-name="EntityName"                                     (9)
/>
(1)

name : Le nom de la propriété.

(2)

class (optionnel - par défaut du type de la propriété déterminé par réflexion) : Le nom de la classe associée.

(3)

cascade (optionnel) : Indique quelles opérations doivent être cascadées de l'objet père vers l'objet associé.

(4)

constrained (optionnel) : Indique qu'une contrainte de clef étrangère sur la clef primaire de la table mappée référence la table de la classe associée. Cette option affecte l'ordre dans lequel chaque save() et chaque delete() sont cascadés et détermine si l'association peut utiliser un proxy (aussi utilisé par l'outil d'export de schéma).

(5)

fetch (optionnel - par défaut à select) : Choisit entre récupération par jointure externe ou select séquentiel.

(6)

property-ref (optionnel) : Le nom de la propriété de la classe associée qui est jointe à la clef primaire de cette classe. Si ce n'est pas spécifié, la clef primaire de la classe associée est utilisée.

(7)

access (optionnel - par défaut à property) : La stratégie à utiliser par Hibernate pour accéder à la valeur de la propriété.

(8)

formula (optionnel) : Presque toutes les associations one-to-one pointent sur la clef primaire de l'entité propriétaire. Dans les rares cas différents, vous devez donner une ou plusieurs autres colonnes ou expression à joindre par une formule SQL (voir org.hibernate.test.onetooneformula pour un exemple).

(9)

lazy (optionnel - par défaut proxy) : Par défaut, les associations simples sont soumise à proxy. lazy="no-proxy" spécifie que la propriété doit être chargée à la demande au premier accès à l'instance. (nécessite l'intrumentation du bytecode à la construction). lazy="false" indique que l'association sera toujours chargée agressivement. Notez que si constrained="false", l'utilisation de proxy est impossible et Hibernate chargera automatiquement l'association !

(10)

entity-name (optional) : The entity name of the associated class.

Il existe deux types d'associations one-to-one :

  • associations par clef primaire

  • association par clef étrangère unique

Les associations par clef primaire ne nécessitent pas une colonne supplémentaire en table ; si deux lignes sont liés par l'association alors les deux lignes de la table partagent la même valeur de clef primaire. Donc si vous voulez que deux objets soient liés par une association par clef primaire, vous devez faire en sorte qu'on leur assigne la même valeur d'identifiant !

Pour une association par clef primaire, ajoutez les mappings suivants à Employee et Person, respectivement.

<one-to-one name="person" class="Person"/>
<one-to-one name="employee" class="Employee" constrained="true"/>

Maintenant, vous devez faire en sorte que les clefs primaires des lignes liées dans les tables PERSON et EMPLOYEE sont égales. On utilise une stratégie Hibernate spéciale de génération d'identifiants appelée foreign :

<class name="person" table="PERSON">
    <id name="id" column="PERSON_ID">
        <generator class="foreign">
            <param name="property">employee</param>
        </generator>
    </id>
    ...
    <one-to-one name="employee"
        class="Employee"
        constrained="true"/>
</class>

Une instance fraîchement enregistrée de Person se voit alors assignée la même valeur de clef primaire que l'instance de Employee référencée par la propriété employee de cette Person.

Alternativement, une clef étrangère avec contrainte d'unicité de Employee vers Person peut être indiquée ainsi :

<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>

Et cette association peut être rendue bidirectionnelle en ajoutant ceci au mapping de Person :

<one-to-one name"employee" class="Employee" property-ref="person"/>

5.1.12. natural-id

<natural-id mutable="true|false"/>
        <property ... />
        <many-to-one ... />
        ......
</natural-id>

Bien que nous recommandions l'utilisation de clé primaire générée, vous devriez toujours essayer d'identifier des clé métier (naturelles) pour toutes vos entités. Une clé naturelle est une propriété ou une combinaison de propriétés uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé naturelle dans l'élément <natural-id>. Hibernate générera la clé unique nécessaire et les contraintes de non-nullité, et votre mapping s'auto-documentera.

Nous vous recommandons fortement d'implémenter equals() et hashCode() pour comparer les clés naturelles de l'entité.

Ce mapping n'est pas destiné à être utilisé avec des entités qui ont des clés naturelles.

  • mutable (optionel, par défaut à false) : Par défaut, les identifiants naturels sont supposés être immuable (constants).

5.1.13. component, dynamic-component

L'élément <component> mappe les propriétés d'un objet fils aux colonnes d'une classe parente. Les composants peuvent en retour déclarer leurs propres propriétés, composants ou collections. Voir "Components" plus bas.

<component 
        name="propertyName"                 (1)
        class="className"                   (2)
        insert="true|false"                 (3)
        update="true|false"                 (4)
        access="field|property|ClassName"   (5)
        lazy="true|false"                   (6)
        optimistic-lock="true|false"        (7)
        unique="true|false"                 (8)
>
        
        <property ...../>
        <many-to-one .... />
        ........
</component>
(1)

name : Nom de la propriété

(2)

class (optionnel - par défaut au type de la propriété déterminé par réflexion) : le nom de la classe (fille) du composant.

(3)

insert : Est ce que les colonnes mappées apparaissent dans les INSERTs ?

(4)

update: Est ce que les colonnes mappées apparaissent dans les UPDATEs ?

(5)

access (optionnel - par défaut à property) : La stratégie que Hibernate doit utiliser pour accéder à la valeur de cette propriété.

(6)

lazy (optionnel - par défaut à false) : Indique que ce composant doit être chargé au premier accès à la variable d'instance (nécessite une instrumentation du bytecode au moment du build).

(7)

optimistic-lock (optionnel - par défaut à true) : Indique que les mises à jour sur ce composant nécessitent ou non l'acquisition d'un verrou optimiste. En d'autres termes, cela détermine si une incrémentation de version doit avoir lieu quand la propriété est marquée obsolète (dirty).

(8)

unique (optionnel - par défaut à false) : Indique qu'une contrainte d'unicité existe sur toutes les colonnes mappées de ce composant.

Les tags fils <property> mappent les propriétés de la classe fille sur les colonnes de la table.

L'élément <component> permet de déclarer sous-élément <parent> qui associe une propriété de la classe composant comme une référence arrière vers l'entité contenante.

L'élément <dynamic-component> permet à une Map d'être mappée comme un composant, quand les noms de la propriété font référence aux clefs de cette Map, voir Section 8.5, « Composant Dynamique ».

5.1.14. properties

L'élément <properties> permet la définition d'un groupement logique nommé des propriétés d'une classe. L'utilisation la plus importante de cette construction est la possibilité pour une combinaison de propriétés d'être la cible d'un property-ref. C'est aussi un moyen pratique de définir une contrainte d'unicité multi-colonnes.

<properties 
        name="logicalName"                  (1)
        insert="true|false"                 (2)
        update="true|false"                 (3)
        optimistic-lock="true|false"        (4)
        unique="true|false"                 (5)
>
        
        <property ...../>
        <many-to-one .... />
        ........
</properties>
(1)

name : Le nom logique d'un regroupement et non le véritable nom d'une propriété.

(2)

insert : Est-ce que les colonnes mappées apparaissent dans les INSERTs ?

(3)

update : Est-ce que les colonnes mappées apparaissent dans les UPDATEs ?

(4)

optimistic-lock (optionnel - par défaut à true) : Indique que les mises à jour sur ce composant nécessitent ou non l'acquisition d'un verrou optimiste. En d'autres termes, cela détermine si une incrémentation de version doit avoir lieu quand la propriété est marquée obsolète (dirty).

(5)

unique (optionnel - par défaut à false) : Indique qu'une contrainte d'unicité existe sur toutes les colonnes mappées de ce composant.

Par exemple, si nous avons le mapping de <properties> suivant :

<class name="Person">
    <id name="personNumber"/>
    ...
    <properties name="name" 
            unique="true" update="false">
        <property name="firstName"/>
        <property name="initial"/>
        <property name="lastName"/>
    </properties>
</class>

Alors nous pourrions avoir une association sur des données d'un ancien système (legacy) qui font référence à cette clef unique de la table Person au lieu de la clef primaire :

<many-to-one name="person" 
         class="Person" property-ref="name">
    <column name="firstName"/>
    <column name="initial"/>
    <column name="lastName"/>
</many-to-one>

Nous ne recommandons pas l'utilisation de ce genre de chose en dehors du contexte de mapping de données héritées d'anciens systèmes.

5.1.15. subclass

Pour finir, la persistance polymorphique nécessite la déclaration de chaque sous-classe de la classe persistante de base. pour la stratégie de mapping de type table-per-class-hierarchy, on utilise la déclaration <subclass>.

<subclass
        name="ClassName"                              (1)
        discriminator-value="discriminator_value"     (2)
        proxy="ProxyInterface"                        (3)
        lazy="true|false"                             (4)
        dynamic-update="true|false"
        dynamic-insert="true|false"
        entity-name="EntityName"
        node="element-name"
        extends="SuperclassName">

        <property .... />
        .....
</subclass>
(1)

name : Le nom complet de la sous-classe.

(2)

discriminator-value (optionnel - par défaut le nom de la classe) : une valeur qui distingue les différentes sous-classes.

(3)

proxy (optionnel) : Indique une classe ou interface à utiliser pour les chargements à la demande des proxies (lazy).

(4)

lazy (optionnel, par défaut à true) : Spécifier lazy="false" désactive l'utilisation du chargement à la demande (lazy).

Chaque sous-classe devrait déclarer ses propres propriétés persistantes et sous-classes. Les propriétés <version> et <id> sont implicitement hérités de la classe de base. Chaque sous-classe dans une hiérarchie doit définir une unique discriminator-value. Si aucune n'est spécifiée, le nom complet de la classe Java est utilisé.

Pour plus d'infos sur le mapping d'héritage, voir Chapitre 9, Mapping d'héritage de classe.

<hibernate-mapping>
    <subclass name="DomesticCat" extends="Cat" discriminator-value="D">
         <property name="name" type="string"/>
    </subclass>
</hibernate-mapping>

Pour des informations sur les mappings d'héritage, voir Chapitre 9, Mapping d'héritage de classe.

5.1.16. joined-subclass

Une autre façon possible de faire est la suivante, chaque sous-classe peut être mappée vers sa propre table (stratégie de mapping de type table-per-subclass). L'état hérité est récupéré en joignant la table de la super-classe. L'élément <joined-subclass> est utilisé.

<joined-subclass
        name="ClassName"                    (1)
        table="tablename"                   (2)
        proxy="ProxyInterface"              (3)
        lazy="true|false"                   (4)
        dynamic-update="true|false"
        dynamic-insert="true|false"
        schema="schema"
        catalog="catalog"
        extends="SuperclassName"
        persister="ClassName"
        subselect="SQL expression"
        entity-name="EntityName">

        <key .... >

        <property .... />
        .....
</joined-subclass>
(1)

name : Le nom Java complet de la sous-classe.

(2)

table : Le nom de la table de la sous-classe.

(3)

proxy (optionnel) : Indique une classe ou interface pour le chargement différé des proxies.

(4)

lazy (optionnel, par défaut à true) : Indiquer lazy="false" désactive l'utilisation du chargement à la demande.

Aucune colonne discriminante n'est nécessaire pour cette stratégie de mapping. Cependant, chaque sous-classe doit déclarer une colonne de table contenant l'objet identifiant qui utilise l'élément <key>. Le mapping au début de ce chapitre serait ré-écrit ainsi :

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="eg">

        <class name="Cat" table="CATS">
                <id name="id" column="uid" type="long">
                        <generator class="hilo"/>
                </id>
                <property name="birthdate" type="date"/>
                <property name="color" not-null="true"/>
                <property name="sex" not-null="true"/>
                <property name="weight"/>
                <many-to-one name="mate"/>
                <set name="kittens">
                        <key column="MOTHER"/>
                        <one-to-many class="Cat"/>
                </set>
                <joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
                    <key column="CAT"/>
                    <property name="name" type="string"/>
                </joined-subclass>
        </class>

        <class name="eg.Dog">
                <!-- mapping for Dog could go here -->
        </class>

</hibernate-mapping>

Pour des informations sur les mappings d'héritage, voir Chapitre 9, Mapping d'héritage de classe.

5.1.17. union-subclass

Une troisième option est de seulement mapper vers des tables les classes concrètes d'une hiérarchie d'héritage, (stratégie de type table-per-concrete-class) où chaque table définit tous les états persistants de la classe, y compris les états hérités. Dans Hibernate il n'est absolument pas nécessaire de mapper explicitement de telles hiérarchies d'héritage. Vous pouvez simplement mapper chaque classe avec une déclaration <class> différente. Cependant, si vous souhaitez utiliser des associations polymorphiques (càd une association vers la superclasse de la hiérarchie), vous devez utiliser le mapping <union-subclass>.

<union-subclass
        name="ClassName"                    (1)
        table="tablename"                   (2)
        proxy="ProxyInterface"              (3)
        lazy="true|false"                   (4)
        dynamic-update="true|false"
        dynamic-insert="true|false"
        schema="schema"
        catalog="catalog"
        extends="SuperclassName"
        abstract="true|false"
        persister="ClassName"
        subselect="SQL expression"
        entity-name="EntityName">

        <property .... />
        .....
</union-subclass>
(1)

name : Le nom Java complet de la sous-classe.

(2)

table : nom de la table de la sous-classe.

(3)

proxy (optionnel) : Indique une classe ou interface pour le chargement différé des proxies.

(4)

lazy (optionnel, par défaut à true) : Indiquer lazy="false" désactive l'utilisation du chargement à la demande.

Aucune colonne discriminante ou colonne clef n'est requise pour cette stratégie de mapping.

Pour des informations sur les mappings d'héritage, voir Chapitre 9, Mapping d'héritage de classe.

5.1.18. join

En utilisant l'élément <join>, il est possible de mapper des propriétés d'une classe sur plusieurs tables.

<join
        table="tablename"                        (1)
        schema="owner"                           (2)
        catalog="catalog"                        (3)
        fetch="join|select"                      (4)
        inverse="true|false"                     (5)
        optionnel="true|false">                  (6)
        
        <key ... />
        
        <property ... />
        ...
</join>
(1)

table : Le nom de la table jointe.

(2)

schema (optionnel) : court-circuite le nom de schéma spécifié par l'élément de base <hibernate-mapping>.

(3)

catalog (optionnel) : court-circuite le nom de catalogue spécifié par l'élément de base <hibernate-mapping>.

(4)

fetch (optionnel - par défaut à join) : Si positionné à join, Hibernate utilisera une jointure interne pour charger une jointure définie par une classe ou ses super-classes et une jointure externe pour une <jointure> définie par une sous-classe. Si positionné à select alors Hibernate utilisera un select séquentiel pour une <jointure> définie sur une sous-classe, qui ne sera délivrée que si une ligne se représente une instance de la sous-classe. Les jointures internes seront quand même utilisées pour charger une <jointure> définie par une classe et ses super-classes.

(5)

inverse (optionnel - par défaut à false) : Si positionné à true, Hibernate n'essaiera pas d'insérer ou de mettre à jour les propriétés définies par cette jointure.

(6)

optionnel (optionnel - par défaut à false) : Si positionné à true, Hibernate insèrera une ligne seulement si les propriétés définies par cette jointure sont non-nulles et utilisera toujours une jointure externe pour charger les propriétés.

Par exemple, les informations d'adresse pour une personne peuvent être mappées vers une table séparée (tout en préservant des sémantiques de type valeur pour toutes ses propriétés) :

<class name="Person"
    table="PERSON">

    <id name="id" column="PERSON_ID">...</id>

    <join table="ADDRESS">
        <key column="ADDRESS_ID"/>
        <property name="address"/>
        <property name="zip"/>
        <property name="country"/>
    </join>
    ...

Cette fonctionnalité est souvent seulement utile pour les modèles de données hérités d'anciens systèmes (legacy), nous recommandons d'utiliser moins de tables que de classes et un modèle de domaine à granularité fine. Cependant, c'est utile pour passer d'une stratégie de mapping d'héritage à une autre dans une hiérarchie simple ainsi qu'il est expliqué plus tard.

5.1.19. key

Nous avons rencontré l'élément <key> à plusieurs reprises maintenant. Il apparaît partout que l'élément de mapping parent définit une jointure sur une nouvele table, et définit la clef étrangère dans la table jointe, ce qui référence la clef primaire de la table d'origine.

<key
        column="columnname"                      (1)
        on-delete="noaction|cascade"             (2)
        property-ref="propertyName"              (3)
        not-null="true|false"                    (4)
        update="true|false"                      (5)
        unique="true|false"                      (6)
/>
(1)

column (optionnel) : Le nom de la colonne de la clef étrangère Cela peut aussi être spécifié par l'élément(s) intégré(s) <column>.

(2)

on-delete (optionnel, par défaut à noaction) : Indique si la contrainte de clef étrangère possède la possibilité au niveau base de données de suppression en cascade.

(3)

property-ref (optionnel) : Indique que la clef étrangère fait référence à des colonnes qui ne sont pas la clef primaire de la table d'origine (Pour les données de systèmes legacy).

(4)

not-null (optionnel) : Indique que les colonnes des clefs étrangères ne peuvent pas être nulles (c'est implicite si la clef étrangère fait partie de la clef primaire).

(5)

update (optionnel) : Indique que la clef étrangère ne devrait jamais être mise à jour (implicite si celle-ci fait partie de la clef primaire).

(6)

unique (optionnel) : Indique que la clef étrangère doit posséder une contrainte d'unicité (implicite si la clef étrangère est aussi la clef primaire).

Nous recommandons pour les systèmes où les suppressions doivent être performantes de définir toutes les clefs on-delete="cascade", ainsi Hibernate utilisera une contrainte ON CASCADE DELETE au niveau base de données, plutôt que de nombreux DELETE individuels. Attention, cette fonctionnalité court-circuite la stratégie habituelle de verrou optimiste pour les données versionnées.

Les attributs not-null et update sont utiles pour mapper une association one-to-many unidirectionnelle. Si vous mappez un one-to-many unidirectionnel vers une clef étrangère non nulle, vous devez déclarer la colonne de la clef en utilisant <key not-null="true">.

5.1.20. éléments column et formula

Tout élément de mapping qui accepte un attribut column acceptera alternativement un sous-élément <column>. De façon identique, <formula> est une alternative à l'attribut formula.

<column
        name="column_name"
        length="N"
        precision="N"
        scale="N"
        not-null="true|false"
        unique="true|false"
        unique-key="multicolumn_unique_key_name"
        index="index_name"
        sql-type="sql_type_name"
        check="SQL expression"/>
<formula>SQL expression</formula>

Les attributs column et formula peuvent même être combinés au sein d'une même propriété ou mapping d'association pour exprimer, par exemple, des conditions de jointure exotiques.

<many-to-one name="homeAddress" class="Address"
        insert="false" update="false">
    <column name="person_id" not-null="true" length="10"/>
    <formula>'MAILING'</formula>
</many-to-one>

5.1.21. import

Supposez que votre application possède deux classes persistantes du même nom, et vous ne voulez pas préciser le nom Java complet (packages inclus) dans les queries Hibernate. Les classes peuvent alors être "importées" explicitement plutôt que de compter sur auto-import="true".Vous pouvez même importer des classes et interfaces qui ne sont pas mappées explicitement.

<import class="java.lang.Object" rename="Universe"/>
<import
        class="ClassName"              (1)
        rename="ShortName"             (2)
/>
(1)

class : Nom Java complet de la classe.

(2)

rename (optionnel - par défaut vaut le nom de la classe Java (sans package)) : Nom pouvant être utilisé dans le langage de requête.

5.1.22. any

Il existe encore un type de mapping de propriété. L'élément de mapping <any> définit une association polymorphique vers des classes de tables multiples. Ce type de mapping requiert toujours plus d'une colonne. La première colonne contient le type de l'entité associée. Les colonnes restantes contiennent l'identifiant. il est impossible de spécifier une contrainte de clef étrangère pour ce type d'association, donc ce n'est certainement pas considéré comme le moyen habituel de mapper des associations (polymorphiques). Vous devriez utiliser cela uniquement dans des cas particuliers (par exemple des logs d'audit, des données de session utilisateur, etc...).

L'attribut meta-type permet à l'application de spécifier un type personnalisé qui mappe des valeurs de colonnes de le base de données sur des classes persistantes qui ont un attribut identifiant du type spécifié par id-type. Vous devez spécifier le mapping à partir de valeurs du méta-type sur les noms des classes.

<any name="being" id-type="long" meta-type="string">
    <meta-value value="TBL_ANIMAL" class="Animal"/>
    <meta-value value="TBL_HUMAN" class="Human"/>
    <meta-value value="TBL_ALIEN" class="Alien"/>
    <column name="table_name"/>
    <column name="id"/>
</any>
<any
        name="propertyName"                      (1)
        id-type="idtypename"                     (2)
        meta-type="metatypename"                 (3)
        cascade="cascade_style"                  (4)
        access="field|property|ClassName"        (5)
        optimistic-lock="true|false"             (6)
>
        <meta-value ... />
        <meta-value ... />
        .....
        <column .... />
        <column .... />
        .....
</any>
(1)

name : le nom de la propriété.

(2)

id-type : le type identifiant.

(3)

meta-type (optionnel - par défaut à string) : Tout type permis pour un mapping par discriminateur.

(4)

cascade (optionnel - par défaut à none) : le style de cascade.

(5)

access (optionnel - par défaut à property) : La stratégie à utiliser par Hibernate pour accéder à cette propriété.

(6)

optimistic-lock (optionnel - par défaut à true) : Indique que les mises à jour sur cette propriété nécessitent ou non l'acquisition d'un verrou optimiste. En d'autres termes, définit si un incrément de version doit avoir lieu quand cette propriété est marquée dirty.

5.2. Hibernate Types

5.2.1. Entités et valeurs

Pour comprendre le comportement des différents objets Java par rapport au service de persistance, nous avons besoin de les classer en deux groupes :

Une entité existe indépendamment de tout autre objet possédant une référence vers l'entité. Comparez cela avec le modèle Java habituel où un objet est supprimé par le garbage collector dès qu'il n'est plus référencé. Les entités doivent être explicitement enregistrées et supprimées (sauf dans les cas où sauvegardes et suppressions sont cascadées d'une entité mère vers ses enfants). C'est différent du modèle ODMG de persistance par atteignabilité - et correspond mieux à la façon dont les objets sont habituellement utilisés dans des grands systèmes. Les entités permettent les références circulaires et partagées. Elles peuvent aussi être versionnées.

L'état persistant d'une entité consiste en des références vers d'autres entités et instances de types valeurs. Ces valeurs sont des types primitifs, des collections (et non le contenu d'une collection), des composants de certains objets immuables. Contrairement aux entités, les valeurs (et en particulier les collections et composants) sont persistés par atteignabiliité. Comme les valeurs (et types primitifs) sont persistés et supprimés avec l'entité qui les contient, ils ne peuvent pas posséder leurs propres versions. Les valeurs n'ont pas d'identité indépendantes, ainsi elles ne peuvent pas être partagées par deux entités ou collections.

Jusqu'à présent nous avons utilisé le terme "classe persistante" pour parler d'entités. Nous allons continuer à faire ainsi. Cependant, au sens strict, toutes les classes définies par un utilisateur possédant un état persistant ne sont pas des entités. Un composant est une classe définie par un utilisateur avec les caractéristiques d'une valeur. Une propriété Java de type java.lang.String a aussi les caractéristiques d'une valeur. Given this definition, we can say that all types (classes) provided by the JDK have value type semantics in Java, while user-defined types may be mapped with entity or value type semantics. This decision is up to the application developer. A good hint for an entity class in a domain model are shared references to a single instance of that class, while composition or aggregation usually translates to a value type.

Nous nous pencherons sur ces deux concepts tout au long de la documentation.

Le défi est de mapper les type Javas (et la définition des développeurs des entités et valeurs types) sur les types du SQL ou des bases de données. Le pont entre les deux systèmes est proposé par Hibernate : pour les entités nous utilisons <class>, <subclass> et ainsi de suite. Pour les types valeurs nous utilisons <property>, <component>, etc., habituellement avec un attribut type. La valeur de cet attribut est le nom d'un type de mapping Hibernate. Hibernate propose de base de nombreux mappings (pour les types de valeurs standards du JDK). Vous pouvez écrire vos propres types de mappings et implémenter aussi vos propres stratégies de conversion, nous le verrons plus tard.

Tous les types proposés de base par Hibernate à part les collections autorisent la valeur null.

5.2.2. Basic value types

Les types basiques de mapping proposés de base peuvent grossièrement être rangés dans les catégories suivantes :

integer, long, short, float, double, character, byte, boolean, yes_no, true_false

Les mappings de type des primitives Java ou leurs classes wrappers (ex: Integer pour int) vers les types SQL (propriétaires) appropriés. boolean, yes_noet true_false sont tous des alternatives pour les types Java boolean ou java.lang.Boolean.

string

Mapping de type de java.lang.String vers VARCHAR (ou le VARCHAR2 Oracle).

date, time, timestamp

Mappings de type pour java.util.Date et ses sous-classes vers les types SQL DATE, TIME et TIMESTAMP (ou équivalent).

calendar, calendar_date

Mappings de type pour java.util.Calendar vers les types SQL TIMESTAMP et DATE (ou équivalent).

big_decimal, big_integer

Mappings de type pour java.math.BigDecimal et java.math.BigInteger vers NUMERIC (ou le NUMBER Oracle).

locale, timezone, currency

Mappings de type pour java.util.Locale, java.util.TimeZone et java.util.Currency vers VARCHAR (ou le VARCHAR2 Oracle). Les instances de Locale et Currency sont mappées sur leurs codes ISO. Les instances de TimeZone sont mappées sur leur ID.

class

Un type de mapping pour java.lang.Class vers VARCHAR (ou le VARCHAR2 Oracle). Un objet Class est mappé sur son nom Java complet.

binary

Mappe les tableaux de bytes vers le type binaire SQL approprié.

text

Mappe les longues chaînes de caractères Java vers les types SQL CLOB ou TEXT.

serializable

Mappe les types Java sérialisables vers le type SQL binaire approprié. Vous pouvez aussi indiquer le type Hibernate serializable avec le nom d'une classe Java sérialisable ou une interface qui ne soit pas par défaut un type de base.

clob, blob

Mappings de type pour les classes JDBC java.sql.Clob and java.sql.Blob. Ces types peuvent ne pas convenir pour certaines applications car un objet blob ou clob peut ne pas être réutilisable en dehors d'une transaction (de plus l'implémentation par les pilotes est moyennement bonne).

imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date, imm_serializable, imm_binary

Mappings de type pour ceux qui sont habituellement modifiable, pour lesquels Hibernate effectue certains optimisations convenant seulement aux types Java immuables, et l'application les traite comme immuable. Par exemple, vous ne devriez pas appeler Date.setTime() sur une instance mappée sur un imm_timestamp. Pour changer la valeur de la propriété, et faire que cette modification soit persistée, l'application doit assigner un nouvel (non identique) objet à la propriété.

Les identifiants uniques des entités et collections peuvent être de n'importe quel type de base excepté binary, blob et clob (les identifiants composites sont aussi permis, voir plus bas).

Les types de base des valeurs ont des Type constants correspondants définis dans org.hibernate.Hibernate. Par exemple, Hibernate.STRING représenté le type string.

5.2.3. Types de valeur définis par l'utilisateur

Il est assez facile pour les développeurs de créer leurs propres types de valeurs. Par exemple, vous pourriez vouloir persister des propriétés du type java.lang.BigInteger dans des colonnnes VARCHAR. Hibernate ne procure pas par défaut un type pour cela. Mais les types que vous pouvez créer ne se limitent pas à mapper des propriétés (ou élément collection) à une simple colonne d'une table. Donc, par exemple, vous pourriez avoir une propriété Java getName()/setName() de type java.lang.String persistée dans les colonnes FIRST_NAME, INITIAL, SURNAME.

Pour implémenter votre propre type, vous pouvez soit implémenter org.hibernate.UserType soit org.hibernate.CompositeUserType et déclarer des propriétés utilisant des noms de classes complets du type. Regardez org.hibernate.test.DoubleStringType pour voir ce qu'il est possible de faire.

<property name="twoStrings" type="org.hibernate.test.DoubleStringType">
    <column name="first_string"/>
    <column name="second_string"/>
</property>

Remarquez l'utilisation des tags <column> pour mapper une propriété sur des colonnes multiples.

Les interfaces CompositeUserType, EnhancedUserType, UserCollectionType, et UserVersionType permettent des utilisations plus spécialisées.

Vous pouvez même donner des paramètres en indiquant UserType dans le fichier de mapping ; Pour cela, votre UserType doit implémenter l'interface org.hibernate.usertype.ParameterizedType. Pour spécifier des paramètres dans votre type propre, vous pouvez utiliser l'élément <type> dans vos fichiers de mapping.

<property name="priority">
    <type name="com.mycompany.usertypes.DefaultValueIntegerType">
        <param name="default">0</param>
    </type>
</property>

Le UserType permet maintenant de récupérer la valeur pour le paramètre nommé default à partir de l'objet Properties qui lui est passé.

Si vous utilisez fréquemment un UserType, cela peut être utile de lui définir un nom plus court. Vous pouvez faire cela en utilisant l'élément <typedef>. Les typedefs permettent d'assigner un nom à votre type propre et peuvent aussi contenir une liste de valeurs de paramètres par défaut si ce type est paramétré.

<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero">
    <param name="default">0</param>
</typedef>
<property name="priority" type="default_zero"/>

Il est aussi possible de redéfinir les paramètres par défaut du typedef au cas par cas en utilisant des paramètres type sur le mapping de la propriété.

Bien que le fait que Hibernate propose de base une riche variété de types, et qu'il supporte les composants signifie que vous aurez très rarement besoin d'utiliser un nouveau type propre, il est néanmoins de bonne pratique d'utiliser des types propres pour les classes (non entités) qui apparaissent fréquemment dans votre application. Par exemple une classe MonetaryAmount est un bon candidat pour un CompositeUserType même s'il pourrait facilement être mappé comme un composant. Une motivation pour cela est l'abstraction. Avec un type propre vos documents de mapping sont à l'abri des changements futurs dans votre façon de représenter des valeurs monétaires.

5.3. Mapper une classe plus d'une fois

Il est possible de proposer plus d'un mapping par classe persistante. Dans ce cas, vous devez spécifier un nom d'entité pour lever l'ambiguité entre les instances des entités mappées (par défaut, le nom de l'entité est celui de la classe). Hibernate vous permet de spécifier le nom de l'entité lorsque vous utilisez des objets persistants, lorsque vous écrivez des requêtes ou quand vous mappez des associations vers les entités nommées.

<class name="Contract" table="Contracts" 
        entity-name="CurrentContract">
    ...
    <set name="history" inverse="true" 
            order-by="effectiveEndDate desc">
        <key column="currentContractId"/>
        <one-to-many entity-name="HistoricalContract"/>
    </set>
</class>

<class name="Contract" table="ContractHistory" 
        entity-name="HistoricalContract">
    ...
    <many-to-one name="currentContract" 
            column="currentContractId" 
            entity-name="CurrentContract"/>
</class>

Remarquez comment les associations sont désormais spécifiées en utilisant entity-name au lieu de class.

5.4. SQL quoted identifiers

Vous pouvez forcer Hibernate à mettre un identifiant entre quotes dans le SQL généré en mettant le nom de la table ou de la colonne entre backticks dans le document de mapping. Hibernate utilisera les bons styles de quotes pour le Dialect SQL (habituellement des doubles quotes, mais des parenthèses pour SQL server et des backticks pour MySQL).

<class name="LineItem" table="`Line Item`">
    <id name="id" column="`Item Id`"/><generator class="assigned"/></id>
    <property name="itemNumber" column="`Item #`"/>
    ...
</class>

5.5. alternatives Metadata

XML ne convient pas à tout le monde, il y a donc des moyens alternatifs pour définir des metatda de mappings O/R dans Hibernate.

5.5.1. utilisation de XDoclet

De nombreux utilisateurs de Hibernate préfèrent embarquer les informations de mappings directement au sein du code source en utilisant les tags XDoclet @hibernate.tags. Nous ne couvrons pas cette approche dans ce document cependant, puisque c'est considéré comme faisant partie de XDoclet. Cependant, nous présentons l'exemple suivant de la classe Cat avec des mappings XDoclet.

package eg;
import java.util.Set;
import java.util.Date;

/**
 * @hibernate.class
 *  table="CATS"
 */
public class Cat {
    private Long id; // identifier
    private Date birthdate;
    private Cat mother;
    private Set kittens
    private Color color;
    private char sex;
    private float weight;

    /*
     * @hibernate.id
     *  generator-class="native"
     *  column="CAT_ID"
     */
    public Long getId() {
        return id;
    }
    private void setId(Long id) {
        this.id=id;
    }

    /**
     * @hibernate.many-to-one
     *  column="PARENT_ID"
     */
    public Cat getMother() {
        return mother;
    }
    void setMother(Cat mother) {
        this.mother = mother;
    }

    /**
     * @hibernate.property
     *  column="BIRTH_DATE"
     */
    public Date getBirthdate() {
        return birthdate;
    }
    void setBirthdate(Date date) {
        birthdate = date;
    }
    /**
     * @hibernate.property
     *  column="WEIGHT"
     */
    public float getWeight() {
        return weight;
    }
    void setWeight(float weight) {
        this.weight = weight;
    }

    /**
     * @hibernate.property
     *  column="COLOR"
     *  not-null="true"
     */
    public Color getColor() {
        return color;
    }
    void setColor(Color color) {
        this.color = color;
    }
    /**
     * @hibernate.set
     *  inverse="true"
     *  order-by="BIRTH_DATE"
     * @hibernate.collection-key
     *  column="PARENT_ID"
     * @hibernate.collection-one-to-many
     */
    public Set getKittens() {
        return kittens;
    }
    void setKittens(Set kittens) {
        this.kittens = kittens;
    }
    // addKitten not needed by Hibernate
    public void addKitten(Cat kitten) {
        kittens.add(kitten);
    }

    /**
     * @hibernate.property
     *  column="SEX"
     *  not-null="true"
     *  update="false"
     */
    public char getSex() {
        return sex;
    }
    void setSex(char sex) {
        this.sex=sex;
    }
}

Voyez le site web de Hibernate pour plus d'exemples sur XDoclet et Hibernate.

5.5.2. Utilisation des annotations JDK 5.0

Le JDK 5.0 introduit des annotations proches de celles de XDoclet au niveau java, qui sont type-safe et vérifiées à la compilation. Ce mécanisme est plus puissant que XDoclet et mieux supporté par les outils et IDE. IntelliJ IDEA, par exemple, supporte l'auto-complétion et le surlignement syntaxique des annotations JDK 5.0. La nouvelle révision des spécifications des EJB (JSR-220) utilise les annotations JDK 5.0 comme mécanisme primaire pour les meta-données des beans entités. Hibernate3 implémente l'EntityManager de la JSR-220 (API de persistance), le support du mapping de meta-données est disponible via le package Hibernate Annotations, en tant que module séparé à télécharger. EJB3 (JSR-220) et les métadata Hibernate3 sont supportés.

Ceci est un exemple d'une classe POJO annotée comme un EJB entité :

@Entity(access = AccessType.FIELD)
public class Customer implements Serializable {

    @Id;
    Long id;

    String firstName;
    String lastName;
    Date birthday;

    @Transient
    Integer age;

    @Embedded
    private Address homeAddress;

    @OneToMany(cascade=CascadeType.ALL)
    @JoinColumn(name="CUSTOMER_ID")
    Set<Order> orders;

    // Getter/setter and business methods
}

Notez que le support des annotations JDK 5.0 (et de la JSR-220) est encore en cours et n'est pas terminé. Référez vous au module Hibernate Annotation pour plus de détails.

5.6. Propriétés générées

Les propriétés générées sont des propriétés dont les valeurs sont générées par la base de données. Typiquement, les applications Hibernate avaient besoin d'invoquer refresh sur les instances qui contenaient des propriétés pour lesquelles la base de données générait des valeurs. Marquer les propriétés comme générées permet à l'application de déléguer cette responsabilité à Hibernate. Principalement, à chaque fois qu'Hibernate réalise une insertion ou une mise à jour en base de données pour une entité marquée comme telle, cela provoque immédiatement un select pour récupérer les valeurs générées.

Les propriétés marquées comme générées doivent de plus ne pas être insérables et modifiables Seuls Section 5.1.7, « version (optionnel) », Section 5.1.8, « timestamp (optionnel) », et Section 5.1.9, « property » peuvent être marqués comme générées.

never (par défaut) - indique la valeur de la propriété n'est pas générée dans la base de données.

insert - indique que la valeur de la propriété donnée est générée à l'insertion mais pas lors des futures mises à jour de l'enregistrement. Les colonnes de type "date de création" sont le cas d'utilisation typique de cette option. Notez que même les propriétés Section 5.1.7, « version (optionnel) » et Section 5.1.8, « timestamp (optionnel) » peuvent être déclarées comme générées, cette option n'est pas disponible à cet endroit...

always - indique que la valeur de la propriété est générée à l'insert comme aux updates.

5.7. Objets auxiliaires de la base de données

Permettent les ordres CREATE et DROP d'objets arbitraire de la base de donnéées, en conjonction avec les outils Hibernate d'évolutions de schéma, pour permettre de définir complètement un schéma utilisateur au sein des fichiers de mapping Hibernate. Bien que conçu spécifiquement pour créer et supprimer des objets tels que des triggers et des procédures stockées, ou toute commande pouvant être exécutée via une méthode de java.sql.Statement.execute() (ALTERs, INSERTS, etc). Il y a principalement deux modes pour définir les objets auxiliaires de base de données...

Le premier mode est de lister explicitement les commandes CREATE et DROP dans le fichier de mapping:

<hibernate-mapping>
    ...
    <database-object>
        <create>CREATE TRIGGER my_trigger ...</create>
        <drop>DROP TRIGGER my_trigger</drop>
    </database-object>
</hibernate-mapping>

Le second mode est de fournir une classe particulière qui connait comment construire les commandes CREATE et DROP. Cette classe particulière doit implémenter l'interface org.hibernate.mapping.AuxiliaryDatabaseObject.

<hibernate-mapping>
    ...
    <database-object>
        <definition class="MyTriggerDefinition"/>
    </database-object>
</hibernate-mapping>

Additionnellement, ces objets de base de données peuvent être optionnellement traités selon l'utilisation de dialectes particuliers..

<hibernate-mapping>
    ...
    <database-object>
        <definition class="MyTriggerDefinition"/>
        <dialect-scope name="org.hibernate.dialect.Oracle9Dialect"/>
        <dialect-scope name="org.hibernate.dialect.OracleDialect"/>
    </database-object>
</hibernate-mapping>