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

As mentioned previously, Hibernate will first attempt to resolve DTDs in its classpath. The manner in which it does this is by registering a custom org.xml.sax.EntityResolver implementation with the SAXReader it uses to read in the xml files. This custom EntityResolver recognizes two different systemId namespaces.

  • a hibernate namespace is recognized whenever the resolver encounteres a systemId starting with http://hibernate.sourceforge.net/; the resolver attempts to resolve these entities via the classlaoder which loaded the Hibernate classes.

  • a user namespace is recognized whenever the resolver encounteres a systemId using a classpath:// URL protocol; the resolver will attempt to resolve these entities via (1) the current thread context classloader and (2) the classloader which loaded the Hibernate classes.

An example of utilizing user namespacing:

<?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>

Where types.xml is a resource in the your.domain package and contains a custom typedef.

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)
        check="arbitrary sql check condition"         (18)
        rowid="rowid"                                 (19)
        subselect="SQL expression"                    (20)
        abstract="true|false"                         (21)
        node="element-name"
/>
(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 (optional): Override the schema name specified by the root <hibernate-mapping> element.

(6)

catalog (optional): Override the catalog name specified by the root <hibernate-mapping> element.

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

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.

(19)

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é.

(20)

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.

(21)

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)
        node="element-name|@attribute-name|element/@attribute|."

        <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 (optional - defaults to property): The strategy Hibernate should use for accessing the property value.

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>

All generators implement the interface org.hibernate.id.IdentifierGenerator. This is a very simple interface; some applications may choose to provide their own specialized implementations. However, Hibernate provides a range of built-in implementations. There are shortcut names for the built-in generators:

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.

sequence-identity

a specialized sequence generation strategy which utilizes a database sequence for the actual value generation, but combines this with JDBC3 getGeneratedKeys to actually return the generated identifier value as part of the insert statement execution. This strategy is only known to be supported on Oracle 10g drivers targetted for JDK 1.4. Note comments on these insert statements are disabled due to a bug in the Oracle drivers.

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. Enhanced identifier generators

Starting with release 3.2.3, there are 2 new generators which represent a re-thinking of 2 different aspects of identifier generation. The first aspect is database portability; the second is optimization (not having to query the database for every request for a new identifier value). These two new generators are intended to take the place of some of the named generators described above (starting in 3.3.x); however, they are included in the current releases and can be referenced by FQN.

The first of these new generators is org.hibernate.id.enhanced.SequenceStyleGenerator which is intended firstly as a replacement for the sequence generator and secondly as a better portability generator than native (because native (generally) chooses between identity and sequence which have largely different semantics which can cause subtle isssues in applications eyeing portability). org.hibernate.id.enhanced.SequenceStyleGenerator however achieves portability in a different manner. It chooses between using a table or a sequence in the database to store its incrementing values depending on the capabilities of the dialect being used. The difference between this and native is that table-based and sequence-based storage have the same exact semantic (in fact sequences are exactly what Hibernate tries to emmulate with its table-based generators). This generator has a number of configuration parameters:

  • sequence_name (optional, defaults to hibernate_sequence): The name of the sequence (or table) to be used.

  • initial_value (optional, defaults to 1): The initial value to be retrieved from the sequence/table. In sequence creation terms, this is analogous to the clause typical named "STARTS WITH".

  • increment_size (optional, defaults to 1): The value by which subsequent calls to the sequence/table should differ. In sequence creation terms, this is analogous to the clause typical named "INCREMENT BY".

  • force_table_use (optional, defaults to false): Should we force the use of a table as the backing structure even though the dialect might support sequence?

  • value_column (optional, defaults to next_val): Only relevant for table structures! The name of the column on the table which is used to hold the value.

  • optimizer (optional, defaults to none): See Section 5.1.6, « Identifier generator optimization »

The second of these new generators is org.hibernate.id.enhanced.TableGenerator which is intended firstly as a replacement for the table generator (although it actually functions much more like org.hibernate.id.MultipleHiLoPerTableGenerator) and secondly as a re-implementation of org.hibernate.id.MultipleHiLoPerTableGenerator utilizing the notion of pluggable optimiziers. Essentially this generator defines a table capable of holding a number of different increment values simultaneously by using multiple distinctly keyed rows. This generator has a number of configuration parameters:

  • table_name (optional, defaults to hibernate_sequences): The name of the table to be used.

  • value_column_name (optional, defaults to next_val): The name of the column on the table which is used to hold the value.

  • segment_column_name (optional, defaults to sequence_name): The name of the column on the table which is used to hold the "segement key". This is the value which distinctly identifies which increment value to use.

  • segment_value (optional, defaults to default): The "segment key" value for the segment from which we want to pull increment values for this generator.

  • segment_value_length (optional, defaults to 255): Used for schema generation; the column size to create this segment key column.

  • initial_value (optional, defaults to 1): The initial value to be retrieved from the table.

  • increment_size (optional, defaults to 1): The value by which subsequent calls to the table should differ.

  • optimizer (optional, defaults to ): See Section 5.1.6, « Identifier generator optimization »

5.1.6. Identifier generator optimization

For identifier generators which store values in the database, it is inefficient for them to hit the database on each and every call to generate a new identifier value. Instead, you'd ideally want to group a bunch of them in memory and only hit the database when you have exhausted your in-memory value group. This is the role of the pluggable optimizers. Currently only the two enhanced generators (Section 5.1.5, « Enhanced identifier generators » support this notion.

  • none (generally this is the default if no optimizer was specified): This says to not perform any optimizations, and hit the database each and every request.

  • hilo: applies a hi/lo algorithm around the database retrieved values. The values from the database for this optimizer are expected to be sequential. The values retrieved from the database structure for this optimizer indicates the "group number"; the increment_size is multiplied by that value in memory to define a group "hi value".

  • pooled: like was discussed for hilo, this optimizers attempts to minimize the number of hits to the database. Here, however, we simply store the starting value for the "next group" into the database structure rather than a sequential value in combination with an in-memory grouping algorithm. increment_size here refers to the values coming from the database.

5.1.7. 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 (optional - defaults to property): The strategy Hibernate should use for accessing the property value.

  • 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.8. 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.9. 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 (optional - defaults to property): The strategy Hibernate should use for accessing the property value.

(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.10. 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 (optional - defaults to property): The strategy Hibernate should use for accessing the property value.

(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">.

5.1.11. 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 (optional - defaults to property): The strategy Hibernate should use for accessing the property value.

(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.12. 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)
        entity-name="EntityName"                                     (14)
        formula="arbitrary SQL expression"                           (15)
        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: The name of the property.

(2)

column (optional): The name of the foreign key column. This may also be specified by nested <column> element(s).

(3)

class (optional - defaults to the property type determined by reflection): The name of the associated class.

(4)

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

(5)

fetch (optional - defaults to select): Chooses between outer-join fetching or sequential select fetching.

(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 (optional - defaults to property): The strategy Hibernate should use for accessing the property value.

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

(14)

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

(15)

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>.

one-to-one

<many-to-one name="owner" property-ref="identity.ssn" column="OWNER_SSN"/>

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

name : Le nom de la propriété.

<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)
        lazy="proxy|no-proxy|false"                                  (9)
        entity-name="EntityName"                                     (10)
        node="element-name|@attribute-name|element/@attribute|."
        embed-xml="true|false"
        foreign-key="foreign_key_name"
/>
(1)

name: The name of the property.

(2)

class (optional - defaults to the property type determined by reflection): The name of the associated class.

(3)

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

(4)

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

(5)

fetch (optional - defaults to select): Chooses between outer-join fetching or sequential select fetching.

(6)

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

(7)

access (optional - defaults to property): The strategy Hibernate should use for accessing the property value.

(8)

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 !

(9)

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

(10)

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

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.

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 :

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

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.

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

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

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

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

natural-id

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

5.1.14. 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.

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

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

  • component, dynamic-component

5.1.15. 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.

name : Nom de la propriété

<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)
        node="element-name|."
>

        <property ...../>
        <many-to-one .... />
        ........
</component>
(1)

name: The name of the property.

(2)

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

(3)

insert: Do the mapped columns appear in SQL INSERTs?

(4)

update: Do the mapped columns appear in SQL UPDATEs?

(5)

access (optional - defaults to property): The strategy Hibernate should use for accessing the property value.

(6)

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

(7)

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

(8)

unique (optional - defaults to false): Specifies that a unique constraint exists upon all mapped columns of the component.

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 ».

properties

5.1.16. 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.

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

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

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

(2)

insert: Do the mapped columns appear in SQL INSERTs?

(3)

update: Do the mapped columns appear in SQL UPDATEs?

(4)

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

(5)

unique (optional - defaults to false): Specifies that a unique constraint exists upon all mapped columns of the component.

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 :

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

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.

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

subclass

5.1.17. 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>.

name : Le nom complet de la sous-classe.

<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: The fully qualified class name of the subclass.

(2)

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

(3)

proxy (optional): Specifies a class or interface to use for lazy initializing proxies.

(4)

lazy (optional, defaults to true): Setting lazy="false" disables the use of lazy fetching.

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

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

5.1.18. 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"
        node="element-name">

        <key .... >

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

name: The fully qualified class name of the subclass.

(2)

table: The name of the subclass table.

(3)

proxy (optional): Specifies a class or interface to use for lazy initializing proxies.

(4)

lazy (optional, defaults to true): Setting lazy="false" disables the use of lazy fetching.

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.19. 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"
        node="element-name">

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

name: The fully qualified class name of the subclass.

(2)

table: The name of the subclass table.

(3)

proxy (optional): Specifies a class or interface to use for lazy initializing proxies.

(4)

lazy (optional, defaults to true): Setting lazy="false" disables the use of lazy fetching.

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.20. 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)
        optional="true|false">                   (6)

        <key ... />

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

table : Le nom de la table jointe.

(2)

schema (optional): Override the schema name specified by the root <hibernate-mapping> element.

(3)

catalog (optional): Override the catalog name specified by the root <hibernate-mapping> element.

(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.21. 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 (optional): The name of the foreign key column. This may also be specified by nested <column> element(s).

(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.22. é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"
        default="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.23. 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.24. 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 (optional - defaults to property): The strategy Hibernate should use for accessing the property value.

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