Hibernate.orgCommunity Documentation

HIBERNATE - Relational Persistence for Idiomatic Java

Hibernate Reference Documentation

3.6.10.Final

Legal Notice

February 8, 2012


Préface
1. Tutoriel
1.1. Section 1 - Première application Hibernate
1.1.1. Configuration
1.1.2. La première classe
1.1.3. Le fichier de mappage
1.1.4. Configuration d'Hibernate
1.1.5. Construction avec Maven
1.1.6. Démarrage et aides
1.1.7. Charger et stocker des objets
1.2. Section 2 - Mapper des associations
1.2.1. Mapper la classe Person
1.2.2. Une association unidirectionnelle basée sur Set
1.2.3. Travailler avec l'association
1.2.4. Collection de valeurs
1.2.5. Associations bidirectionnelles
1.2.6. Travailler avec des liens bidirectionnels
1.3. Section 3 - L'application web EventManager
1.3.1. Écrire la servlet de base
1.3.2. Traiter et interpréter
1.3.3. Déployer et tester
1.4. Résumé
2. Architecture
2.1. Généralités
2.1.1. Minimal architecture
2.1.2. Comprehensive architecture
2.1.3. Basic APIs
2.2. Intégration JMX
2.3. Sessions contextuelles
3. Configuration
3.1. Configuration par programmation
3.2. Obtenir une SessionFactory
3.3. Connexions JDBC
3.4. Propriétés de configuration optionnelles
3.4.1. Dialectes SQL
3.4.2. Chargement par jointure externe
3.4.3. Flux binaires
3.4.4. Cache de second niveau et cache de requêtes
3.4.5. Substitution dans le langage de requêtes
3.4.6. Statistiques Hibernate
3.5. Journalisation
3.6. Sélectionne une NamingStrategy (stratégie de nommage)
3.7. Implementing a PersisterClassProvider
3.8. Fichier de configuration XML
3.9. Java EE Application Server integration
3.9.1. Configuration de la stratégie transactionnelle
3.9.2. SessionFactory associée au JNDI
3.9.3. Gestion du contexte de la session courante à JTA
3.9.4. Déploiement JMX
4. Classes persistantes
4.1. Un exemple simple de POJO
4.1.1. Implémenter un constructeur sans argument
4.1.2. Provide an identifier property
4.1.3. Prefer non-final classes (semi-optional)
4.1.4. Déclarer les accesseurs et mutateurs des attributs persistants (optionnel)
4.2. Implémenter l'héritage
4.3. Implémenter equals() et hashCode()
4.4. Modèles dynamiques
4.5. Tuplizers
4.6. EntityNameResolvers
5. Mappage O/R de base
5.1. Déclaration de mappage
5.1.1. Entity
5.1.2. Identifiers
5.1.3. Optimistic locking properties (optional)
5.1.4. Property
5.1.5. Embedded objects (aka components)
5.1.6. Inheritance strategy
5.1.7. Mapping one to one and one to many associations
5.1.8. Natural-id
5.1.9. Any
5.1.10. Propriétés
5.1.11. Some hbm.xml specificities
5.2. Types Hibernate
5.2.1. Entités et valeurs
5.2.2. Types valeurs de base
5.2.3. Types de valeur personnalisés
5.3. Mapper une classe plus d'une fois
5.4. SQL quoted identifiers
5.5. Propriétés générées
5.6. Column transformers: read and write expressions
5.7. Objets auxiliaires de la base de données
6. Types
6.1. Value types
6.1.1. Basic value types
6.1.2. Composite types
6.1.3. Collection types
6.2. Entity types
6.3. Significance of type categories
6.4. Custom types
6.4.1. Custom types using org.hibernate.type.Type
6.4.2. Custom types using org.hibernate.usertype.UserType
6.4.3. Custom types using org.hibernate.usertype.CompositeUserType
6.5. Type registry
7. Mapper une collection
7.1. Collections persistantes
7.2. How to map collections
7.2.1. Les clés étrangères d'une collection
7.2.2. Collections indexées
7.2.3. Collections of basic types and embeddable objects
7.3. Mappages de collection avancés
7.3.1. Collections triées
7.3.2. Associations bidirectionnelles
7.3.3. Associations bidirectionnelles avec des collections indexées
7.3.4. Associations ternaires
7.3.5. Using an <idbag>
7.4. Exemples de collections
8. Mapper les associations
8.1. Introduction
8.2. Associations unidirectionnelles
8.2.1. plusieurs-à-un
8.2.2. Un-à-un
8.2.3. un-à-plusieurs
8.3. Associations unidirectionnelles avec tables de jointure
8.3.1. un-à-plusieurs
8.3.2. plusieurs-à-un
8.3.3. Un-à-un
8.3.4. Plusieurs-à-plusieurs
8.4. Associations bidirectionnelles
8.4.1. un-à-plusieurs / plusieurs-à-un
8.4.2. Un-à-un
8.5. Associations bidirectionnelles avec tables de jointure
8.5.1. un-à-plusieurs / plusieurs-à-un
8.5.2. un-à-un
8.5.3. Plusieurs-à-plusieurs
8.6. Des mappages d'associations plus complexes
9. Mappage de composants
9.1. Objets dépendants
9.2. Collection d'objets dépendants
9.3. Les composants en tant qu'indices de Map
9.4. Les composants en tant qu'identifiants composites
9.5. Les composants dynamiques
10. Mapping d'héritage de classe
10.1. Les trois stratégies
10.1.1. Une table par hiérarchie de classe
10.1.2. Une table par classe fille
10.1.3. Une table par classe fille, en utilisant un discriminant
10.1.4. Mélange d'une table par hiérarchie de classe avec une table par classe fille
10.1.5. Une table par classe concrète
10.1.6. Une table par classe concrète, en utilisant le polymorphisme implicite
10.1.7. Mélange du polymorphisme implicite avec d'autres mappages d'héritage
10.2. Limitations
11. Travailler avec des objets
11.1. États des objets Hibernate
11.2. Rendre des objets persistants
11.3. Chargement d'un objet
11.4. Requêtage
11.4.1. Exécution de requêtes
11.4.2. Filtrer des collections
11.4.3. Requêtes par critères
11.4.4. Requêtes en SQL natif
11.5. Modifier des objets persistants
11.6. Modifier des objets détachés
11.7. Détection automatique d'un état
11.8. Suppression d'objets persistants
11.9. Réplication d'objets entre deux entrepôts de données
11.10. Flush de la session
11.11. Persistance transitive
11.12. Utilisation des méta-données
12. Read-only entities
12.1. Making persistent entities read-only
12.1.1. Entities of immutable classes
12.1.2. Loading persistent entities as read-only
12.1.3. Loading read-only entities from an HQL query/criteria
12.1.4. Making a persistent entity read-only
12.2. Read-only affect on property type
12.2.1. Simple properties
12.2.2. Unidirectional associations
12.2.3. Bidirectional associations
13. Transactions et Accès concurrents
13.1. Portées des sessions et des transactions
13.1.1. Unité de travail
13.1.2. Longue conversation
13.1.3. L'identité des objets
13.1.4. Problèmes communs
13.2. Démarcation des transactions de base de données
13.2.1. Environnement non gérés
13.2.2. Utilisation de JTA
13.2.3. Gestion des exceptions
13.2.4. Timeout de transaction
13.3. Contrôle de concurrence optimiste
13.3.1. Vérification du versionnage au niveau applicatif
13.3.2. Les sessions longues et le versionnage automatique.
13.3.3. Les objets détachés et le versionnage automatique
13.3.4. Personnaliser le versionnage automatique
13.4. Verrouillage pessimiste
13.5. Modes de libération de connexion
14. Intercepteurs et événements
14.1. Intercepteurs
14.2. Système d'événements
14.3. Sécurité déclarative de Hibernate
15. Traitement par lot
15.1. Insertions en lot
15.2. Mise à jour des lots
15.3. L'interface StatelessSession
15.4. Opérations de style DML
16. HQL : langage d'interrogation d'Hibernate
16.1. Sensibilité à la casse
16.2. La clause from
16.3. Associations et jointures
16.4. Formes de syntaxes pour les jointures
16.5. Faire référence à la propriété identifiant
16.6. La clause select
16.7. Fonctions d'agrégation
16.8. Requêtes polymorphiques
16.9. La clause where
16.10. Expressions
16.11. La clause order by
16.12. La clause group by
16.13. Sous-requêtes
16.14. Exemples HQL
16.15. Nombreuses mises à jour et suppressions
16.16. Trucs & Astuces
16.17. Composants
16.18. Syntaxe des constructeurs de valeur de ligne
17. Requêtes par critères
17.1. Créer une instance de Criteria
17.2. Restriction du résultat
17.3. Trier les résultats
17.4. Associations
17.5. Peuplement d'associations de manière dynamique
17.6. Requêtes par l'exemple
17.7. Projections, agrégation et regroupement
17.8. Requêtes et sous-requêtes détachées
17.9. Requêtes par identifiant naturel
18. SQL natif
18.1. Utiliser une requête SQLQuery
18.1.1. Requêtes scalaires
18.1.2. Requêtes d'entités
18.1.3. Gérer les associations et collections
18.1.4. Retour d'entités multiples
18.1.5. Retour d'entités non gérées
18.1.6. Gérer l'héritage
18.1.7. Paramètres
18.2. Requêtes SQL nommées
18.2.1. Utilisation de return-property pour spécifier explicitement les noms des colonnes/alias
18.2.2. Utilisation de procédures stockées pour les requêtes
18.3. SQL personnalisé pour créer, mettre à jour et effacer
18.4. SQL personnalisé pour le chargement
19. Filtrer les données
19.1. Filtres Hibernate
20. Mappage XML
20.1. Travailler avec des données XML
20.1.1. Spécifier le mappage XML et le mappage d'une classe ensemble
20.1.2. Spécifier seulement un mappage XML
20.2. Métadonnées du mappage XML
20.3. Manipuler des données XML
21. Améliorer les performances
21.1. Stratégies de chargement
21.1.1. Travailler avec des associations chargées en différé
21.1.2. Personnalisation des stratégies de chargement
21.1.3. Proxies pour des associations vers un seul objet
21.1.4. Initialisation des collections et des proxies
21.1.5. Utiliser le chargement par lot
21.1.6. Utilisation du chargement par sous select
21.1.7. Fetch profiles
21.1.8. Utiliser le chargement en différé des propriétés
21.2. Le cache de second niveau
21.2.1. Mappages de Cache
21.2.2. Stratégie : lecture seule
21.2.3. Stratégie : lecture/écriture
21.2.4. Stratégie : lecture/écriture non stricte
21.2.5. Stratégie : transactionelle
21.2.6. Support de stratégie de concurrence du fournisseur-cache
21.3. Gérer les caches
21.4. Le cache de requêtes
21.4.1. Enabling query caching
21.4.2. Query cache regions
21.5. Comprendre les performances des collections
21.5.1. Taxinomie
21.5.2. Les lists, les maps, les idbags et les ensembles sont les collections les plus efficaces pour la mise à jour
21.5.3. Les sacs et les listes sont les plus efficaces pour les collections inverses
21.5.4. Suppression en un coup
21.6. Moniteur de performance
21.6.1. Suivi d'une SessionFactory
21.6.2. Métriques
22. Guide de la boîte à outils
22.1. Génération automatique du schéma
22.1.1. Personnaliser le schéma
22.1.2. Exécuter l'outil
22.1.3. Propriétés
22.1.4. Utiliser Ant
22.1.5. Mises à jour incrémentales du schéma
22.1.6. Utiliser Ant pour des mises à jour de schéma par incrément
22.1.7. Validation du schéma
22.1.8. Utiliser Ant pour la validation du Schéma
23. Additional modules
23.1. Bean Validation
23.1.1. Adding Bean Validation
23.1.2. Configuration
23.1.3. Catching violations
23.1.4. Database schema
23.2. Hibernate Search
23.2.1. Description
23.2.2. Integration with Hibernate Annotations
24. Exemple : père/fils
24.1. Une note à propos des collections
24.2. Un-à-plusieurs bidirectionnel
24.3. Cycle de vie en cascade
24.4. Cascades et unsaved-value (valeurs non sauvegardées)
24.5. Conclusion
25. Exemple : application Weblog
25.1. Classes persistantes
25.2. Mappages Hibernate
25.3. Code Hibernate
26. Exemple : quelques mappages
26.1. Employeur/Employé (Employer/Employee)
26.2. Auteur/Travail
26.3. Client/Commande/Produit
26.4. Divers exemples de mappages
26.4.1. "Typed" association un-à-un
26.4.2. Exemple de clef composée
26.4.3. Plusieurs-à-plusieurs avec un attribut de clef composée partagée
26.4.4. Contenu basé sur une discrimination
26.4.5. Associations sur des clés alternées
27. Meilleures pratiques
28. Considérations de portabilité des bases de données
28.1. Aspects fondamentaux de la portabilité
28.2. Dialecte
28.3. Résolution de dialecte
28.4. Générer les identifiants
28.5. Fonctions de base de données
28.6. Type mappings
References

Working with both Object-Oriented software and Relational Databases can be cumbersome and time consuming. Development costs are significantly higher due to a paradigm mismatch between how data is represented in objects versus relational databases. Hibernate is an Object/Relational Mapping solution for Java environments. The term Object/Relational Mapping refers to the technique of mapping data from an object model representation to a relational data model representation (and visa versa). See http://en.wikipedia.org/wiki/Object-relational_mapping for a good high-level discussion.

Note

While having a strong background in SQL is not required to use Hibernate, having a basic understanding of the concepts can greatly help you understand Hibernate more fully and quickly. Probably the single best background is an understanding of data modeling principles. You might want to consider these resources as a good starting point:

Hibernate not only takes care of the mapping from Java classes to database tables (and from Java data types to SQL data types), but also provides data query and retrieval facilities. It can significantly reduce development time otherwise spent with manual data handling in SQL and JDBC. Hibernate’s design goal is to relieve the developer from 95% of common data persistence-related programming tasks by eliminating the need for manual, hand-crafted data processing using SQL and JDBC. However, unlike many other persistence solutions, Hibernate does not hide the power of SQL from you and guarantees that your investment in relational technology and knowledge is as valid as always.

Hibernate may not be the best solution for data-centric applications that only use stored-procedures to implement the business logic in the database, it is most useful with object-oriented domain models and business logic in the Java-based middle-tier. However, Hibernate can certainly help you to remove or encapsulate vendor-specific SQL code and will help with the common task of result set translation from a tabular representation to a graph of objects.

Si vous n'êtes pas familiarisé avec Hibernate et le mappage Objet/Relationnel ou même Java, veuillez suivre les étapes suivantes :

  1. Read Chapitre 1, Tutoriel for a tutorial with step-by-step instructions. The source code for the tutorial is included in the distribution in the doc/reference/tutorial/ directory.

  2. Read Chapitre 2, Architecture to understand the environments where Hibernate can be used.

  3. Veuillez consulter le répertoire eg/ dans la distribution Hibernate, qui contient une application autonome simple. Copier votre pilote JDBC dans le répertoire lib/ et éditez etc/hibernate.properties, en spécifiant les valeurs qu'il faut dans votre base de données. A partir d'une invite de commande du répertoire de distribution, veuillez saisir ant eg (en utilisant Ant), et sous Windows, tapez build eg.

  4. Use this reference documentation as your primary source of information. Consider reading [JPwH] if you need more help with application design, or if you prefer a step-by-step tutorial. Also visit http://caveatemptor.hibernate.org and download the example application from [JPwH].

  5. Les questions FAQ sont traitées sur le site Hibernate.

  6. Links to third party demos, examples, and tutorials are maintained on the Hibernate website.

  7. La section Community Area (Zône communautaire) du site Hibernate constitue une ressource intéressante pour les modèles conceptuels et autres solutions diverses d'intégration (Tomcat, JBoss AS, Struts, EJB, etc.).

There are a number of ways to become involved in the Hibernate community, including

  • Trying stuff out and reporting bugs. See http://hibernate.org/issuetracker.html details.

  • Trying your hand at fixing some bugs or implementing enhancements. Again, see http://hibernate.org/issuetracker.html details.

  • http://hibernate.org/community.html list a few ways to engage in the community.

    • There are forums for users to ask questions and receive help from the community.

    • There are also IRC channels for both user and developer discussions.

  • Helping improve or translate this documentation. Contact us on the developer mailing list if you have interest.

  • Evangelizing Hibernate within your organization.

A l'intention des nouveaux utilisateurs, ce chapitre fournit une introduction étape par étape à Hibernate, en commençant par une application simple, avec une base de données en-mémoire. Le tutoriel est basé sur une tutoriel antérieur qui avait été développé par Michael Gloegl. Tout le code est contenu dans tutorials/web qui se trouve dans le répertoire source du projet.

Important

Ce tutoriel assume que l'utilisateur est déjà familier avec Java et SQL à la fois. Si vous ne possédez qu'une connaissance de Java et d'SQL limitée, il est conseillé de commencer par vous familiariser avec ces technologies avant d'aborder Hibernate.

Note

La distribution contient un autre exemple d'application qui se trouve dans le répertoire source du projet tutorial/eg.

Supposons que nous ayons besoin d'une petite application de base de données qui puisse stocker des événements que nous voulons suivre, et des informations à propos des hôtes de ces événements.

Note

Malgré que vous puissiez utiliser tout base de données qui vous convienne, on choisira HSQLDB (une base de données Java, en-mémoire) pour éviter de décrire l'installation et la configuration de n'importe quel serveur de base de données particulière.

La première chose que nous devons faire est de configurer l'environnement de développement. Nous utiliserons la "standard layout" préconisée par de nombreux outils de génération tels que Maven. Maven, en particulier, a une bonne ressource décrivant cette layout. Comme ce tutoriel va devenir une application web, nous allons créer et utiliser les répertoires src/main/java., src/main/ressources et src/main/webapp.

Nous utiliserons Maven dans ce tutoriel. Nous profiterons de ses capacités de gestion de dépendances transitives, ainsi que de la capacité des nombreux IDE à installer automatiquement un projet sur la base du descripteur Maven.


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>org.hibernate.tutorials</groupId>
    <artifactId>hibernate-tutorial</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>First Hibernate Tutorial</name>

    <build>
         <!-- we dont want the version to be part of the generated war file name -->
         <finalName>${artifactId}</finalName>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
        </dependency>

        <!-- Because this is a web app, we also have a dependency on the servlet api. -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
        </dependency>

        <!-- Hibernate uses slf4j for logging, for our purposes here use the simple backend -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
        </dependency>

        <!-- Hibernate gives you a choice of bytecode providers between cglib and javassist -->
        <dependency>
            <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
        </dependency>
    </dependencies>

</project>

Astuce

It is not a requirement to use Maven. If you wish to use something else to build this tutorial (such as Ant), the layout will remain the same. The only change is that you will need to manually account for all the needed dependencies. If you use something like Ivy providing transitive dependency management you would still use the dependencies mentioned below. Otherwise, you'd need to grab all dependencies, both explicit and transitive, and add them to the project's classpath. If working from the Hibernate distribution bundle, this would mean hibernate3.jar, all artifacts in the lib/required directory and all files from either the lib/bytecode/cglib or lib/bytecode/javassist directory; additionally you will need both the servlet-api jar and one of the slf4j logging backends.

Sauvegardez ce fichier sous la forme pom.xml dans le répertoire root du projet.

Ensuite, nous créons une classe qui représente l'évènement que nous voulons stocker dans notre base de données. Il s'agit d'une simple classe JavaBean avec quelques propriétés :

package org.hibernate.tutorial.domain;


import java.util.Date;
public class Event {
    private Long id;
    private String title;
    private Date date;
    public Event() {}
    public Long getId() {
        return id;
    }
    private void setId(Long id) {
        this.id = id;
    }
    public Date getDate() {
        return date;
    }
    public void setDate(Date date) {
        this.date = date;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
}

Vous constaterez que cette classe utilise les conventions de nommage standard JavaBean pour les méthodes getter/setter des propriétés, ainsi qu'une visibilité privée pour les champs. Ceci est la conception recommandée - mais pas obligatoire. Hibernate peut aussi accéder aux champs directement, le bénéfice des méthodes d'accès est la robustesse pour la refonte de code.

La propriété id contient la valeur d'un identifiant unique pour un événement particulier. Toutes les classes d'entités persistantes (il y a également des classes dépendantes de moindre importance) auront besoin d'une telle propriété identifiante si nous voulons utiliser l'ensemble complet des fonctionnalités de Hibernate. En fait, la plupart des applications (surtout les applications web) ont besoin de distinguer des objets par des identifiants, par conséquent considérez cela comme une fonctionnalité et non comme une limitation. Cependant, nous ne manipulons généralement pas l'identité d'un objet, dorénavant la méthode setter devrait être privée. Seul Hibernate assignera les identifiants lorsqu'un objet est sauvegardé. Remarquez que Hibernate peut accéder aux méthodes publiques, privées et protégées, ainsi qu'aux champs (publics, privés, protégés) directement. À vous de choisir, et vous pouvez également l'ajuster à la conception de votre application.

Le constructeur sans argument est requis pour toutes les classes persistantes; Hibernate doit créer des objets pour vous en utilisant la réflexion Java. Le constructeur peut être privé, cependant, la visibilité du paquet est requise pour la génération de proxies à l'exécution et une récupération efficace des données sans instrumentation du bytecode.

Sauvegardez ce fichier dans le répertoire src/main/java/org/hibernate/tutorial/domain.

Hibernate a besoin de savoir comment charger et stocker des objets d'une classe persistante. C'est là qu'intervient le fichier de mappage Hibernate. Le fichier de mappage indique à Hibernate à quelle table accéder dans la base de données, et les colonnes de cette table à utiliser.

La structure basique de ce fichier de mappage ressemble à ce qui suit :


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

<hibernate-mapping package="org.hibernate.tutorial.domain">
[...]
</hibernate-mapping
>

Notez que la DTD Hibernate est très sophistiquée. Vous pouvez l'utiliser pour l'auto-finalisation des éléments et des attributs de mappage XML dans votre éditeur ou votre IDE. Ouvrez également le fichier DTD dans votre éditeur de texte - c'est le moyen le plus facile d'obtenir une vue d'ensemble de tous les éléments et attributs, et de voir les valeurs par défaut, ainsi que quelques commentaires. Notez qu'Hibernate ne chargera pas le fichier DTD à partir du web, mais regardera d'abord dans le chemin de classe de l'application. Le fichier DTD est inclus dans hibernate-core.jar ainsi que dans le répertoire src de la distribution Hibernate).

Entre les deux balises hibernate-mapping, incluez un élément class. Toutes les classes d'entités persistantes (encore une fois, il pourrait y avoir des classes dépendantes plus tard, qui ne sont pas des entités mère) ont besoin d'un mappage vers une table de la base de données SQL :


<hibernate-mapping package="org.hibernate.tutorial.domain">

    <class name="Event" table="EVENTS">

    </class>

</hibernate-mapping>

Plus loin, nous indiquons à Hibernate comment persister et charger un objet de la classe Event dans la table EVENTS, chaque instance étant représentée par une ligne dans cette table. Maintenant nous continuons avec le mappage de la propriété de l'identifiant unique vers la clef primaire des tables. De plus, comme nous ne voulons pas nous occuper de la gestion de cet identifiant, nous utilisons une stratégie de génération d'identifiant Hibernate pour la colonne de la clé primaire subrogée :


<hibernate-mapping package="org.hibernate.tutorial.domain">

    <class name="Event" table="EVENTS">
        <id name="id" column="EVENT_ID">
            <generator class="native"/>
        </id>
    </class>

</hibernate-mapping>

L'élément ID est la déclaration de l'identifiant de propriété. L'attribut de mappage name="id" déclare le nom de la propriété JavaBean et indique à Hibernate d'utiliser les méthodes getId() et setId() pour accéder à la propriété. L'attribut de colonne indique à Hibernate quelle colonne de la table EVENTS contient la valeur de clé primaire.

L'élément imbriqué Générateur spécifie la stratégie de génération d'identifiant (c'est à dire comment les valeurs d'identifiant sont-elles générées?). Dans ce cas nous avons choisi native, qui offre un niveau de la portabilité selon le dialecte de base de données configurée. Mise en veille prolongée prend en charge la base de données générée, unique au monde, ainsi que l'application affectée, les identifiants. Génération de valeur d'identifiant est aussi l'un des nombreux points d'extension d'Hibernate et vous pouvez plug-in votre propre stratégie.

Enfin, nous incluons des déclarations pour les propriétés persistantes de la classe dans le fichier de mappage. Par défaut, aucune propriété de la classe n'est considérée comme persistante :



<hibernate-mapping package="org.hibernate.tutorial.domain">

    <class name="Event" table="EVENTS">
        <id name="id" column="EVENT_ID">
            <generator class="native"/>
        </id>
        <property name="date" type="timestamp" column="EVENT_DATE"/>
        <property name="title"/>
    </class>

</hibernate-mapping>

Comme avec l'élément id, l'attribut name de l'élément property indique à Hibernate quelles méthodes getters/setters utiliser. Par conséquent dans ce cas, Hibernate cherchera getDate()/setDate(), de même que getTitle()/setTitle().

Note

Pourquoi le mappage de la propriété date inclut-il l'attribut column, mais non le title ? Sans l'attribut column, Hibernate utilise par défaut le nom de la propriété comme nom de colonne. Cela fonctionne bien pour title. Cependant, date est un mot clé réservé dans la plupart des bases de données, donc nous utilisons un nom différent pour le mappage.

Il est intéressant de noter que le mappage de title manque également d'un attribut type. Les types que nous déclarons et utilisons dans les fichiers de mappage ne sont pas, comme vous pourriez vous y attendre, des types de données Java. Ce ne sont pas, non plus, des types de base de données SQL. Ces types sont donc appelés types de mappage Hibernate, des convertisseurs qui peuvent traduire des types Java en types SQL et vice versa. De plus, Hibernate tentera de déterminer la bonne conversion et le type de mappage lui-même si l'attribut type n'est pas présent dans le mappage. Dans certains cas, cette détection automatique (utilisant la réflexion sur la classe Java) pourrait ne pas donner la valeur attendue ou dont vous avez besoin. C'est le cas avec la propriété date. Hibernate ne peut pas savoir si la propriété "mappera" une colonne SQL de type date, timestamp ou time. Nous déclarons que nous voulons conserver des informations avec une date complète et l'heure en mappant la propriété avec un convertisseur timestamp.

Astuce

Hibernate rend cette détermination de type de mappage en utilisant la réflection au moment du traitement des fichiers de mappage. Cela prend du temps et consomme des ressources, donc, si la performance de démarrage est importante, vous devriez considérer définir explicitement quel type utiliser.

Sauvegardez ce fichier de mappage ainsi src/main/resources/org/hibernate/tutorial/domain/Event.hbm.xml.

A ce niveau là, vous devriez avoir la classe persistante et son fichier de mappage en place. Il est temps maintenant de configurer Hibernate. Tout d'abord, il nous faut configurer HSQLDB pour qu'il puisse exécuter en "server mode"

Vous utiliserez le lugin exec Maven pour lancer le serveur HSQLDB en exécutant : mvn exec:java -Dexec.mainClass="org.hsqldb.Server" -Dexec.args="-database.0 file:target/data/tutorial". Vous observez qu'elle démarre et ouvre un socket TCP/IP, c'est là que notre application se connectera plus tard. Si vous souhaitez démarrez à partir d'une nouvelle base de données pour ce tutoriel (choisissez CTRL + C dans la fenêtre), effacez tous les fichiers dans le répertoire target/data et redémarrez HSQL DB.

Hibernate se connectera à la base de données pour le compte de votre application, donc il devra savoir comment obtenir des connexions. Pour ce tutoriel, nous devrons utliser un pool de connexions autonomes (et non pas javax.sql.DataSource). Hibernate bénéficie du support de deux pools de connexions JDBC open source de tierce partie : c3p0 and proxool. Cependant, nous utiliserons le pool de connexions intégré Hibernate pour ce tutoriel.

Attention

The built-in Hibernate connection pool is in no way intended for production use. It lacks several features found on any decent connection pool.

Pour la configuration de Hibernate, nous pouvons utiliser un simple fichier hibernate.properties, un fichier hibernate.cfg.xml légèrement plus sophistiqué, ou même une configuration complète par programmation. La plupart des utilisateurs préfèrent le fichier de configuration XML :


<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <session-factory>

        <!-- Database connection settings -->
        <property name="connection.driver_class"
>org.hsqldb.jdbcDriver</property>
        <property name="connection.url"
>jdbc:hsqldb:hsql://localhost</property>
        <property name="connection.username"
>sa</property>
        <property name="connection.password"
></property>

        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size"
>1</property>

        <!-- SQL dialect -->
        <property name="dialect"
>org.hibernate.dialect.HSQLDialect</property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class"
>thread</property>

        <!-- Disable the second-level cache  -->
        <property name="cache.provider_class"
>org.hibernate.cache.NoCacheProvider</property>

        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql"
>true</property>

        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto"
>update</property>

        <mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>

    </session-factory>

</hibernate-configuration
>

Note

Vous pourrez remarquer que cette configuration XML utilise une DTD différente.

Nous configurons une SessionFactory de Hibernate - une fabrique globale responsable d'une base de données particulière. Si vous avez plusieurs base de données, utilisez plusieurs configurations <session-factory>, généralement dans des fichiers de configuration différents (pour un démarrage plus facile).

Les quatre premiers éléments property contiennent la configuration nécessaire pour la connexion JDBC. L'élément property du dialecte spécifie quelle variante du SQL Hibernate va générer.

Astuce

In most cases, Hibernate is able to properly determine which dialect to use. See Section 28.3, « Résolution de dialecte » for more information.

La gestion automatique des sessions d'Hibernate pour les contextes de persistance est bien pratique, comme vous pourrez le constater. L'option hbm2ddl.auto active la génération automatique des schémas de base de données - directement dans la base de données. Cela peut également être désactivé (en supprimant l'option de configuration) ou redirigé vers un fichier avec l'aide de la tâche Ant SchemaExport. Finalement, nous ajoutons le(s) fichier(s) de mappage pour les classes persistantes.

Sauvegarder ce fichier en tant que hibernate.cfg.xml dans le répertoire src/main/resources.

Nous allons maintenant construire le tutoriel avec Maven. Vous aurez besoin d'installer Maven pour cela. Il est disponible dans la page Maven download page. Maven pourra lire le fichier /pom.xml que nous avons créé plus tôt et saura comment effectuer quelques tâches du projet de base. Tout d'abord, exécutons compile pour s'assurer que nous pouvons tout compiler jusqu'à maintenant :

[hibernateTutorial]$ mvn compile
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building First Hibernate Tutorial
[INFO]    task-segment: [compile]
[INFO] ------------------------------------------------------------------------
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:compile]
[INFO] Compiling 1 source file to /home/steve/projects/sandbox/hibernateTutorial/target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2 seconds
[INFO] Finished at: Tue Jun 09 12:25:25 CDT 2009
[INFO] Final Memory: 5M/547M
[INFO] ------------------------------------------------------------------------

Il est temps de charger et de stocker quelques objets Event, mais d'abord nous devons compléter la configuration avec du code d'infrastructure. Nous devons démarrer Hibernate. Ce démarrage inclut la construction d'un objet SessionFactory global et le stocker dans un lieu facile d'accès dans le code de l'application. Une SessionFactory peut ouvrir de nouvelles Sessions. Une Session représente une unité de travail simplement "threadée". La org.hibernate.SessionFactory est un objet global "thread-safe", instancié une seule fois.

Nous créerons une classe d'aide HibernateUtil qui s'occupe du démarrage et rend la gestion des org.hibernate.SessionFactory plus facile.

package org.hibernate.tutorial.util;


import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
    private static final SessionFactory sessionFactory = buildSessionFactory();
    private static SessionFactory buildSessionFactory() {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            return new Configuration().configure().buildSessionFactory();
        }
        catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

Sauvegardez ce code en tant que src/main/java/org/hibernate/tutorial/util/HibernateUtil.java

Cette classe ne produit pas seulement la org.hibernate.SessionFactory globale dans un initialiseur statique. Elle masque le fait qu'elle exploite un singleton statique. Nous aurions pu aussi bien vérouiller la référence org.hibernate.SessionFactory à partir de JNDI dans un serveur d'application ou dans n'importe quelle location en fait. Elle pourrait aussi obtenir la SessionFactory depuis JNDI dans un serveur d'applications.

Si vous nommez org.hibernate.SessionFactory dans votre fichier de configuration, Hibernate tentera la récupération depuis JNDI. Pour éviter ce code, vous pouvez aussi utiliser un déploiement JMX et laisser le conteneur (compatible JMX) instancier et lier un HibernateService à JNDI. Ces options avancées sont expliquées plus loin.

Nous avons finalement besoin de configurer le système de journalisation - Hibernate utilise commons-logging et vous laisse le choix entre log4j et le système de logs du JDK 1.4. La plupart des développeurs préfèrent log4j : copiez log4j.properties de la distribution de Hibernate (il est dans le répertoire etc/) dans votre répertoire src, puis faîtes de même avec hibernate.cfg.xml. Regardez la configuration d'exemple et changez les paramètres si vous voulez une sortie plus verbeuse. Par défaut, seul le message de démarrage de Hibernate est affiché sur la sortie standard.

L'infrastructure de ce toturiel est complète - et nous sommes prêts à effectuer un travail réel avec Hibernate.

We are now ready to start doing some real work with Hibernate. Let's start by writing an EventManager class with a main() method:

package org.hibernate.tutorial;


import org.hibernate.Session;
import java.util.*;
import org.hibernate.tutorial.domain.Event;
import org.hibernate.tutorial.util.HibernateUtil;
public class EventManager {
    public static void main(String[] args) {
        EventManager mgr = new EventManager();
        if (args[0].equals("store")) {
            mgr.createAndStoreEvent("My Event", new Date());
        }
        HibernateUtil.getSessionFactory().close();
    }
    private void createAndStoreEvent(String title, Date theDate) {
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        session.beginTransaction();
        Event theEvent = new Event();
        theEvent.setTitle(title);
        theEvent.setDate(theDate);
        session.save(theEvent);
        session.getTransaction().commit();
    }
}

Nous créons un nouvel objet Event dans createAndStoreEvent(), et nous le remettons à Hibernate, qui s'occupe maintenant du SQL et exécute les INSERT s dans la base de données.

A org.hibernate.Session is designed to represent a single unit of work (a single atomic piece of work to be performed). For now we will keep things simple and assume a one-to-one granularity between a Hibernate org.hibernate.Session and a database transaction. To shield our code from the actual underlying transaction system we use the Hibernate org.hibernate.Transaction API. In this particular case we are using JDBC-based transactional semantics, but it could also run with JTA.

Quelle est la fonction de sessionFactory.getCurrentSession() ? Premièrement, vous pouvez l'invoquer autant de fois que vous le voulez et n'importe où, du moment que vous avez votre SessionFactory (facile grâce à HibernateUtil). La méthode getCurrentSession() renvoie toujours l'unité de travail courante. Souvenez vous que nous avons basculé notre option de configuration au mécanisme basé sur le "thread" dans hibernate.cfg.xml. Par conséquent, l'unité de travail courante est liée au thread Java courant qui exécute notre application.

Une org.hibernate.Session commence lorsque le thread courant commence à appeler getCurrentSession(). Ensuite, elle est attachée par Hibernate au thread courant. Lorsque la transaction s'achève, par commit ou par rollback, Hibernate détache automatiquement la Session du thread et la ferme pour vous. Si vous invoquez getCurrentSession() une nouvelle fois, vous obtenez une nouvelle Session et pouvez entamer une nouvelle unité de travail.

A propos de la portée de l'unité de travail, la session org.hibernate.Session Hibernate devrait-elle être utilisée pour exécuter une ou plusieurs opérations en base de données ? L'exemple ci-dessus utilise une Session pour une opération. C'est une pure coïncidence, l'exemple n'est pas assez complexe pour montrer d'autres approches. La portée d'une Session Hibernate est flexible mais vous ne devriez jamais concevoir votre application de manière à utiliser une nouvelle Session Hibernate pour chaque opération en base de données. Donc même si vous le voyez quelquefois dans les exemples suivants, considérez une session par opération comme un anti-modèle. Une véritable application (web) est affichée plus loin dans ce tutoriel.

See Chapitre 13, Transactions et Accès concurrents for more information about transaction handling and demarcation. The previous example also skipped any error handling and rollback.

Pour pouvoir exécuter ceci, nous utiliserons le plugin exec Maven pour appeler notre classe avec la configuration de classpath qui convient : mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="store"

Note

Vous aurez sans doute besoin d'effectuer mvn compile pour commencer.

Vous devriez constater qu'Hibernate démarre et selon votre configuration, beaucoup de traces sur la sortie. À la fin, vous trouverez la ligne suivante :

[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)

C'est l' INSERT exécutée par Hibernate.

Maintenant nous aimerions aussi lister les événements stockés, donc nous ajoutons une option à la méthode principale :

        if (args[0].equals("store")) {

            mgr.createAndStoreEvent("My Event", new Date());
        }
        else if (args[0].equals("list")) {
            List events = mgr.listEvents();
            for (int i = 0; i < events.size(); i++) {
                Event theEvent = (Event) events.get(i);
                System.out.println(
                        "Event: " + theEvent.getTitle() + " Time: " + theEvent.getDate()
                );
            }
        }

Nous ajoutons aussi une nouvelle méthode listEvents() :

    private List listEvents() {

        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        session.beginTransaction();
        List result = session.createQuery("from Event").list();
        session.getTransaction().commit();
        return result;
    }

Here, we are using a Hibernate Query Language (HQL) query to load all existing Event objects from the database. Hibernate will generate the appropriate SQL, send it to the database and populate Event objects with the data. You can create more complex queries with HQL. See Chapitre 16, HQL : langage d'interrogation d'Hibernate for more information.

Nous pouvons maintenant appeler notre nouvelle fonctionnalité, en utilisant à nouveau le plugin exec Maven : mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="list"

Pour l'instant, nous nous sommes contentés de mapper une classe d'une entité persistante vers une table. Profitons-en pour ajouter quelques associations de classe. D'abord nous ajouterons des gens à notre application, et stockerons une liste d'événements auxquels ils participent.

Nous allons ajouter une collection d'événements à la classe Person. De cette manière nous pouvons facilement naviguer dans les événements d'une personne particulière, sans exécuter une requête explicite - en appelant aPerson.getEvents(). Nous utilisons une collection Java, un Set, parce que la collection ne contiendra pas d'éléments dupliqués et l'ordre ne nous importe pas pour ces exemples :

public class Person {


    private Set events = new HashSet();
    public Set getEvents() {
        return events;
    }
    public void setEvents(Set events) {
        this.events = events;
    }
}

D'abord nous mappons cette association, mais pensez à l'autre côté. Clairement, nous pouvons la laisser unidirectionnelle. Ou bien, nous pourrions créer une autre collection sur Event, si nous voulons être capable de la parcourir de manière bidirectionnelle. Ce n'est pas nécessaire d'un point de vue fonctionnel. Vous pourrez toujours exécuter une requête explicite pour récupérer les participants d'un évènement particulier. Vous êtes libre de choisir la conception, ce qui est certain, c'est que la cardinalité de l'association : "plusieurs" valués des deux côtés, est appelée plusieurs-à-plusieurs. Par conséquent nous utilisons un mappage Hibernate plusieurs-à-plusieurs :


<class name="Person" table="PERSON">
    <id name="id" column="PERSON_ID">
        <generator class="native"/>
    </id>
    <property name="age"/>
    <property name="firstname"/>
    <property name="lastname"/>

    <set name="events" table="PERSON_EVENT">
        <key column="PERSON_ID"/>
        <many-to-many column="EVENT_ID" class="Event"/>
    </set>

</class>

Hibernate supporte toutes sortes de mappage de collection, un set étant le plus commun. Pour une association plusieurs-à-plusieurs (ou une relation d'entité n:m), une table d'association est requise. Chaque ligne dans cette table représente un lien entre une personne et un événement. Le nom de la table est configuré avec l'attribut table de l'élément set. Le nom de la colonne identifiant dans l'association, du côté de la personne, est défini avec l'élément key, et le nom de la colonne pour l'événement avec l'attribut column de many-to-many. Vous devez aussi donner à Hibernate la classe des objets de votre collection (c'est-à-dire : la classe de l'autre côté de la collection).

Le schéma de base de données pour ce mappage est donc :

    _____________        __________________
   |             |      |                  |       _____________
   |   EVENTS    |      |   PERSON_EVENT   |      |             |
   |_____________|      |__________________|      |    PERSON   |
   |             |      |                  |      |_____________|
   | *EVENT_ID   | <--> | *EVENT_ID        |      |             |
   |  EVENT_DATE |      | *PERSON_ID       | <--> | *PERSON_ID  |
   |  TITLE      |      |__________________|      |  AGE        |
   |_____________|                                |  FIRSTNAME  |
                                                  |  LASTNAME   |
                                                  |_____________|
 

Réunissons quelques personnes et quelques événements dans une nouvelle méthode dans EventManager :

    private void addPersonToEvent(Long personId, Long eventId) {

        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        session.beginTransaction();
        Person aPerson = (Person) session.load(Person.class, personId);
        Event anEvent = (Event) session.load(Event.class, eventId);
        aPerson.getEvents().add(anEvent);
        session.getTransaction().commit();
    }

Après le chargement d'une Person et d'un Event, modifiez simplement la collection en utilisant les méthodes normales de la collection. Comme vous pouvez le constater, il n'y a pas d'appel explicite à update() ou save(), Hibernate détecte automatiquement que la collection a été modifiée et a besoin d'être mise à jour. Ceci est appelé la vérification sale automatique (automatic dirty checking), et vous pouvez aussi l'essayer en modifiant le nom ou la propriété date de n'importe lequel de vos objets. Tant qu'ils sont dans un état persistant, c'est-à-dire, liés à une Session Hibernate particulière (c-à-d qu'ils ont juste été chargés ou sauvegardés dans une unité de travail), Hibernate surveille les changements et exécute le SQL correspondants. Le processus de synchronisation de l'état de la mémoire avec la base de données, généralement seulement à la fin d'une unité de travail, est appelé flushing. Dans notre code, l'unité de travail s'achève par un commit (ou rollback) de la transaction avec la base de données.

Vous pourriez bien sûr charger une personne et un événement dans différentes unités de travail. Ou vous modifiez un objet à l'extérieur d'une Session, s'il n'est pas dans un état persistant (s'il était persistant avant, nous appelons cet état détaché). Vous pouvez même modifier une collection lorsqu'elle est détachée :

    private void addPersonToEvent(Long personId, Long eventId) {

        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        session.beginTransaction();
        Person aPerson = (Person) session
                .createQuery("select p from Person p left join fetch p.events where p.id = :pid")
                .setParameter("pid", personId)
                .uniqueResult(); // Eager fetch the collection so we can use it detached
        Event anEvent = (Event) session.load(Event.class, eventId);
        session.getTransaction().commit();
        // End of first unit of work
        aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached
        // Begin second unit of work
        Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
        session2.beginTransaction();
        session2.update(aPerson); // Reattachment of aPerson
        session2.getTransaction().commit();
    }

L'appel à update rend un objet détaché à nouveau persistant, vous pourriez dire qu'il le lie à une nouvelle unité de travail, ainsi toutes les modifications que vous avez faites pendant qu'il était détaché peuvent être sauvegardées dans la base de données, cela inclut toute modification effectuées sur une collection de cet objet entité.

Cela n'a pas grand intérêt dans notre situation, mais c'est un concept important qu'il vous faut concevoir dans votre application. Pour le moment, complétez cet exercice en ajoutant une nouvelle action à la méthode principale de l'EventManager et invoquez-la depuis la ligne de commande. Si vous avez besoin des identifiants d'un client et d'un évènement - la méthode save() vous les retourne (vous devrez peut-être modifier certaines méthodes précédentes pour retourner ces identifiants) :

        else if (args[0].equals("addpersontoevent")) {

            Long eventId = mgr.createAndStoreEvent("My Event", new Date());
            Long personId = mgr.createAndStorePerson("Foo", "Bar");
            mgr.addPersonToEvent(personId, eventId);
            System.out.println("Added person " + personId + " to event " + eventId);
        }

C'était un exemple d'une association entre deux classes de même importance, deux entités. Comme mentionné plus tôt, il y a d'autres classes et d'autres types dans un modèle typique, généralement "moins importants". Vous en avez déjà vu certains, comme un int ou une String. Nous appelons ces classes des types de valeur, et leurs instances dépendent d'une entité particulière. Des instances de ces types n'ont pas leur propre identité, elles ne sont pas non plus partagées entre des entités (deux personnes ne référencent pas le même objet firstname, même si elles ont le même prénom). Bien sûr, des types de valeur n'existent pas seulement dans le JDK (en fait, dans une application Hibernate toutes les classes du JDK sont considérées comme des types de valeur), vous pouvez aussi écrire vous-même des classes dépendantes, Address ou MonetaryAmount, par exemple.

Vous pouvez aussi concevoir une collection de types de valeur. C'est conceptuellement très différent d'une collection de références vers d'autres entités, mais très ressemblant dans Java.

Ajoutons un ensemble d'adresses email à l'entité Person qui sera représenté en tant que java.util.Set d'instance java.lang.String :

    private Set emailAddresses = new HashSet();


    public Set getEmailAddresses() {
        return emailAddresses;
    }
    public void setEmailAddresses(Set emailAddresses) {
        this.emailAddresses = emailAddresses;
    }

Le mappage de ce Set :


        <set name="emailAddresses" table="PERSON_EMAIL_ADDR">
            <key column="PERSON_ID"/>
            <element type="string" column="EMAIL_ADDR"/>
        </set>

La différence comparée au mappage vu plus tôt est la partie element, qui indique à Hibernate que la collection ne contient pas de référence vers une autre entité, mais une collection d'éléments de type String (le nom en minuscule vous indique que c'est un type/convertisseur du mappage Hibernate). Une fois encore, l'attribut table de l'élément set détermine le nom de la table pour la collection. L'élément key définit le nom de la colonne de la clé étrangère dans la table de la collection. L'attribut column dans l'élément element définit le nom de la colonne où les valeurs de String seront réellement stockées.

Considérons le schéma mis à jour :

  _____________        __________________
 |             |      |                  |       _____________
 |   EVENTS    |      |   PERSON_EVENT   |      |             |       ___________________
 |_____________|      |__________________|      |    PERSON   |      |                   |
 |             |      |                  |      |_____________|      | PERSON_EMAIL_ADDR |
 | *EVENT_ID   | <--> | *EVENT_ID        |      |             |      |___________________|
 |  EVENT_DATE |      | *PERSON_ID       | <--> | *PERSON_ID  | <--> |  *PERSON_ID       |
 |  TITLE      |      |__________________|      |  AGE        |      |  *EMAIL_ADDR      |
 |_____________|                                |  FIRSTNAME  |      |___________________|
                                                |  LASTNAME   |
                                                |_____________|
 

Vous pouvez voir que la clé primaire de la table de la collection est en fait une clé composée, utilisant les deux colonnes. Ceci implique aussi qu'il ne peut pas y avoir d'adresses email dupliquées par personne, ce qui est exactement la sémantique dont nous avons besoin pour un ensemble dans Java.

Vous pouvez maintenant tester et ajouter des éléments à cette collection, juste comme nous l'avons fait auparavant en liant des personnes et des événements. C'est le même code dans Java.

    private void addEmailToPerson(Long personId, String emailAddress) {

        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        session.beginTransaction();
        Person aPerson = (Person) session.load(Person.class, personId);
        // adding to the emailAddress collection might trigger a lazy load of the collection
        aPerson.getEmailAddresses().add(emailAddress);
        session.getTransaction().commit();
    }

Cette fois-ci, nous n'avons pas utilisé de requête de chargement fetch pour initialiser la collection. Traquez les logs SQL et tentez d'optimiser ce cas avec un chargement agressif.

Ensuite nous allons mapper une association bidirectionnelle - faire fonctionner l'association entre une personne et un événement à partir des deux côtés dans Java. Bien sûr, le schéma de la base de données ne change pas, nous avons toujours une pluralité plusieurs-à-plusieurs.

D'abord, ajoutez une collection de participants à la classe Event :

    private Set participants = new HashSet();


    public Set getParticipants() {
        return participants;
    }
    public void setParticipants(Set participants) {
        this.participants = participants;
    }

Maintenant mappez ce côté de l'association aussi, dans Event.hbm.xml.


        <set name="participants" table="PERSON_EVENT" inverse="true">
            <key column="EVENT_ID"/>
            <many-to-many column="PERSON_ID" class="Person"/>
        </set
>

Comme vous le voyez, ce sont des mappages de sets normaux dans les deux documents de mappage. Notez que les noms de colonne dans key et many-to-many sont inversés dans les 2 documents de mappage. L'ajout le plus important ici est l'attribut inverse="true" dans l'élément set du mappage de la collection des Events.

Cela signifie que Hibernate devrait prendre l'autre côté - la classe Person - quand il a besoin de trouver des informations à propos du lien entre les deux. Ce sera beaucoup plus facile à comprendre une fois que vous verrez comment le lien bidirectionnel entre les deux entités est créé.

Premièrement, gardez à l'esprit qu'Hibernate n'affecte pas la sémantique normale de Java. Comment avons-nous créé un lien entre une Person et un Event dans l'exemple unidirectionnel? Nous avons ajouté une instance de Event à la collection des références d'événement d'une instance de Person. Donc, évidemment, si vous voulons rendre ce lien bidirectionnel, nous devons faire la même chose de l'autre côté, en ajoutant une référence de Person à la collection dans un Event. Cette "configuration du lien des deux côtés" est absolument nécessaire et vous ne devriez jamais oublier de le faire.

Beaucoup de développeurs programment de manière défensive et créent des méthodes de gestion de lien pour affecter correctement les deux côtés, par exemple dans Person :

    protected Set getEvents() {

        return events;
    }
    protected void setEvents(Set events) {
        this.events = events;
    }
    public void addToEvent(Event event) {
        this.getEvents().add(event);
        event.getParticipants().add(this);
    }
    public void removeFromEvent(Event event) {
        this.getEvents().remove(event);
        event.getParticipants().remove(this);
    }

Notez que les méthodes get et set pour la collection sont maintenant protégées - ceci permet aux classes et aux sous-classes du même paquetage d'accéder aux méthodes, mais empêche quiconque de mettre le désordre directement dans les collections (enfin, presque). Vous devriez probablement faire de même avec la collection de l'autre côté.

Et à propos de l'attribut de mappage inverse ? Pour vous, et pour Java, un lien bidirectionnel consiste simplement à configurer correctement les références des deux côtés. Hibernate n'a cependant pas assez d'informations pour ordonner correctement les expressions SQL INSERT et UPDATE (pour éviter les violations de contrainte), et a besoin d'aide pour gérer proprement les associations bidirectionnelles. Rendre inverse un côté de l'association, indique à Hibernate de l'ignorer, pour le considérer comme un miroir de l'autre côté. Cela suffit à Hibernate pour gérer tous les problèmes de transformation d'un modèle de navigation directionnelle vers un schéma SQL de base de données. Les règles dont vous devez vous souvenir sont : toutes les associations bidirectionnelles ont besoin d'un côté marqué inverse. Dans une association un-à-plusieurs ce doit être le côté plusieurs, dans une association plusieurs-à-plusieurs, vous pouvez choisir n'importe quel côté, il n'y pas de différence.

Une application web Hibernate utilise la Session et Transaction comme une application autonome. Cependant, quelques modèles communs sont utiles. Nous allons coder une EventManagerServlet. Ce servlet peut lister tous les évènements stockés dans la base de données, et fournir une formulaire HTML pour saisir de nouveaux évènements.

Tout d'abord, nous devons créer notre servlet de base. La servlet n'accepte que les requêtes HTTP GET, la méthode à implémenter est donc doGet() :

package org.hibernate.tutorial.web;


// Imports
public class EventManagerServlet extends HttpServlet {
    protected void doGet(
            HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        SimpleDateFormat dateFormatter = new SimpleDateFormat( "dd.MM.yyyy" );
        try {
            // Begin unit of work
            HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();
            // Process request and render page...
            // End unit of work
            HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().commit();
        }
        catch (Exception ex) {
            HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().rollback();
            if ( ServletException.class.isInstance( ex ) ) {
                throw ( ServletException ) ex;
            }
            else {
                throw new ServletException( ex );
            }
        }
    }
}

Servir la servlet en tant que src/main/java/org/hibernate/tutorial/web/EventManagerServlet.java

Le modèle appliqué ici est appelé session-per-request. Lorsqu'une requête appelle la servlet, une nouvelle Session Hibernate est ouverte à la première invocation de getCurrentSession() sur la SessionFactory. Ensuite, une transaction avec la base de données est démarrée - tous les accès à la base de données interviennent au sein de la transaction, peu importe que les données soient lues ou écrites (nous n'utilisons pas le mode auto-commit dans les applications).

N'utilisez pas une nouvelle Session Hibernate pour chaque opération en base de données. Utilisez une Session Hibernate qui porte sur l'ensemble de la requête. Utlisez getCurrentSession(), ainsi elle est automatiquement attachée au thread Java courant.

Ensuite, les actions possibles de la requêtes sont exécutées et la réponse HTML est rendue. Nous y reviendrons ultérieurement.

Enfin, l'unité de travail s'achève lorsque l'exécution et le rendu sont achevés. Si un problème survient lors de ces deux phases, une exception est lancée et la transaction avec la base de données subit un rollback. Cela complète le modèle session-per-request. Au lieu d'avoir un code de délimitant les transactions au sein de chaque servlet, vous pouvez écrire un filtre de servlet. Voir le site Hibernate et le Wiki pour plus d'informations sur ce modèle, appelé Open Session in View - vous en aurez besoin dès que vous utiliserez des JSP et non des servlets pour le rendu de vos vues.

Implémentons l'exécution de la requête et le rendu de la page.

        // Write HTML header

        PrintWriter out = response.getWriter();
        out.println("<html><head><title>Event Manager</title></head><body>");
        // Handle actions
        if ( "store".equals(request.getParameter("action")) ) {
            String eventTitle = request.getParameter("eventTitle");
            String eventDate = request.getParameter("eventDate");
            if ( "".equals(eventTitle) || "".equals(eventDate) ) {
                out.println("<b><i>Please enter event title and date.</i></b>");
            }
            else {
                createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
                out.println("<b><i>Added event.</i></b>");
            }
        }
        // Print page
       printEventForm(out);
       listEvents(out, dateFormatter);
       // Write HTML footer
       out.println("</body></html>");
       out.flush();
       out.close();

Ce style de code avec une mixture de Java et d'HTML ne serait pas extensible dans une application plus complexe - gardez à l'esprit que nous ne faisons qu'illustrer les concepts basiques de Hibernate dans ce didacticiel. Ce code affiche une entête et un pied de page HTML. Dans cette page, sont affichés un formulaire pour la saisie d'évènements ainsi qu'une liste de tous les évènements de la base de données. La première méthode est triviale et ne fait que sortir de l'HTML :

    private void printEventForm(PrintWriter out) {

        out.println("<h2>Add new event:</h2>");
        out.println("<form>");
        out.println("Title: <input name='eventTitle' length='50'/><br/>");
        out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");
        out.println("<input type='submit' name='action' value='store'/>");
        out.println("</form>");
    }

La méthode listEvents() utilise la Session Hibernate liée au thread courant pour exécuter la requête :

    private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {


        List result = HibernateUtil.getSessionFactory()
                .getCurrentSession().createCriteria(Event.class).list();
        if (result.size() > 0) {
            out.println("<h2>Events in database:</h2>");
            out.println("<table border='1'>");
            out.println("<tr>");
            out.println("<th>Event title</th>");
            out.println("<th>Event date</th>");
            out.println("</tr>");
            Iterator it = result.iterator();
            while (it.hasNext()) {
                Event event = (Event) it.next();
                out.println("<tr>");
                out.println("<td>" + event.getTitle() + "</td>");
                out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
                out.println("</tr>");
            }
            out.println("</table>");
        }
    }

Enfin, l'action store renvoie à la méthode createAndStoreEvent(), qui utilise aussi la Session du thread courant:

    protected void createAndStoreEvent(String title, Date theDate) {

        Event theEvent = new Event();
        theEvent.setTitle(title);
        theEvent.setDate(theDate);
        HibernateUtil.getSessionFactory()
                .getCurrentSession().save(theEvent);
    }

La servlet est complétée. Une requête à la servlet sera exécutée par une seule Session et Transaction. Comme dans l'application autonome vue auparavant, Hibernate peut automatiquement lier ces objets au thread courant d'exécution. Cela vous laisse la liberté de séparer votre code en couches et d'accéder à la SessionFactory selon le moyen que vous aurez choisi. Généralement, vous utiliserez des conceptions plus sophistiquées et déplacerez le code d'accès aux données dans une couche DAO. Consultez le wiki Hibernate pour plus d'exemples.

Pour déployer cette application en vue de procéder à des tests, nous devons créer un WAR (Web ARchive). Tout d'abord, nous devons définir le descripteur WAR en tant que src/main/webapp/WEB-INF/web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <servlet>
        <servlet-name>Event Manager</servlet-name>
        <servlet-class>org.hibernate.tutorial.web.EventManagerServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>Event Manager</servlet-name>
        <url-pattern>/eventmanager</url-pattern>
    </servlet-mapping>
</web-app>

Pour construire et déployer, appelez ant war dans votre projet et copiez le fichier hibernate-tutorial.war dans le répertoire webapp de Tomcat.

Note

If you do not have Tomcat installed, download it from http://tomcat.apache.org/ and follow the installation instructions. Our application requires no changes to the standard Tomcat configuration.

Une fois l'application déployée et Tomcat lancé, accédez à l'application via http://localhost:8080/hibernate-tutorial/eventmanager. Assurez vous de consulter les traces Tomcat pour observer l'initialisation d'Hibernate à la première requête touchant votre servlet (l'initialisation statique dans HibernateUtil est invoquée) et pour vérifier qu'aucune exception ne survienne.

Le diagramme ci-dessus procure une vue - (très) haut niveau - de l'architecture Hibernate :

Unfortunately we cannot provide a detailed view of all possible runtime architectures. Hibernate is sufficiently flexible to be used in a number of ways in many, many architectures. We will, however, illustrate 2 specifically since they are extremes.

Here are quick discussions about some of the API objects depicted in the preceding diagrams (you will see them again in more detail in later chapters).

SessionFactory (org.hibernate.SessionFactory)

A thread-safe, immutable cache of compiled mappings for a single database. A factory for org.hibernate.Session instances. A client of org.hibernate.connection.ConnectionProvider. Optionally maintains a second level cache of data that is reusable between transactions at a process or cluster level.

Session (org.hibernate.Session)

A single-threaded, short-lived object representing a conversation between the application and the persistent store. Wraps a JDBC java.sql.Connection. Factory for org.hibernate.Transaction. Maintains a first level cache of persistent the application's persistent objects and collections; this cache is used when navigating the object graph or looking up objects by identifier.

Objets et collections persistants

Short-lived, single threaded objects containing persistent state and business function. These can be ordinary JavaBeans/POJOs. They are associated with exactly one org.hibernate.Session. Once the org.hibernate.Session is closed, they will be detached and free to use in any application layer (for example, directly as data transfer objects to and from presentation). Chapitre 11, Travailler avec des objets discusses transient, persistent and detached object states.

Objets et collections éphémères (transient) et détachés

Instances of persistent classes that are not currently associated with a org.hibernate.Session. They may have been instantiated by the application and not yet persisted, or they may have been instantiated by a closed org.hibernate.Session. Chapitre 11, Travailler avec des objets discusses transient, persistent and detached object states.

Transaction (org.hibernate.Transaction)

(Optional) A single-threaded, short-lived object used by the application to specify atomic units of work. It abstracts the application from the underlying JDBC, JTA or CORBA transaction. A org.hibernate.Session might span several org.hibernate.Transactions in some cases. However, transaction demarcation, either using the underlying API or org.hibernate.Transaction, is never optional.

ConnectionProvider (org.hibernate.connection.ConnectionProvider)

(Optional) A factory for, and pool of, JDBC connections. It abstracts the application from underlying javax.sql.DataSource or java.sql.DriverManager. It is not exposed to application, but it can be extended and/or implemented by the developer.

TransactionFactory (org.hibernate.TransactionFactory)

(Optional) A factory for org.hibernate.Transaction instances. It is not exposed to the application, but it can be extended and/or implemented by the developer.

Extension Interfaces

Hibernate fournit de nombreuses interfaces d'extensions optionnelles que vous pouvez implémenter pour personnaliser le comportement de votre couche de persistance. Reportez vous à la documentation de l'API pour plus de détails.

Certaines applications utilisant Hibernate ont besoin d'une sorte de session "contextuelle", où une session donnée est en effet liée à la portée d'un contexte particulier. Cependant, les applications ne définissent pas toutes la notion de contexte de la même manière, et différents contextes définissent différentes portées à la notion de "courant". Les applications qui utilisaient Hibernate, versions précédentes à la 3.0, avaient tendance à employer un principe maison de sessions contextuelles basées sur le ThreadLocal, ainsi que sur des classes utilitaires comme HibernateUtil, ou utilisaient des framework tiers (comme Spring ou Pico) qui fournissaient des sessions contextuelles basées sur l'utilisation de proxy/interception.

A partir de la version 3.0.1, Hibernate a ajouté la méthode SessionFactory.getCurrentSession(). Initialement, cela demandait l'usage de transactions JTA, où la transaction JTA définissait la portée et le contexte de la session courante. L'équipe Hibernate pense que, étant donnée la maturité des nombreuses implémentations autonomes du JTA TransactionManager, la plupart (sinon toutes) des applications devraient utiliser la gestion des transactions par JTA qu'elles soient ou non déployées dans un conteneur J2EE. Par conséquent, il vous suffira de contextualiser vos sessions via la méthode basée sur JTA.

Cependant, depuis la version 3.1, la logique derrière SessionFactory.getCurrentSession() est désormais enfichable. A cette fin, une nouvelle interface d'extension(org.hibernate.context.CurrentSessionContext et un nouveau paramètre de configuration hibernate.current_session_context_class ont été ajoutés pour enficher la portée et le contexte de sessions courantes caractéristiques.

Pour une description détaillée de son contrat, consultez les Javadocs de l'interface org.hibernate.context.CurrentSessionContext. Elle définit une seule méthode, currentSession(), par laquelle l'implémentation est responsable de traquer la session contextuelle courante. Hibernate fournit trois implémentations de cette interface :

The first two implementations provide a "one session - one database transaction" programming model. This is also known and used as session-per-request. The beginning and end of a Hibernate session is defined by the duration of a database transaction. If you use programmatic transaction demarcation in plain JSE without JTA, you are advised to use the Hibernate Transaction API to hide the underlying transaction system from your code. If you use JTA, you can utilize the JTA interfaces to demarcate transactions. If you execute in an EJB container that supports CMT, transaction boundaries are defined declaratively and you do not need any transaction or session demarcation operations in your code. Refer to Chapitre 13, Transactions et Accès concurrents for more information and code examples.

Le paramètre de configuration hibernate.current_session_context_class définit quelle implémentation de org.hibernate.context.CurrentSessionContext doit être utilisée. Notez que pour assurer la compatibilité avec les versions précédentes, si ce paramètre n'est pas défini mais qu'un org.hibernate.transaction.TransactionManagerLookup est configuré, Hibernate utilisera le org.hibernate.context.JTASessionContext. La valeur de ce paramètre devrait juste nommer la classe d'implémentation à utiliser. Pour les trois implémentations prêtes à utiliser, toutefois, il y a trois noms brefs correspondants : "jta", "thread" et "managed".

Hibernate est conçu pour fonctionner dans de nombreux environnements , c'est pourquoi il existe beaucoup de paramètres de configuration. Heureusement, la plupart ont des valeurs par défaut appropriées et la Hibernate inclut un fichier d'exemples hibernate.properties dans le répertoire etc/ qui fournit les différentes options. Vous n'avez qu'à placer ce fichier dans votre classpath et à l'adapter à vos besoins.

Une instance de org.hibernate.cfg.Configuration représente un ensemble de mappages des classes Java d'une application vers la base de données SQL. La Configuration est utilisée pour construire un objet (immuable) SessionFactory. Les mappages sont constitués d'un ensemble de fichiers de mappage XML.

Vous pouvez obtenir une instance de Configuration en l'instanciant directement et en spécifiant la liste des documents XML de mappage. Si les fichiers de mappage sont dans le classpath, vous pouvez utiliser la méthode addResource() :

Configuration cfg = new Configuration()

    .addResource("Item.hbm.xml")
    .addResource("Bid.hbm.xml");

Une solution alternative consiste à spécifier la classe mappée et à donner à Hibernate la possibilité de trouver les documents de mappage pour vous :

Configuration cfg = new Configuration()

    .addClass(org.hibernate.auction.Item.class)
    .addClass(org.hibernate.auction.Bid.class);

Hibernate va rechercher les fichiers de mappages /org/hibernate/auction/Item.hbm.xml et /org/hibernate/auction/Bid.hbm.xml dans le classpath. Cette approche élimine les noms de fichiers en dur.

Une Configuration vous permet également de préciser des propriétés de configuration. Par exemple :

Configuration cfg = new Configuration()

    .addClass(org.hibernate.auction.Item.class)
    .addClass(org.hibernate.auction.Bid.class)
    .setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLInnoDBDialect")
    .setProperty("hibernate.connection.datasource", "java:comp/env/jdbc/test")
    .setProperty("hibernate.order_updates", "true");

Ce n'est pas le seul moyen de passer des propriétés de configuration à Hibernate. Les différentes options sont :

Si vous souhaitez démarrer rapidement, hibernate.properties est l'approche la plus facile.

org.hibernate.cfg.Configuration est un objet de démarrage qui sera supprimé une fois qu'une SessionFactory aura été créée.

Il est conseillé que org.hibernate.SessionFactory crée les connexions JDBC et les mette dans un pool pour vous. Si vous suivez cette approche, ouvrir une org.hibernate.Session est aussi simple que :

Session session = sessions.openSession(); // open a new Session

Dès que vous initierez une action qui requiert un accès à la base de données, une connexion JDBC sera récupérée dans le pool.

À cet effet, il faut passer les propriétés de la connexion JDBC à Hibernate. Tous les noms des propriétés Hibernate et leur signification sont définies dans la classe org.hibernate.cfg.Environment. Nous allons maintenant décrire les paramètres de configuration des connexions JDBC les plus importants.

Hibernate obtiendra des connexions (et les mettra dans un pool) en utilisant java.sql.DriverManager si vous positionnez les paramètres de la manière suivante :


L'algorithme natif de pool de connexions de Hibernate est plutôt rudimentaire. Il a été conçu dans le but de vous aider à démarrer et n'est pas prévu pour un système en production ou même pour un test de performance. Utilisez plutôt un pool tiers pour de meilleures performances et une meilleure stabilité : remplacez la propriété hibernate.connection.pool_size avec les propriétés spécifiques au pool de connexions que vous avez choisi. Cela désactivera le pool de connexions interne de Hibernate. Vous pouvez par exemple utiliser C3P0.

C3P0 est un pool de connexions JDBC open source distribué avec Hibernate dans le répertoire lib. Hibernate utilisera son provider C3P0ConnectionProvider pour le pool de connexions si vous configurez les propriétés hibernate.c3p0.*. Si vous voulez utiliser Proxool, référez vous au groupe de propriétés hibernate.properties correspondant et consultez le site web Hibernate pour plus d'informations.

Voici un exemple de fichier hibernate.properties pour C3P0:

hibernate.connection.driver_class = org.postgresql.Driver
hibernate.connection.url = jdbc:postgresql://localhost/mydatabase
hibernate.connection.username = myuser
hibernate.connection.password = secret
hibernate.c3p0.min_size=5
hibernate.c3p0.max_size=20
hibernate.c3p0.timeout=1800
hibernate.c3p0.max_statements=50
hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect

Pour l'utilisation de Hibernate au sein d'un serveur d'applications, il est recommandé de configurer Hibernate presque toujours de façon à ce qu'il obtienne ses connexions de la DataSource enregistrée du serveur d'applications dans le JNDI. À cet effet, vous devrez définir au moins une des propriétés suivantes :


Voici un exemple de fichier hibernate.properties pour l'utilisation d'une datasource JNDI fournie par un serveur d'applications :

hibernate.connection.datasource = java:/comp/env/jdbc/test
hibernate.transaction.factory_class = \
    org.hibernate.transaction.JTATransactionFactory
hibernate.transaction.manager_lookup_class = \
    org.hibernate.transaction.JBossTransactionManagerLookup
hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect

Les connexions JDBC obtenues à partir d'une datasource JNDI participeront automatiquement aux transactions gérées par le conteneur du serveur d'applications.

Des propriétés arbitraires de connexion peuvent être passées en préfixant le nom de la propriété par "hibernate.connnection". Par exemple, vous pouvez spécifier un charSet en utilisant hibernate.connection.charSet.

Vous pouvez fournir votre propre stratégie d'obtention des connexions JDBC en implémentant l'interface org.hibernate.connection.ConnectionProvider. Vous pouvez sélectionner une implémentation spécifique par la propriété hibernate.connection.provider_class.

Il y a un certain nombre d'autres propriétés qui contrôlent le fonctionnement d'Hibernate à l'exécution. Toutes sont optionnelles et ont comme valeurs par défaut des valeurs raisonnables.

Tableau 3.3. Propriétés de configuration Hibernate

Nom de la propriétéFonction
hibernate.dialectLe nom de la classe d'un org.hibernate.dialect.Dialect Hibernate qui permet à Hibernate de générer du SQL optimisé pour une base de données relationnelle particulière.

par ex.full.classname.of.Dialect

Dans la plupart des cas, Hibernate sera en mesure de choisir l'implémentation org.hibernate.dialect.Dialect qui convient sur la base des métadonnées JDBC retournées par le driver JDBC.

hibernate.show_sqlÉcrit toutes les requêtes SQL sur la console. Il s'agit d'une alternative au paramétrage de la catégorie de log org.hibernate.SQL à debug.

par ex.true | false

hibernate.format_sqlEffectue un pretty print du SQL dans la console et dans le log.

par ex.true | false

hibernate.default_schemaQualifie des noms de table non qualifiés avec le schéma/tablespace dans le SQL généré.

e.g. SCHEMA_NAME

hibernate.default_catalogQualifie les noms de tables non qualifiées avec ce catalogue dans le SQL généré.

e.g. CATALOG_NAME

hibernate.session_factory_nameorg.hibernate.SessionFactory sera automatiquement liée à ce nom dans JNDI après sa création.

par ex.jndi/composite/name

hibernate.max_fetch_depthConfigure la profondeur maximale d'un arbre de chargement par jointures externes pour les associations à cardinalité unitaire (un-à-un, plusieurs-à-un). Un 0 désactive le chargement par défaut par jointure externe.

par ex. valeurs recommandées entre 0 et 3

hibernate.default_batch_fetch_sizeConfigure une taille par défaut pour le chargement par lot des associations Hibernate

ex. valeurs recommandées : 4, 8, 16

hibernate.default_entity_modeSets a default mode for entity representation for all sessions opened from this SessionFactory

dynamic-map, dom4j, pojo

hibernate.order_updatesForce Hibernate à trier les mises à jour SQL par la valeur de la clé primaire des éléments mis à jour. Cela permet de limiter les deadlocks de transaction dans les systèmes hautement concurrents.

par ex.true | false

hibernate.generate_statisticsSi activé, Hibernate va collecter des statistiques utiles pour le réglage des performances.

par ex.true | false

hibernate.use_identifier_rollbackSi activé, les propriétés correspondant à l'identifiant des objets sont remises aux valeurs par défaut lorsque les objets sont supprimés.

par ex.true | false

hibernate.use_sql_commentsSi activé, Hibernate génère des commentaires à l'intérieur des requêtes SQL pour faciliter le débogage, par défaut à false.

par ex.true | false

hibernate.id.new_generator_mappingsSetting is relevant when using @GeneratedValue. It indicates whether or not the new IdentifierGenerator implementations are used for javax.persistence.GenerationType.AUTO, javax.persistence.GenerationType.TABLE and javax.persistence.GenerationType.SEQUENCE. Default to false to keep backward compatibility.

par ex.true | false


Note

We recommend all new projects which make use of to use @GeneratedValue to also set hibernate.id.new_generator_mappings=true as the new generators are more efficient and closer to the JPA 2 specification semantic. However they are not backward compatible with existing databases (if a sequence or a table is used for id generation).

Tableau 3.4. Propriétés Hibernate liées à JDBC et aux connexions

Nom de la propriétéFonction
hibernate.jdbc.fetch_sizeUne valeur non nulle détermine la taille des chargements JDBC (appelle Statement.setFetchSize()).
hibernate.jdbc.batch_sizeUne valeur non nulle active l'utilisation par Hibernate des mise à jour par lot de JDBC2.

ex. les valeurs recommandées entre 5 et 30

hibernate.jdbc.batch_versioned_dataSet this property to true if your JDBC driver returns correct row counts from executeBatch(). It is usually safe to turn this option on. Hibernate will then use batched DML for automatically versioned data. Defaults to false.

par ex.true | false

hibernate.jdbc.factory_classSélectionne un org.hibernate.jdbc.Batcher personnalisé. La plupart des applications n'auront pas besoin de cette propriété de configuration.

par.ex. classname.of.BatcherFactory

hibernate.jdbc.use_scrollable_resultsetActive l'utilisation par Hibernate des ensembles de résultats déroulants de JDBC2. Cette propriété est seulement nécessaire lorsque l'on utilise des connexions JDBC fournies par l'utilisateur. Autrement, Hibernate utilise les métadonnées de la connexion.

par ex.true | false

hibernate.jdbc.use_streams_for_binaryUtilise des flux lorsque l'on écrit/lit des types binary ou des types serializablevers/à partir de JDBC. *system-level property*

par ex.true | false

hibernate.jdbc.use_get_generated_keysActive l'utilisation de PreparedStatement.getGeneratedKeys() de JDBC3 pour récupérer nativement les clés générées après insertion. Nécessite un pilote JDBC3+ et JRE1.4+, configurés à false si votre pilote a des problèmes avec les générateurs d'identifiant Hibernate. Par défaut, essaie de déterminer les possibilités du pilote en utilisant les metadonnées de connexion.

par ex. true|false

hibernate.connection.provider_classLe nom de la classe d'un org.hibernate.connection.ConnectionProvider personnalisé qui fournit des connexions JDBC à Hibernate.

par ex.classname.of.ConnectionProvider

hibernate.connection.isolationDéfinit le niveau d'isolation des transactions JDBC. Regardez java.sql.Connection pour des valeurs significatives mais notez également que la plupart des bases de données ne supportent pas tous les niveaux d'isolation et que certaines définissent des isolations non standard supplémentaires.

par ex.1, 2, 4, 8

hibernate.connection.autocommitActive le mode de commit automatique (autocommit) pour les connexions JDBC du pool (non recommandé).

par ex.true | false

hibernate.connection.release_modeSpécifie à quel moment Hibernate doit relâcher les connexions JDBC. Par défaut, une connexion JDBC est conservée jusqu'à ce que la session soit explicitement fermée ou déconnectée. Pour une source de données JTA d'un serveur d'applications, vous devriez utiliser after_statement pour libérer les connexions de manière plus agressive après chaque appel JDBC. Pour une connexion non JTA, il est souvent préférable de libérer la connexion à la fin de chaque transaction en utilisant after_transaction. auto choisira after_statement pour les stratégies de transactions JTA et CMT et after_transaction pour des stratégies de transactions JDBC.

e.g. auto (default) | on_close | after_transaction | after_statement

This setting only affects Sessions returned from SessionFactory.openSession. For Sessions obtained through SessionFactory.getCurrentSession, the CurrentSessionContext implementation configured for use controls the connection release mode for those Sessions. See Section 2.3, « Sessions contextuelles  »

hibernate.connection.<propertyName>Passez une propriété JDBC propertyName à DriverManager.getConnection().
hibernate.jndi.<propertyName>Passez la propriété <propertyName> au JNDI InitialContextFactory.

Tableau 3.5. Propriétés du Cache Hibernate

Nom de la propriétéFonction
hibernate.cache.provider_classLe nom de classe d'un CacheProvider personnalisé.

par ex.classname.of.CacheProvider

hibernate.cache.use_minimal_putsOptimise le cache de second niveau en minimisant les écritures, au prix de plus de lectures. Ce paramètre est surtout utile pour les caches en cluster,et est activé par défaut dans hibernate3 pour les implémentations de cache en cluster.

par ex. true|false

hibernate.cache.use_query_cacheActiver le cache de requête, les requêtes individuelles doivent tout de même être déclarées comme pouvant être mises en cache.

par ex. true|false

hibernate.cache.use_second_level_cachePeut être utilisé pour désactiver complètement le cache de second niveau qui est activé par défaut pour les classes qui spécifient un élément <cache> dans leur mappage.

par ex. true|false

hibernate.cache.query_cache_factoryLe nom de classe d'une interface QueryCache personnalisée, par défaut prend la valeur du StandardQueryCache imbriqué.

par ex.classname.of.QueryCache

hibernate.cache.region_prefixUn préfixe à utiliser pour les noms de régions du cache de second niveau.

par ex. prefix

hibernate.cache.use_structured_entriesForce Hibernate à stocker les données dans le cache de second niveau en un format plus adapté à la visualisation.

par ex. true|false

hibernate.cache.default_cache_concurrency_strategySetting used to give the name of the default org.hibernate.annotations.CacheConcurrencyStrategy to use when either @Cacheable or @Cache is used. @Cache(strategy="..") is used to override this default.


Tableau 3.7. Propriétés diverses

Nom de la propriétéFonction
hibernate.current_session_context_classSupply a custom strategy for the scoping of the "current" Session. See Section 2.3, « Sessions contextuelles  » for more information about the built-in strategies.

e.g. jta | thread | managed | custom.Class

hibernate.query.factory_classChoisit l'implémentation du parseur de requête HQL.

par ex.org.hibernate.hql.ast.ASTQueryTranslatorFactory ou org.hibernate.hql.classic.ClassicQueryTranslatorFactory

hibernate.query.substitutionsLien entre les jetons de requêtes Hibernate et les jetons SQL (les jetons peuvent être des fonctions ou des noms textuels par exemple).

par ex.hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC

hibernate.hbm2ddl.autoValide ou exporte automatiquement le schéma DDL vers la base de données lorsque la SessionFactory est créée. La valeur create-drop permet de supprimer le schéma de base de données lorsque la SessionFactory est fermée explicitement.

par ex.validate | update | create | create-drop

hibernate.hbm2ddl.import_files

Comma-separated names of the optional files containing SQL DML statements executed during the SessionFactory creation. This is useful for testing or demoing: by adding INSERT statements for example you can populate your database with a minimal set of data when it is deployed.

File order matters, the statements of a give file are executed before the statements of the following files. These statements are only executed if the schema is created ie if hibernate.hbm2ddl.auto is set to create or create-drop.

e.g. /humans.sql,/dogs.sql

hibernate.bytecode.use_reflection_optimizer

Enables the use of bytecode manipulation instead of runtime reflection. This is a System-level property and cannot be set in hibernate.cfg.xml. Reflection can sometimes be useful when troubleshooting. Hibernate always requires either CGLIB or javassist even if you turn off the optimizer.

par ex.true | false

hibernate.bytecode.provider

Both javassist or cglib can be used as byte manipulation engines; the default is javassist.

e.g. javassist | cglib


Il est recommandé de toujours positionner la propriété hibernate.dialect à la sous-classe de org.hibernate.dialect.Dialect appropriée à votre base de données. Si vous spécifiez un dialecte, Hibernate utilisera des valeurs adaptées pour certaines autres propriétés listées ci-dessus, vous évitant ainsi de l'effectuer à la main.


Hibernate utilise Simple Logging Facade for Java (SLF4J) pour enregistrer divers événements du système. SLF4J peut diriger votre sortie de logging vers plusieurs structures de loggings (NOP, Simple, log4j version 1.2, JDK 1.4 logging, JCL or logback) suivant la liaison que vous choisirez. Pour pouvoir configurer votre logging, vous aurez besoin de slf4j-api.jar dans votre chemin de classe, ainsi que du fichier jar pour votre liaison préférée - slf4j-log4j12.jar pour Log4J. Voir la documentation SLF4J documentation pour davantage d'informations. Pour utiliser Log4j, vous aurez aussi besoin de mettre un fichier log4j.properties dans votre chemin de classe. Un exemple de fichier de propriétés est distribué avec Hibernate dans le répertoire src/.

Il est vivement recommandé de vous familiariser avec les messages des logs de Hibernate. Beaucoup de soin a été apporté pour donner le plus de détails possible sans les rendre illisibles. C'est un outil essentiel en cas de problèmes. Les catégories de logs les plus intéressantes sont les suivantes :


Lorsque vous développez des applications avec Hibernate, vous devriez quasiment toujours travailler avec le niveau debug activé pour la catégorie org.hibernate.SQL, ou sinon avec la propriété hibernate.show_sql activée.

Une approche alternative est de spécifier toute la configuration dans un fichier nommé hibernate.cfg.xml. Ce fichier peut être utilisé à la place du fichier hibernate.properties, voire même peut servir à surcharger les propriétés si les deux fichiers sont présents.

Le fichier de configuration XML doit par défaut se placer à la racine du CLASSPATH. En voici un exemple :


<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <!-- a SessionFactory instance listed as /jndi/name -->
    <session-factory
        name="java:hibernate/SessionFactory">

        <!-- properties -->
        <property name="connection.datasource">java:/comp/env/jdbc/MyDB</property>
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="show_sql">false</property>
        <property name="transaction.factory_class">
            org.hibernate.transaction.JTATransactionFactory
        </property>
        <property name="jta.UserTransaction">java:comp/UserTransaction</property>

        <!-- mapping files -->
        <mapping resource="org/hibernate/auction/Item.hbm.xml"/>
        <mapping resource="org/hibernate/auction/Bid.hbm.xml"/>

        <!-- cache settings -->
        <class-cache class="org.hibernate.auction.Item" usage="read-write"/>
        <class-cache class="org.hibernate.auction.Bid" usage="read-only"/>
        <collection-cache collection="org.hibernate.auction.Item.bids" usage="read-write"/>

    </session-factory>

</hibernate-configuration>

Comme vous pouvez le constater, l'avantage de cette approche est l'externalisation des noms des fichiers de mappage de la configuration. Le fichier hibernate.cfg.xml est également plus pratique quand on commence à régler le cache d'Hibernate. Notez que vous pouvez choisir entre utiliser hibernate.properties ou hibernate.cfg.xml, les deux sont équivalents, sauf en ce qui concerne les bénéfices de l'utilisation de la syntaxe XML mentionnés ci-dessus.

Avec la configuration XML, démarrer Hibernate devient donc aussi simple que ceci :

SessionFactory sf = new Configuration().configure().buildSessionFactory();

Vous pouvez choisir un fichier de configuration XML différent en utilisant :

SessionFactory sf = new Configuration()

    .configure("catdb.cfg.xml")
    .buildSessionFactory();

Hibernate possède les points d'intégration suivants pour l'infrastructure J2EE :

En fonction de votre environnement, vous mettrez l'option de configuration hibernate.connection.aggressive_release à true si le serveur d'applications affiche des exceptions de type "connection containment".

L'API de la Session Hibernate est indépendante de tout système de démarcation des transactions, présent dans votre architecture. Si vous laissez Hibernate utiliser l'API JDBC directement via un pool de connexion, vous commencerez et terminerez vos transactions en appelant l'API JDBC. Si votre application tourne à l'intérieur d'un serveur d'applications J2EE, vous utiliserez peut être les transactions gérées par les beans (BMT) et vous appellerez l'API JTA et UserTransaction lorsque cela est nécessaire.

Pour conserver votre code portable entre ces deux environnements (et d'autres), nous vous recommandons d'utiliser l'API optionnelle Transaction d'Hibernate, qui encapsule et masque le système de transaction sous-jacent. Pour cela, vous devez préciser une classe de fabrique d'instances de Transaction en positionnant la propriété de configuration hibernate.transaction.factory_class.

Il existe trois choix standards (intégrés) :

Vous pouvez également définir vos propres stratégies transactionnelles (pour un service de transaction CORBA par exemple).

Certaines fonctionnalités de Hibernate (c'est-à-dire le cache de second niveau, l'association automatique des Sessions à JTA, etc.) nécessitent l'accès au TransactionManager JTA dans un environnement géré. Dans un serveur d'applications, vous devez indiquer comment Hibernate peut obtenir une référence vers le TransactionManager, car J2EE ne fournit pas un seul mécanisme standard.


Une SessionFactory Hibernate associée au JNDI peut simplifier l'accès à la fabrique et donc la création de nouvelles Session s. Notez que cela n'est pas lié avec les Datasource associées au JNDI, elles utilisent juste le même registre !

Si vous désirez associer la SessionFactory à un nom JNDI, spécifiez un nom (par ex. java:hibernate/SessionFactory) en utilisant la propriété hibernate.session_factory_name. Si cette propriété est omise, la SessionFactory ne sera pas associée au JNDI (c'est particulièrement pratique dans les environnements ayant une implémentation JNDI par défaut en lecture seule, comme c'est le cas pour Tomcat).

Lorsqu'il associe la SessionFactory au JNDI, Hibernate utilisera les valeurs de hibernate.jndi.url, hibernate.jndi.class pour instancier un contexte d'initialisation. S'ils ne sont pas spécifiés, l'InitialContext par défaut sera utilisé.

Hibernate va automatiquement placer la SessionFactory dans JNDI après avoir appelé cfg.buildSessionFactory(). Cela signifie que vous devez avoir cet appel dans un code de démarrage (ou dans une classe utilitaire) dans votre application sauf si vous utilisez le déploiement JMX avec le service HibernateService présenté plus tard dans ce document.

Si vous utilisez SessionFactory JNDI, un EJB ou n'importe quelle autre classe peut obtenir la SessionFactory en utilisant une recherche JNDI.

It is recommended that you bind the SessionFactory to JNDI in a managed environment and use a static singleton otherwise. To shield your application code from these details, we also recommend to hide the actual lookup code for a SessionFactory in a helper class, such as HibernateUtil.getSessionFactory(). Note that such a class is also a convenient way to startup Hibernate—see chapter 1.

The easiest way to handle Sessions and transactions is Hibernate's automatic "current" Session management. For a discussion of contextual sessions see Section 2.3, « Sessions contextuelles  ». Using the "jta" session context, if there is no Hibernate Session associated with the current JTA transaction, one will be started and associated with that JTA transaction the first time you call sessionFactory.getCurrentSession(). The Sessions retrieved via getCurrentSession() in the "jta" context are set to automatically flush before the transaction completes, close after the transaction completes, and aggressively release JDBC connections after each statement. This allows the Sessions to be managed by the life cycle of the JTA transaction to which it is associated, keeping user code clean of such management concerns. Your code can either use JTA programmatically through UserTransaction, or (recommended for portable code) use the Hibernate Transaction API to set transaction boundaries. If you run in an EJB container, declarative transaction demarcation with CMT is preferred.

La ligne cfg.buildSessionFactory() doit toujours être exécutée quelque part pour obtenir une SessionFactory dans JNDI. Vous pouvez faire cela dans un bloc d'initialisation static (comme celui qui se trouve dans la classe HibernateUtil) ou vous pouvez déployer Hibernate en temps que service géré.

Hibernate est distribué avec org.hibernate.jmx.HibernateService pour le déploiement sur un serveur d'applications avec le support de JMX comme JBoss AS. Le déploiement et la configuration sont spécifiques à chaque vendeur. Voici un fichier jboss-service.xml d'exemple pour JBoss 4.0.x :


<?xml version="1.0"?>
<server>

<mbean code="org.hibernate.jmx.HibernateService"
    name="jboss.jca:service=HibernateFactory,name=HibernateFactory">

    <!-- Required services -->
    <depends>jboss.jca:service=RARDeployer</depends>
    <depends>jboss.jca:service=LocalTxCM,name=HsqlDS</depends>

    <!-- Bind the Hibernate service to JNDI -->
    <attribute name="JndiName">java:/hibernate/SessionFactory</attribute>

    <!-- Datasource settings -->
    <attribute name="Datasource">java:HsqlDS</attribute>
    <attribute name="Dialect">org.hibernate.dialect.HSQLDialect</attribute>

    <!-- Transaction integration -->
    <attribute name="TransactionStrategy">
        org.hibernate.transaction.JTATransactionFactory</attribute>
    <attribute name="TransactionManagerLookupStrategy">
        org.hibernate.transaction.JBossTransactionManagerLookup</attribute>
    <attribute name="FlushBeforeCompletionEnabled">true</attribute>
    <attribute name="AutoCloseSessionEnabled">true</attribute>

    <!-- Fetching options -->
    <attribute name="MaximumFetchDepth">5</attribute>

    <!-- Second-level caching -->
    <attribute name="SecondLevelCacheEnabled">true</attribute>
    <attribute name="CacheProviderClass">org.hibernate.cache.EhCacheProvider</attribute>
    <attribute name="QueryCacheEnabled">true</attribute>

    <!-- Logging -->
    <attribute name="ShowSqlEnabled">true</attribute>

    <!-- Mapping files -->
    <attribute name="MapResources">auction/Item.hbm.xml,auction/Category.hbm.xml</attribute>

</mbean>

</server>

Ce fichier est déployé dans un répertoire META-INF et est empaqueté dans un fichier JAR avec l'extension .sar (service archive). Vous devez également empaqueter Hibernate, les librairies tierces requises, vos classes persistantes compilées et vos fichiers de mappage dans la même archive. Vos beans entreprise (souvent des EJB session) peuvent rester dans leur propre fichier JAR mais vous pouvez inclure ce fichier JAR dans le jar principal du service pour avoir une seule unité déployable à chaud. Vous pouvez consulter la documentation de JBoss AS pour plus d'informations sur les services JMX et le déploiement des EJB.

Persistent classes are classes in an application that implement the entities of the business problem (e.g. Customer and Order in an E-commerce application). The term "persistent" here means that the classes are able to be persisted, not that they are in the persistent state (see Section 11.1, « États des objets Hibernate » for discussion).

Hibernate works best if these classes follow some simple rules, also known as the Plain Old Java Object (POJO) programming model. However, none of these rules are hard requirements. Indeed, Hibernate assumes very little about the nature of your persistent objects. You can express a domain model in other ways (using trees of java.util.Map instances, for example).

Exemple 4.1. Simple POJO representing a cat

package eg;

import java.util.Set;
import java.util.Date;
public class Cat {
private Long id; // identifier
private Date birthdate;
private Color color;
private char sex;
private float weight;
    private int litterId;
    private Cat mother;
    private Set kittens = new HashSet();
    private void setId(Long id) {
        this.id=id;
    }
    public Long getId() {
        return id;
    }
    void setBirthdate(Date date) {
        birthdate = date;
    }
    public Date getBirthdate() {
        return birthdate;
    }
    void setWeight(float weight) {
        this.weight = weight;
    }
    public float getWeight() {
        return weight;
    }
    public Color getColor() {
        return color;
    }
    void setColor(Color color) {
        this.color = color;
    }
    void setSex(char sex) {
        this.sex=sex;
    }
    public char getSex() {
        return sex;
    }
    void setLitterId(int id) {
        this.litterId = id;
    }
    public int getLitterId() {
        return litterId;
    }
    void setMother(Cat mother) {
        this.mother = mother;
    }
    public Cat getMother() {
        return mother;
    }
    void setKittens(Set kittens) {
        this.kittens = kittens;
    }
    public Set getKittens() {
        return kittens;
    }
    // addKitten not needed by Hibernate
    public void addKitten(Cat kitten) {
        kitten.setMother(this);
    kitten.setLitterId( kittens.size() );
        kittens.add(kitten);
    }
}

On explore quatre règles principales de classes persistantes en détail dans les sections qui suivent :

Cat has a property named id. This property maps to the primary key column(s) of the underlying database table. The type of the identifier property can be any "basic" type (see ???). See Section 9.4, « Les composants en tant qu'identifiants composites » for information on mapping composite (multi-column) identifiers.

Note

Identifiers do not necessarily need to identify column(s) in the database physically defined as a primary key. They should just identify columns that can be used to uniquely identify rows in the underlying table.

Nous recommandons que vous déclariez les propriétés d'identifiant de manière uniforme. Nous recommandons également que vous utilisiez un type nullable (c'est-à-dire non primitif).

A central feature of Hibernate, proxies (lazy loading), depends upon the persistent class being either non-final, or the implementation of an interface that declares all public methods. You can persist final classes that do not implement an interface with Hibernate; you will not, however, be able to use proxies for lazy association fetching which will ultimately limit your options for performance tuning. To persist a final class which does not implement a "full" interface you must disable proxy generation. See Exemple 4.2, « Disabling proxies in hbm.xml » and Exemple 4.3, « Disabling proxies in annotations ».



If the final class does implement a proper interface, you could alternatively tell Hibernate to use the interface instead when generating the proxies. See Exemple 4.4, « Proxying an interface in hbm.xml » and Exemple 4.5, « Proxying an interface in annotations ».



You should also avoid declaring public final methods as this will again limit the ability to generate proxies from this class. If you want to use a class with public final methods, you must explicitly disable proxying. Again, see Exemple 4.2, « Disabling proxies in hbm.xml » and Exemple 4.3, « Disabling proxies in annotations ».

Vous devez surcharger les méthodes equals() et hashCode() si vous :

Hibernate garantit l'équivalence de l'identité persistante (ligne de base de données) et l'identité Java seulement à l'intérieur de la portée d'une session particulière. Donc dès que nous mélangeons des instances venant de différentes sessions, nous devons implémenter equals() et hashCode() si nous souhaitons avoir une sémantique correcte pour les Set s.

La manière la plus évidente est d'implémenter equals()/hashCode() en comparant la valeur de l'identifiant des deux objets. Si cette valeur est identique, les deux doivent représenter la même ligne de base de données, ils sont donc égaux (si les deux sont ajoutés à un Set, nous n'aurons qu'un seul élément dans le Set). Malheureusement, nous ne pouvons pas utiliser cette approche avec des identifiants générés ! Hibernate n'assignera de valeur d'identifiant qu'aux objets qui sont persistants, une instance nouvellement créée n'aura donc pas de valeur d'identifiant ! De plus, si une instance est non sauvegardée et actuellement dans un Set, le sauvegarder assignera une valeur d'identifiant à l'objet. Si equals() et hashCode() sont basées sur la valeur de l'identifiant, le code de hachage devrait changer, rompant le contrat du Set. Consultez le site web de Hibernate pour des informations plus approfondies. Notez que ceci n'est pas un problème Hibernate, mais concerne la sémantique normale de Java pour l'identité et l'égalité d'un objet.

Nous recommandons donc d'implémenter equals() et hashCode() en utilisant l'égalité par clé métier. L'égalité par clé métier signifie que la méthode equals() compare uniquement les propriétés qui forment une clé métier, une clé qui identifierait notre instance dans le monde réel (une clé candidate naturelle) :

public class Cat {


    ...
    public boolean equals(Object other) {
        if (this == other) return true;
        if ( !(other instanceof Cat) ) return false;
        final Cat cat = (Cat) other;
        if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
        if ( !cat.getMother().equals( getMother() ) ) return false;
        return true;
    }
    public int hashCode() {
        int result;
        result = getMother().hashCode();
        result = 29 * result + getLitterId();
        return result;
    }
}

A business key does not have to be as solid as a database primary key candidate (see Section 13.1.3, « L'identité des objets »). Immutable or unique properties are usually good candidates for a business key.

Les entités persistantes ne doivent pas nécessairement être représentées comme des classes POJO ou des objets JavaBean à l'exécution. Hibernate supporte aussi les modèles dynamiques (en utilisant des Map s de Map s à l'exécution) et la représentation des entités comme des arbres DOM4J. Avec cette approche, vous n'écrivez pas de classes persistantes, seulement des fichiers de mappage.

By default, Hibernate works in normal POJO mode. You can set a default entity representation mode for a particular SessionFactory using the default_entity_mode configuration option (see Tableau 3.3, « Propriétés de configuration Hibernate »).

Les exemples suivants démontrent la représentation utilisant des Map s. D'abord, dans le fichier de mappage, un entity-name doit être déclaré au lieu (ou en plus) d'un nom de classe :


<hibernate-mapping>

    <class entity-name="Customer">

        <id name="id"
            type="long"
            column="ID">
            <generator class="sequence"/>
        </id>

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

        <property name="address"
            column="ADDRESS"
            type="string"/>

        <many-to-one name="organization"
            column="ORGANIZATION_ID"
            class="Organization"/>

        <bag name="orders"
            inverse="true"
            lazy="false"
            cascade="all">
            <key column="CUSTOMER_ID"/>
            <one-to-many class="Order"/>
        </bag>

    </class>
    
</hibernate-mapping>

Notez que même si des associations sont déclarées en utilisant des noms de classe cible, le type de cible d'une association peut aussi être une entité dynamique au lieu d'un POJO.

Après avoir configuré le mode d'entité par défaut à dynamic-map pour la SessionFactory, nous pouvons lors de l'exécution fonctionner avec des Map s de Map s :

Session s = openSession();

Transaction tx = s.beginTransaction();
// Create a customer
Map david = new HashMap();
david.put("name", "David");
// Create an organization
Map foobar = new HashMap();
foobar.put("name", "Foobar Inc.");
// Link both
david.put("organization", foobar);
// Save both
s.save("Customer", david);
s.save("Organization", foobar);
tx.commit();
s.close();

Les avantages d'un mappage dynamique sont un gain de temps pour le prototypage sans la nécessité d'implémenter les classes d'entité. Pourtant, vous perdez la vérification du typage au moment de la compilation et aurez plus d'exceptions à gérer lors de l'exécution. Grâce au mappage de Hibernate, le schéma de la base de données peut facilement être normalisé et solidifié, permettant de rajouter une implémentation propre du modèle de domaine plus tard.

Les modes de représentation d'une entité peuvent aussi être configurés en se basant sur Session :

Session dynamicSession = pojoSession.getSession(EntityMode.MAP);


// Create a customer
Map david = new HashMap();
david.put("name", "David");
dynamicSession.save("Customer", david);
...
dynamicSession.flush();
dynamicSession.close()
...
// Continue on pojoSession

Veuillez noter que l'appel à getSession() en utilisant un EntityMode se fait sur l'API Session, et non sur SessionFactory. De cette manière, la nouvelle Session partage les connexions JDBC, transactions et autres informations de contexte sous-jacentes. Cela signifie que vous n'avez pas à appeler flush() et close() sur la Session secondaire, et laissez aussi la gestion de la transaction et de la connexion à l'unité de travail primaire.

More information about the XML representation capabilities can be found in Chapitre 20, Mappage XML.

org.hibernate.tuple.Tuplizer and its sub-interfaces are responsible for managing a particular representation of a piece of data given that representation's org.hibernate.EntityMode. If a given piece of data is thought of as a data structure, then a tuplizer is the thing that knows how to create such a data structure, how to extract values from such a data structure and how to inject values into such a data structure. For example, for the POJO entity mode, the corresponding tuplizer knows how create the POJO through its constructor. It also knows how to access the POJO properties using the defined property accessors.

There are two (high-level) types of Tuplizers:

Users can also plug in their own tuplizers. Perhaps you require that java.util.Map implementation other than java.util.HashMap be used while in the dynamic-map entity-mode. Or perhaps you need to define a different proxy generation strategy than the one used by default. Both would be achieved by defining a custom tuplizer implementation. Tuplizer definitions are attached to the entity or component mapping they are meant to manage. Going back to the example of our Customer entity, Exemple 4.6, « Specify custom tuplizers in annotations » shows how to specify a custom org.hibernate.tuple.entity.EntityTuplizer using annotations while Exemple 4.7, « Specify custom tuplizers in hbm.xml » shows how to do the same in hbm.xml



org.hibernate.EntityNameResolver is a contract for resolving the entity name of a given entity instance. The interface defines a single method resolveEntityName which is passed the entity instance and is expected to return the appropriate entity name (null is allowed and would indicate that the resolver does not know how to resolve the entity name of the given entity instance). Generally speaking, an org.hibernate.EntityNameResolver is going to be most useful in the case of dynamic models. One example might be using proxied interfaces as your domain model. The hibernate test suite has an example of this exact style of usage under the org.hibernate.test.dynamicentity.tuplizer2. Here is some of the code from that package for illustration.

/**

 * A very trivial JDK Proxy InvocationHandler implementation where we proxy an
 * interface as the domain model and simply store persistent state in an internal
 * Map.  This is an extremely trivial example meant only for illustration.
 */
public final class DataProxyHandler implements InvocationHandler {
        private String entityName;
        private HashMap data = new HashMap();
        public DataProxyHandler(String entityName, Serializable id) {
                this.entityName = entityName;
                data.put( "Id", id );
        }
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String methodName = method.getName();
                if ( methodName.startsWith( "set" ) ) {
                        String propertyName = methodName.substring( 3 );
                        data.put( propertyName, args[0] );
                }
                else if ( methodName.startsWith( "get" ) ) {
                        String propertyName = methodName.substring( 3 );
                        return data.get( propertyName );
                }
                else if ( "toString".equals( methodName ) ) {
                        return entityName + "#" + data.get( "Id" );
                }
                else if ( "hashCode".equals( methodName ) ) {
                        return new Integer( this.hashCode() );
                }
                return null;
        }
        public String getEntityName() {
                return entityName;
        }
        public HashMap getData() {
                return data;
        }
}
public class ProxyHelper {
    public static String extractEntityName(Object object) {
        // Our custom java.lang.reflect.Proxy instances actually bundle
        // their appropriate entity name, so we simply extract it from there
        // if this represents one of our proxies; otherwise, we return null
        if ( Proxy.isProxyClass( object.getClass() ) ) {
            InvocationHandler handler = Proxy.getInvocationHandler( object );
            if ( DataProxyHandler.class.isAssignableFrom( handler.getClass() ) ) {
                DataProxyHandler myHandler = ( DataProxyHandler ) handler;
                return myHandler.getEntityName();
            }
        }
        return null;
    }
    // various other utility methods ....
}
/**
 * The EntityNameResolver implementation.
 *
 * IMPL NOTE : An EntityNameResolver really defines a strategy for how entity names
 * should be resolved.  Since this particular impl can handle resolution for all of our
 * entities we want to take advantage of the fact that SessionFactoryImpl keeps these
 * in a Set so that we only ever have one instance registered.  Why?  Well, when it
 * comes time to resolve an entity name, Hibernate must iterate over all the registered
 * resolvers.  So keeping that number down helps that process be as speedy as possible.
 * Hence the equals and hashCode implementations as is
 */
public class MyEntityNameResolver implements EntityNameResolver {
    public static final MyEntityNameResolver INSTANCE = new MyEntityNameResolver();
    public String resolveEntityName(Object entity) {
        return ProxyHelper.extractEntityName( entity );
    }
    public boolean equals(Object obj) {
        return getClass().equals( obj.getClass() );
    }
    public int hashCode() {
        return getClass().hashCode();
    }
}
public class MyEntityTuplizer extends PojoEntityTuplizer {
        public MyEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
                super( entityMetamodel, mappedEntity );
        }
        public EntityNameResolver[] getEntityNameResolvers() {
                return new EntityNameResolver[] { MyEntityNameResolver.INSTANCE };
        }
    public String determineConcreteSubclassEntityName(Object entityInstance, SessionFactoryImplementor factory) {
        String entityName = ProxyHelper.extractEntityName( entityInstance );
        if ( entityName == null ) {
            entityName = super.determineConcreteSubclassEntityName( entityInstance, factory );
        }
        return entityName;
    }
    ...

Pour enregistrer un org.hibernate.EntityNameResolver, les utilisateurs doivent soit :

  1. Implement a custom tuplizer (see Section 4.5, « Tuplizers »), implementing the getEntityNameResolvers method

  2. L'enregistrer dans org.hibernate.impl.SessionFactoryImpl (qui est la classe d'implémentation de org.hibernate.SessionFactory) à l'aide de la méthode registerEntityNameResolver.

Object/relational mappings can be defined in three approaches:

Annotations are split in two categories, the logical mapping annotations (describing the object model, the association between two entities etc.) and the physical mapping annotations (describing the physical schema, tables, columns, indexes, etc). We will mix annotations from both categories in the following code examples.

JPA annotations are in the javax.persistence.* package. Hibernate specific extensions are in org.hibernate.annotations.*. You favorite IDE can auto-complete annotations and their attributes for you (even without a specific "JPA" plugin, since JPA annotations are plain Java 5 annotations).

Here is an example of mapping

package eg;


@Entity 
@Table(name="cats") @Inheritance(strategy=SINGLE_TABLE)
@DiscriminatorValue("C") @DiscriminatorColumn(name="subclass", discriminatorType=CHAR)
public class Cat {
   
   @Id @GeneratedValue
   public Integer getId() { return id; }
   public void setId(Integer id) { this.id = id; }
   private Integer id;
   public BigDecimal getWeight() { return weight; }
   public void setWeight(BigDecimal weight) { this.weight = weight; }
   private BigDecimal weight;
   @Temporal(DATE) @NotNull @Column(updatable=false)
   public Date getBirthdate() { return birthdate; }
   public void setBirthdate(Date birthdate) { this.birthdate = birthdate; }
   private Date birthdate;
   @org.hibernate.annotations.Type(type="eg.types.ColorUserType")
   @NotNull @Column(updatable=false)
   public ColorType getColor() { return color; }
   public void setColor(ColorType color) { this.color = color; }
   private ColorType color;
   @NotNull @Column(updatable=false)
   public String getSex() { return sex; }
   public void setSex(String sex) { this.sex = sex; }
   private String sex;
   @NotNull @Column(updatable=false)
   public Integer getLitterId() { return litterId; }
   public void setLitterId(Integer litterId) { this.litterId = litterId; }
   private Integer litterId;
   @ManyToOne @JoinColumn(name="mother_id", updatable=false)
   public Cat getMother() { return mother; }
   public void setMother(Cat mother) { this.mother = mother; }
   private Cat mother;
   @OneToMany(mappedBy="mother") @OrderBy("litterId")
   public Set<Cat> getKittens() { return kittens; }
   public void setKittens(Set<Cat> kittens) { this.kittens = kittens; }
   private Set<Cat> kittens = new HashSet<Cat>();
}
@Entity @DiscriminatorValue("D")
public class DomesticCat extends Cat {
   public String getName() { return name; }
   public void setName(String name) { this.name = name }
   private String name;
}
@Entity
public class Dog { ... }

The legacy hbm.xml approach uses an XML schema designed to be readable and hand-editable. The mapping language is Java-centric, meaning that mappings are constructed around persistent class declarations and not table declarations.

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

Commençons avec un exemple de mappage :


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
      "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
          "http://www.hibernate.org/dtd/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>

We will now discuss the concepts of the mapping documents (both annotations and XML). We will only describe, however, the document elements and attributes that are used by Hibernate at runtime. The mapping document also contains some extra optional attributes and elements that affect the database schemas exported by the schema export tool (for example, the not-null attribute).

An entity is a regular Java object (aka POJO) which will be persisted by Hibernate.

To mark an object as an entity in annotations, use the @Entity annotation.

@Entity

public class Flight implements Serializable {
    Long id;
    @Id
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
}         

That's pretty much it, the rest is optional. There are however any options to tweak your entity mapping, let's explore them.

@Table lets you define the table the entity will be persisted into. If undefined, the table name is the unqualified class name of the entity. You can also optionally define the catalog, the schema as well as unique constraints on the table.

@Entity

@Table(name="TBL_FLIGHT", 
       schema="AIR_COMMAND", 
       uniqueConstraints=
           @UniqueConstraint(
               name="flight_number", 
               columnNames={"comp_prefix", "flight_number"} ) )
public class Flight implements Serializable {
    @Column(name="comp_prefix")
    public String getCompagnyPrefix() { return companyPrefix; }
    @Column(name="flight_number")
    public String getNumber() { return number; }
}

The constraint name is optional (generated if left undefined). The column names composing the constraint correspond to the column names as defined before the Hibernate NamingStrategy is applied.

@Entity.name lets you define the shortcut name of the entity you can used in JP-QL and HQL queries. It defaults to the unqualified class name of the class.

Hibernate goes beyond the JPA specification and provide additional configurations. Some of them are hosted on @org.hibernate.annotations.Entity:

Some entities are not mutable. They cannot be updated or deleted by the application. This allows Hibernate to make some minor performance optimizations.. Use the @Immutable annotation.

You can also alter how Hibernate deals with lazy initialization for this class. On @Proxy, use lazy=false to disable lazy fetching (not recommended). You can also specify an interface to use for lazy initializing proxies (defaults to the class itself): use proxyClass on @Proxy. Hibernate will initially return proxies (Javassist or CGLIB) that implement the named interface. The persistent object will load when a method of the proxy is invoked. See "Initializing collections and proxies" below.

@BatchSize specifies a "batch size" for fetching instances of this class by identifier. Not yet loaded instances are loaded batch-size at a time (default 1).

You can specific an arbitrary SQL WHERE condition to be used when retrieving objects of this class. Use @Where for that.

In the same vein, @Check lets you define an SQL expression used to generate a multi-row check constraint for automatic schema generation.

There is no difference between a view and a base table for a Hibernate mapping. This is transparent at the database level, although some DBMS do not support views properly, especially with updates. Sometimes you want to use a view, but you cannot create one in the database (i.e. with a legacy schema). In this case, you can map an immutable and read-only entity to a given SQL subselect expression using @org.hibernate.annotations.Subselect:

@Entity

@Subselect("select item.name, max(bid.amount), count(*) "
        + "from item "
        + "join bid on bid.item_id = item.id "
        + "group by item.name")
@Synchronize( {"item", "bid"} ) //tables impacted
public class Summary {
    @Id
    public String getId() { return id; }
    ...
}

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 <subselect> est disponible comme attribut ou comme élément de mappage imbriqué.

We will now explore the same options using the hbm.xml structure. You can declare a persistent class using the class element. For example:

<class
        name="(1)ClassName"
        table=(2)"tableName"
        discri(3)minator-value="discriminator_value"
        mutabl(4)e="true|false"
        schema(5)="owner"
        catalo(6)g="catalog"
        proxy=(7)"ProxyInterface"
        dynami(8)c-update="true|false"
        dynami(9)c-insert="true|false"
        select(10)-before-update="true|false"
        polymo(11)rphism="implicit|explicit"
        where=(12)"arbitrary sql where condition"
        persis(13)ter="PersisterClass"
        batch-(14)size="N"
        optimi(15)stic-lock="none|version|dirty|all"
        lazy="(16)true|false"
        entity(17)-name="EntityName"
        check=(18)"arbitrary sql check condition"
        rowid=(19)"rowid"
        subsel(20)ect="SQL expression"
        abstra(21)ct="true|false"
        node="element-name"
/>

1

name (optionnel) : le nom Java complet de la classe (ou interface) persistante. Si cet attribut est absent, nous supposons que ce mappage 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 différentes sous-classes utilisées dans le comportement polymorphique. 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-mappage>.

6

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

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 SQL UPDATE 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 SQL INSERT 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 SQL UPDATE 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 SQL SELECT pour déterminer si un SQL UPDATE est véritablement nécessaire.

11

polymorphisms (optional - defaults to implicit): determines whether implicit or explicit query polymorphisms is used.

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 lot" 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 verrouillage optimiste.

(16)

lazy (optionnel) : l'extraction différée (lazy fetching) peut être totalement désactivée en configurant lazy="false".

(17)

entity-name (optional - defaults to the class name): Hibernate3 allows a class to be mapped multiple times, potentially to different tables. It also allows entity mappings that are represented by Maps or XML at the Java level. In these cases, you should provide an explicit arbitrary name for the entity. See Section 4.4, « Modèles dynamiques » and Chapitre 20, Mappage XML for more information.

(18)

check (optionnel) : expression SQL utilisée pour générer une contrainte de vérification check 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 mise à jour rapides si cette option vaut rowid. Un ROWID est un détail d'implémentation et représente la localisation physique d'un uplet 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 de base, mais à éviter. Voir plus bas pour plus d'informations.

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

Here is how to do a virtual view (subselect) in XML:


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

The <subselect> is available both as an attribute and a nested mapping element.

Mapped classes must declare the primary key column of the database table. Most classes will also have a JavaBeans-style property holding the unique identifier of an instance.

Mark the identifier property with @Id.

@Entity

public class Person {
   @Id Integer getId() { ... }
   ...
}

In hbm.xml, use the <id> element which defines the mapping from that property to the primary key column.

<id
        name="(1)propertyName"
        type="(2)typename"
        column(3)="column_name"
        unsave(4)d-value="null|any|none|undefined|id_value"
        access(5)="field|property|ClassName">
        node="element-name|@attribute-name|element/@attribute|."

        <generator class="generatorClass"/>
</id>

1

name (optionnel) : nom de la propriété de l'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 colonne de la clé primaire.

4

unsaved-value (optionnel - devient par défaut une valeur "sensible") : une valeur de 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.

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é d'identifiant.

The unsaved-value attribute is almost never needed in Hibernate3 and indeed has no corresponding element in annotations.

You can also declare the identifier as a composite identifier. This allows access to legacy data with composite keys. Its use is strongly discouraged for anything else.

You can define a composite primary key through several syntaxes:

As you can see the last case is far from obvious. It has been inherited from the dark ages of EJB 2 for backward compatibilities and we recommend you not to use it (for simplicity sake).

Let's explore all three cases using examples.

Here is a simple example of @EmbeddedId.

@Entity

class User {
   @EmbeddedId
   @AttributeOverride(name="firstName", column=@Column(name="fld_firstname")
   UserId id;
   Integer age;
}
@Embeddable
class UserId implements Serializable {
   String firstName;
   String lastName;
}

You can notice that the UserId class is serializable. To override the column mapping, use @AttributeOverride.

An embedded id can itself contains the primary key of an associated entity.

@Entity

class Customer {
   @EmbeddedId CustomerId id;
   boolean preferredCustomer;
   @MapsId("userId")
   @JoinColumns({
      @JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
      @JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
   })
   @OneToOne User user;
}
@Embeddable
class CustomerId implements Serializable {
   UserId userId;
   String customerNumber;
   //implements equals and hashCode
}
@Entity 
class User {
   @EmbeddedId UserId id;
   Integer age;
}
@Embeddable
class UserId implements Serializable {
   String firstName;
   String lastName;
   //implements equals and hashCode
}

In the embedded id object, the association is represented as the identifier of the associated entity. But you can link its value to a regular association in the entity via the @MapsId annotation. The @MapsId value correspond to the property name of the embedded id object containing the associated entity's identifier. In the database, it means that the Customer.user and the CustomerId.userId properties share the same underlying column (user_fk in this case).

In practice, your code only sets the Customer.user property and the user id value is copied by Hibernate into the CustomerId.userId property.

While not supported in JPA, Hibernate lets you place your association directly in the embedded id component (instead of having to use the @MapsId annotation).

@Entity

class Customer {
   @EmbeddedId CustomerId id;
   boolean preferredCustomer;
}
@Embeddable
class CustomerId implements Serializable {
   @OneToOne
   @JoinColumns({
      @JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
      @JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
   }) 
   User user;
   String customerNumber;
   //implements equals and hashCode
}
@Entity 
class User {
   @EmbeddedId UserId id;
   Integer age;
}
@Embeddable
class UserId implements Serializable {
   String firstName;
   String lastName;
   //implements equals and hashCode
}

Let's now rewrite these examples using the hbm.xml syntax.


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

First a simple example:


<class name="User">
   <composite-id name="id" class="UserId">
      <key-property name="firstName" column="fld_firstname"/>
      <key-property name="lastName"/>
   </composite-id>
</class>

Then an example showing how an association can be mapped.


<class name="Customer">
   <composite-id name="id" class="CustomerId">
      <key-property name="firstName" column="userfirstname_fk"/>
      <key-property name="lastName" column="userfirstname_fk"/>
      <key-property name="customerNumber"/>
   </composite-id>

   <property name="preferredCustomer"/>

   <many-to-one name="user">
      <column name="userfirstname_fk" updatable="false" insertable="false"/>
      <column name="userlastname_fk" updatable="false" insertable="false"/>
   </many-to-one>
</class>

<class name="User">
   <composite-id name="id" class="UserId">
      <key-property name="firstName"/>
      <key-property name="lastName"/>
   </composite-id>

   <property name="age"/>
</class>

Notice a few things in the previous example:

The last example shows how to map association directly in the embedded id component.


<class name="Customer">
   <composite-id name="id" class="CustomerId">
      <key-many-to-one name="user">
         <column name="userfirstname_fk"/>
         <column name="userlastname_fk"/>
      </key-many-to-one>
      <key-property name="customerNumber"/>
   </composite-id>

   <property name="preferredCustomer"/>
</class>

<class name="User">
   <composite-id name="id" class="UserId">
      <key-property name="firstName"/>
      <key-property name="lastName"/>
   </composite-id>

   <property name="age"/>
</class>

This is the recommended approach to map composite identifier. The following options should not be considered unless some constraint are present.

Another, arguably more natural, approach is to place @Id on multiple properties of your entity. This approach is only supported by Hibernate (not JPA compliant) but does not require an extra embeddable component.

@Entity

class Customer implements Serializable {
   @Id @OneToOne
   @JoinColumns({
      @JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
      @JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
   })
   User user;
  
   @Id String customerNumber;
   boolean preferredCustomer;
   //implements equals and hashCode
}
@Entity 
class User {
   @EmbeddedId UserId id;
   Integer age;
}
@Embeddable
class UserId implements Serializable {
   String firstName;
   String lastName;
   //implements equals and hashCode
}

In this case Customer is its own identifier representation: it must implement Serializable and must implement equals() and hashCode().

In hbm.xml, the same mapping is:


<class name="Customer">
   <composite-id>
      <key-many-to-one name="user">
         <column name="userfirstname_fk"/>
         <column name="userlastname_fk"/>
      </key-many-to-one>
      <key-property name="customerNumber"/>
   </composite-id>

   <property name="preferredCustomer"/>
</class>

<class name="User">
   <composite-id name="id" class="UserId">
      <key-property name="firstName"/>
      <key-property name="lastName"/>
   </composite-id>

   <property name="age"/>
</class>

@IdClass on an entity points to the class (component) representing the identifier of the class. The properties marked @Id on the entity must have their corresponding property on the @IdClass. The return type of search twin property must be either identical for basic properties or must correspond to the identifier class of the associated entity for an association.

@Entity

@IdClass(CustomerId.class)
class Customer implements Serializable {
   @Id @OneToOne
   @JoinColumns({
      @JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
      @JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
   }) 
   User user;
  
   @Id String customerNumber;
   boolean preferredCustomer;
}
class CustomerId implements Serializable {
   UserId user;
   String customerNumber;
   //implements equals and hashCode
}
@Entity 
class User {
   @EmbeddedId UserId id;
   Integer age;
   //implements equals and hashCode
}
@Embeddable
class UserId implements Serializable {
   String firstName;
   String lastName;
   //implements equals and hashCode
}

Customer and CustomerId do have the same properties customerNumber as well as user. CustomerId must be Serializable and implement equals() and hashCode().

While not JPA standard, Hibernate let's you declare the vanilla associated property in the @IdClass.

@Entity

@IdClass(CustomerId.class)
class Customer implements Serializable {
   @Id @OneToOne
   @JoinColumns({
      @JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
      @JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
   }) 
   User user;
  
   @Id String customerNumber;
   boolean preferredCustomer;
}
class CustomerId implements Serializable {
   @OneToOne User user;
   String customerNumber;
   //implements equals and hashCode
}
@Entity 
class User {
   @EmbeddedId UserId id;
   Integer age;
   //implements equals and hashCode
}
@Embeddable
class UserId implements Serializable {
  String firstName;
  String lastName;
}

This feature is of limited interest though as you are likely to have chosen the @IdClass approach to stay JPA compliant or you have a quite twisted mind.

Here are the equivalent on hbm.xml files:


<class name="Customer">
   <composite-id class="CustomerId" mapped="true">
      <key-many-to-one name="user">
         <column name="userfirstname_fk"/>
         <column name="userlastname_fk"/>
      </key-many-to-one>
      <key-property name="customerNumber"/>
   </composite-id>

   <property name="preferredCustomer"/>
</class>

<class name="User">
   <composite-id name="id" class="UserId">
      <key-property name="firstName"/>
      <key-property name="lastName"/>
   </composite-id>

   <property name="age"/>
</class>

Hibernate can generate and populate identifier values for you automatically. This is the recommended approach over "business" or "natural" id (especially composite ids).

Hibernate offers various generation strategies, let's explore the most common ones first that happens to be standardized by JPA:

To mark an id property as generated, use the @GeneratedValue annotation. You can specify the strategy used (default to AUTO) by setting strategy.

@Entity

public class Customer {
   @Id @GeneratedValue
   Integer getId() { ... };
}
@Entity 
public class Invoice {
   @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
   Integer getId() { ... };
}

SEQUENCE and TABLE require additional configurations that you can set using @SequenceGenerator and @TableGenerator:

  • name: name of the generator

  • table / sequenceName: name of the table or the sequence (defaulting respectively to hibernate_sequences and hibernate_sequence)

  • catalog / schema:

  • initialValue: the value from which the id is to start generating

  • allocationSize: the amount to increment by when allocating id numbers from the generator

In addition, the TABLE strategy also let you customize:

  • pkColumnName: the column name containing the entity identifier

  • valueColumnName: the column name containing the identifier value

  • pkColumnValue: the entity identifier

  • uniqueConstraints: any potential column constraint on the table containing the ids

To link a table or sequence generator definition with an actual generated property, use the same name in both the definition name and the generator value generator as shown below.

@Id 

@GeneratedValue(
    strategy=GenerationType.SEQUENCE, 
    generator="SEQ_GEN")
@javax.persistence.SequenceGenerator(
    name="SEQ_GEN",
    sequenceName="my_sequence",
    allocationSize=20
)
public Integer getId() { ... }        

The scope of a generator definition can be the application or the class. Class-defined generators are not visible outside the class and can override application level generators. Application level generators are defined in JPA's XML deployment descriptors (see XXXXXX ???):

<table-generator name="EMP_GEN"

            table="GENERATOR_TABLE"
            pk-column-name="key"
            value-column-name="hi"
            pk-column-value="EMP"
            allocation-size="20"/>
//and the annotation equivalent
@javax.persistence.TableGenerator(
    name="EMP_GEN",
    table="GENERATOR_TABLE",
    pkColumnName = "key",
    valueColumnName = "hi"
    pkColumnValue="EMP",
    allocationSize=20
)
<sequence-generator name="SEQ_GEN" 
    sequence-name="my_sequence"
    allocation-size="20"/>
//and the annotation equivalent
@javax.persistence.SequenceGenerator(
    name="SEQ_GEN",
    sequenceName="my_sequence",
    allocationSize=20
)
         

If a JPA XML descriptor (like META-INF/orm.xml) is used to define the generators, EMP_GEN and SEQ_GEN are application level generators.

Note

Package level definition is not supported by the JPA specification. However, you can use the @GenericGenerator at the package level (see ???).

These are the four standard JPA generators. Hibernate goes beyond that and provide additional generators or additional options as we will see below. You can also write your own custom identifier generator by implementing org.hibernate.id.IdentifierGenerator.

To define a custom generator, use the @GenericGenerator annotation (and its plural counter part @GenericGenerators) that describes the class of the identifier generator or its short cut name (as described below) and a list of key/value parameters. When using @GenericGenerator and assigning it via @GeneratedValue.generator, the @GeneratedValue.strategy is ignored: leave it blank.

@Id @GeneratedValue(generator="system-uuid")

@GenericGenerator(name="system-uuid", strategy = "uuid")
public String getId() {
@Id @GeneratedValue(generator="trigger-generated")
@GenericGenerator(
    name="trigger-generated", 
    strategy = "select",
    parameters = @Parameter(name="key", value = "socialSecurityNumber")
)
public String getId() {

The hbm.xml approach uses the optional <generator> child element inside <id>. If any parameters are required to configure or initialize the generator instance, they are passed using the <param> element.


<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 implémentent l'interface org.hibernate.id.IdentifierGenerator. C'est une interface très simple ; certaines applications peuvent proposer leurs propres 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

prend en charge les colonnes d'identité dans DB2, MySQL, MS SQL Server, Sybase et HypersonicSQL. L'identifiant renvoyé est de type long, short ou int.

sequence

utilise une séquence dans DB2, PostgreSQL, Oracle, SAP DB, McKoi ou 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 valeurs "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, en prenant une séquence en base nommée.

uuid

Generates a 128-bit UUID based on a custom algorithm. The value generated is represented as a string of 32 hexidecimal digits. Users can also configure it to use a separator (config parameter "separator") which separates the hexidecimal digits into 8{sep}8{sep}4{sep}8{sep}4. Note specifically that this is different than the IETF RFC 4122 representation of 8-4-4-4-12. If you need RFC 4122 compliant UUIDs, consider using "uuid2" generator discussed below.

uuid2

Generates a IETF RFC 4122 compliant (variant 2) 128-bit UUID. The exact "version" (the RFC term) generated depends on the pluggable "generation strategy" used (see below). Capable of generating values as java.util.UUID, java.lang.String or as a byte array of length 16 (byte[16]). The "generation strategy" is defined by the interface org.hibernate.id.UUIDGenerationStrategy. The generator defines 2 configuration parameters for defining which generation strategy to use:

Out of the box, comes with the following strategies:

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

permet à l'application d'affecter un identifiant à l'objet avant que la méthode 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 clé primaire assignée par un déclencheur (trigger) de base de données en sélectionnant la ligne par une clé unique quelconque et en extrayant la valeur de la clé primaire.

foreign

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

sequence-identity

Une stratégie de génération de séquence spécialisée qui utilise une séquence de base de données pour la génération réelle de valeurs, tout en utilisant JDBC3 getGeneratedKeys pour retourner effectivement la valeur d'identifiant générée, comme faisant partie de l'exécution de la déclaration insert. Cette stratégie est uniquement prise en charge par les pilotes Oracle 10g pour JDK 1.4. Notez que les commentaires sur ces déclarations insert sont désactivés à cause d'un bogue dans les pilotes d'Oracle.

A partir de la version 3.2.3, 2 générateurs représentent une nouvelle conception de 2 aspects séparés de la génération d'identifiants. Le premier aspect est la portabilité de la base de données; le second est l'optimization, c'est à dire que vous n'avez pas à interroger la base de données pour chaque requête de valeur d'identifiant. Ces deux nouveaux générateurs sont sensés prendre la place de générateurs décrits ci-dessus, ayant pour préfixe 3.3.x. Cependant, ils sont inclus dans les versions actuelles, et peuvent être référencés par FQN.

Le premier de ces nouveaux générateurs est org.Hibernate.ID.Enhanced.SequenceStyleGenerator qui est destiné, tout d'abord, comme un remplacement pour le générateur séquence et, deuxièmement, comme un générateur de portabilité supérieur à natif. C'est parce que natif a généralement le choix entre identité et séquence qui ont des sémantiques largement différentes, ce qui peut entraîner des problèmes subtils en observant la portabilité des applications. org.Hibernate.ID.Enhanced SequenceStyleGenerator., cependant, réalise la portabilité d'une manière différente. Il choisit entre une table ou une séquence dans la base de données pour stocker ses valeurs s'incrémentant, selon les capacités du dialecte utilisé. La différence avec natif c'est que de stockage basé sur les tables ou basé sur la séquence ont la même sémantique. En fait, les séquences sont exactement ce qu'Hibernate essaie d'émuler avec ses générateurs basée sur les tables. Ce générateur a un certain nombre de paramètres de configuration :

Le deuxième de ces nouveaux générateurs est org.Hibernate.ID.Enhanced.TableGenerator, qui est destiné, tout d'abord, comme un remplacement pour le générateur de la table, même si elle fonctionne effectivement beaucoup plus comme org.Hibernate.ID.MultipleHiLoPerTableGeneratoret deuxièmement, comme une remise en œuvre de org.Hibernate.ID.MultipleHiLoPerTableGenerator, qui utilise la notion d'optimizers enfichables. Essentiellement ce générateur définit une table susceptible de contenir un certain nombre de valeurs d'incrément différents simultanément à l'aide de plusieurs lignes distinctement masquées. Ce générateur a un certain nombre de paramètres de configuration :

  • table_name (en optin - valeur par défaut = hibernate_sequences): le nom de la table à utiliser.

  • value_column_name (en option - valeur par défaut =next_val): le nom de la colonne contenue dans la table utilisée pour la valeur.

  • segment_column_name (en option - par défaut = sequence_name): le nom de la colonne de la table qui est utilisée pour contenir la "segment key". Il s'agit de la valeur qui identifie la valeur d'incrément à utiliser.

  • segment_value (en option - par défaut = par défaut): La "segment key"valeur pour le segment à partir de laquelle nous voulons extraire des valeurs d'incrémentation pour ce générateur.

  • segment_value_length (en option - par défaut = 255): Utilisée pour la génération de schéma ; la taille de la colonne pour créer cette colonne de clé de segment.

  • initial_value (en option - par défaut est 1 : La valeur initiale à récupérer à partir de la table.

  • increment_size (en option - par défaut = 1): La valeur par laquelle les appels à la table, qui suivent, devront différer.

  • optimizer (optional - defaults to ??): See Section 5.1.2.3.1, « Optimisation du générateur d'identifiants ».

For identifier generators that 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 can 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.2.3, « La méthode getter de l'identifiant  » support this operation.

  • aucun (en général il s'agit de la valeur par défaut si aucun optimizer n'a été spécifié): n'effectuera pas d'optimisations et n'interrogera pas la base de données à chaque demande.

  • hilo: applique un algorithme hi/lo autour des valeurs extraites des base de données. Les valeurs de la base de données de cet optimizer sont censées être séquentielles. Les valeurs extraites de la structure des base de données pour cet optimizer indique le "numéro de groupe". Le increment_size est multiplié par cette valeur en mémoire pour définir un groupe de "hi value".

  • mise en commun: tout comme dans le cas de hilo, cet optimizer tente de réduire le nombre d'interrogations vers la base de données. Ici, cependant, nous avons simplement stocké la valeur de départ pour le "prochain groupe"dans la structure de la base de données plutôt qu'une valeur séquentielle en combinaison avec un algorithme de regroupement en mémoire. Ici, increment_size fait référence aux valeurs provenant de la base de données.

When using long transactions or conversations that span several database transactions, it is useful to store versioning data to ensure that if the same entity is updated by two conversations, the last to commit changes will be informed and not override the other conversation's work. It guarantees some isolation while still allowing for good scalability and works particularly well in read-often write-sometimes situations.

You can use two approaches: a dedicated version number or a timestamp.

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 clés composées .

You can add optimistic locking capability to an entity using the @Version annotation:

@Entity

public class Flight implements Serializable {
...
    @Version
    @Column(name="OPTLOCK")
    public Integer getVersion() { ... }
}           

The version property will be mapped to the OPTLOCK column, and the entity manager will use it to detect conflicting updates (preventing lost updates you might otherwise see with the last-commit-wins strategy).

The version column may be a numeric. Hibernate supports any kind of type provided that you define and implement the appropriate UserVersionType.

The application must not alter the version number set up by Hibernate in any way. To artificially increase the version number, check in Hibernate Entity Manager's reference documentation LockModeType.OPTIMISTIC_FORCE_INCREMENT or LockModeType.PESSIMISTIC_FORCE_INCREMENT.

If the version number is generated by the database (via a trigger for example), make sure to use @org.hibernate.annotations.Generated(GenerationTime.ALWAYS).

To declare a version property in hbm.xml, use:

<version
        column(1)="version_column"
        name="(2)propertyName"
        type="(3)typename"
        access(4)="field|property|ClassName"
        unsave(5)d-value="null|negative|undefined"
        genera(6)ted="never|always"
        insert(7)="true|false"
        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 que doit utiliser Hibernate pour accéder aux valeurs des propriétés.

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 la propritété identifiant devrait être utilisée.

6

generated (optional - defaults to never): specifies that this version property value is generated by the database. See the discussion of generated properties for more information.

7

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

Alternatively, you can use a timestamp. Timestamps are a less safe implementation of optimistic locking. However, sometimes an application might use the timestamps in other ways as well.

Simply mark a property of type Date or Calendar as @Version.

@Entity

public class Flight implements Serializable {
...
    @Version
    public Date getLastUpdate() { ... }
}           

When using timestamp versioning you can tell Hibernate where to retrieve the timestamp value from - database or JVM - by optionally adding the @org.hibernate.annotations.Source annotation to the property. Possible values for the value attribute of the annotation are org.hibernate.annotations.SourceType.VM and org.hibernate.annotations.SourceType.DB. The default is SourceType.DB which is also used in case there is no @Source annotation at all.

Like in the case of version numbers, the timestamp can also be generated by the database instead of Hibernate. To do that, use @org.hibernate.annotations.Generated(GenerationTime.ALWAYS).

In hbm.xml, use the <timestamp> element:

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

1

column (optionnel - par défaut devient 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 Java Date ou Timestamp de la classe persistante.

3

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

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 propriété 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 Dialect s ne supportent pas cette fonction, et que d'autres l'implémentent mal, à cause d'un manque de précision (Oracle 8 par exemple).

6

generated (optional - defaults to never): specifies that this timestamp property value is actually generated by the database. See the discussion of generated properties for more information.

You need to decide which property needs to be made persistent in a given entity. This differs slightly between the annotation driven metadata and the hbm.xml files.

In the annotations world, every non static non transient property (field or method depending on the access type) of an entity is considered persistent, unless you annotate it as @Transient. Not having an annotation for your property is equivalent to the appropriate @Basic annotation.

The @Basic annotation allows you to declare the fetching strategy for a property. If set to LAZY, specifies that this property should be fetched lazily when the instance variable is first accessed. It requires build-time bytecode instrumentation, if your classes are not instrumented, property level lazy loading is silently ignored. The default is EAGER. You can also mark a property as not optional thanks to the @Basic.optional attribute. This will ensure that the underlying column are not nullable (if possible). Note that a better approach is to use the @NotNull annotation of the Bean Validation specification.

Let's look at a few examples:

public transient int counter; //transient property


private String firstname; //persistent property
@Transient
String getLengthInMeter() { ... } //transient property
String getName() {... } // persistent property
@Basic
int getLength() { ... } // persistent property
@Basic(fetch = FetchType.LAZY)
String getDetailedComment() { ... } // persistent property
@Temporal(TemporalType.TIME)
java.util.Date getDepartureTime() { ... } // persistent property           
@Enumerated(EnumType.STRING)
Starred getNote() { ... } //enum persisted as String in database

counter, a transient field, and lengthInMeter, a method annotated as @Transient, and will be ignored by the Hibernate. name, length, and firstname properties are mapped persistent and eagerly fetched (the default for simple properties). The detailedComment property value will be lazily fetched from the database once a lazy property of the entity is accessed for the first time. Usually you don't need to lazy simple properties (not to be confused with lazy association fetching). The recommended alternative is to use the projection capability of JP-QL (Java Persistence Query Language) or Criteria queries.

JPA support property mapping of all basic types supported by Hibernate (all basic Java types , their respective wrappers and serializable classes). Hibernate Annotations supports out of the box enum type mapping either into a ordinal column (saving the enum ordinal) or a string based column (saving the enum string representation): the persistence representation, defaulted to ordinal, can be overridden through the @Enumerated annotation as shown in the note property example.

In plain Java APIs, the temporal precision of time is not defined. When dealing with temporal data you might want to describe the expected precision in database. Temporal data can have DATE, TIME, or TIMESTAMP precision (ie the actual date, only the time, or both). Use the @Temporal annotation to fine tune that.

@Lob indicates that the property should be persisted in a Blob or a Clob depending on the property type: java.sql.Clob, Character[], char[] and java.lang.String will be persisted in a Clob. java.sql.Blob, Byte[], byte[] and Serializable type will be persisted in a Blob.

@Lob

public String getFullText() {
    return fullText;
}
@Lob
public byte[] getFullCode() {
    return fullCode;
}

If the property type implements java.io.Serializable and is not a basic type, and if the property is not annotated with @Lob, then the Hibernate serializable type is used.

You can also manually specify a type using the @org.hibernate.annotations.Type and some parameters if needed. @Type.type could be:

If you do not specify a type, Hibernate will use reflection upon the named property and guess the correct Hibernate type. Hibernate will attempt to interpret the name of the return class of the property getter using, in order, rules 2, 3, and 4.

@org.hibernate.annotations.TypeDef and @org.hibernate.annotations.TypeDefs allows you to declare type definitions. These annotations can be placed at the class or package level. Note that these definitions are global for the session factory (even when defined at the class level). If the type is used on a single entity, you can place the definition on the entity itself. Otherwise, it is recommended to place the definition at the package level. In the example below, when Hibernate encounters a property of class PhoneNumer, it delegates the persistence strategy to the custom mapping type PhoneNumberType. However, properties belonging to other classes, too, can delegate their persistence strategy to PhoneNumberType, by explicitly using the @Type annotation.

@TypeDef(

   name = "phoneNumber",
   defaultForType = PhoneNumber.class,
   typeClass = PhoneNumberType.class
)
@Entity
public class ContactDetails {
   [...]
   private PhoneNumber localPhoneNumber;
   @Type(type="phoneNumber")
   private OverseasPhoneNumber overseasPhoneNumber;
   [...]
}

The following example shows the usage of the parameters attribute to customize the TypeDef.

//in org/hibernate/test/annotations/entity/package-info.java

@TypeDefs(
    {
    @TypeDef(
        name="caster",
        typeClass = CasterStringType.class,
        parameters = {
            @Parameter(name="cast", value="lower")
        }
    )
    }
)
package org.hibernate.test.annotations.entity;
//in org/hibernate/test/annotations/entity/Forest.java
public class Forest {
    @Type(type="caster")
    public String getSmallText() {
    ...
}      

When using composite user type, you will have to express column definitions. The @Columns has been introduced for that purpose.

@Type(type="org.hibernate.test.annotations.entity.MonetaryAmountUserType")

@Columns(columns = {
    @Column(name="r_amount"),
    @Column(name="r_currency")
})
public MonetaryAmount getAmount() {
    return amount;
}
public class MonetaryAmount implements Serializable {
    private BigDecimal amount;
    private Currency currency;
    ...
}

By default the access type of a class hierarchy is defined by the position of the @Id or @EmbeddedId annotations. If these annotations are on a field, then only fields are considered for persistence and the state is accessed via the field. If there annotations are on a getter, then only the getters are considered for persistence and the state is accessed via the getter/setter. That works well in practice and is the recommended approach.

However in some situations, you need to:

The best use case is an embeddable class used by several entities that might not use the same access type. In this case it is better to force the access type at the embeddable class level.

To force the access type on a given class, use the @Access annotation as showed below:

@Entity

public class Order {
   @Id private Long id;
   public Long getId() { return id; }
   public void setId(Long id) { this.id = id; }
   @Embedded private Address address;
   public Address getAddress() { return address; }
   public void setAddress() { this.address = address; }
}
@Entity
public class User {
   private Long id;
   @Id public Long getId() { return id; }
   public void setId(Long id) { this.id = id; }
   private Address address;
   @Embedded public Address getAddress() { return address; }
   public void setAddress() { this.address = address; }
}
@Embeddable
@Access(AcessType.PROPERTY)
public class Address {
   private String street1;
   public String getStreet1() { return street1; }
   public void setStreet1() { this.street1 = street1; }
   private hashCode; //not persistent
}

You can also override the access type of a single property while keeping the other properties standard.

@Entity

public class Order {
   @Id private Long id;
   public Long getId() { return id; }
   public void setId(Long id) { this.id = id; }
   @Transient private String userId;
   @Transient private String orderId;
   @Access(AccessType.PROPERTY)
   public String getOrderNumber() { return userId + ":" + orderId; }
   public void setOrderNumber() { this.userId = ...; this.orderId = ...; }
}

In this example, the default access type is FIELD except for the orderNumber property. Note that the corresponding field, if any must be marked as @Transient or transient.

The column(s) used for a property mapping can be defined using the @Column annotation. Use it to override default values (see the JPA specification for more information on the defaults). You can use this annotation at the property level for properties that are:

@Entity

public class Flight implements Serializable {
...
@Column(updatable = false, name = "flight_name", nullable = false, length=50)
public String getName() { ... }
            

The name property is mapped to the flight_name column, which is not nullable, has a length of 50 and is not updatable (making the property immutable).

This annotation can be applied to regular properties as well as @Id or @Version properties.

@Column(
    name="colu(1)mnName";
    boolean un(2)ique() default false;
    boolean nu(3)llable() default true;
    boolean in(4)sertable() default true;
    boolean up(5)datable() default true;
    String col(6)umnDefinition() default "";
    String tab(7)le() default "";
    int length(8)() default 255;
    int precis(9)ion() default 0; // decimal precision
    int scale((10)) default 0; // decimal scale

1

name (optional): the column name (default to the property name)

2

unique (optional): set a unique constraint on this column or not (default false)

3

nullable (optional): set the column as nullable (default true).

4

insertable (optional): whether or not the column will be part of the insert statement (default true)

5

updatable (optional): whether or not the column will be part of the update statement (default true)

6

columnDefinition (optional): override the sql DDL fragment for this particular column (non portable)

7

table (optional): define the targeted table (default primary table)

8

length (optional): column length (default 255)

8

precision (optional): column decimal precision (default 0)

10

scale (optional): column decimal scale if useful (default 0)

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

<property
        name="(1)propertyName"
        column(2)="column_name"
        type="(3)typename"
        update(4)="true|false"
        insert(4)="true|false"
        formul(5)a="arbitrary SQL expression"
        access(6)="field|property|ClassName"
        lazy="(7)true|false"
        unique(8)="true|false"
        not-nu(9)ll="true|false"
        optimi(10)stic-lock="true|false"
        genera(11)ted="never|insert|always"
        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> imbriqués.

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 déclarations SQL UPDATE et/ou des INSERT. Mettre les deux à false autorise une propriété pure dérivée dont la valeur est initialisée de quelque autre propriété qui mappe à la même colonne(s) ou par un trigger ou une autre application. (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èdent pas leur propre mappage.

6

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

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 (nécessite une instrumentation du bytecode lors de la phase de construction).

8

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

9

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

10

optimistic-lock (optionnel - par défaut à true) : indique si les mise à jour de cette propriété nécessitent ou non 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 - defaults to never): specifies that this property value is actually generated by the database. See the discussion of generated properties for more information.

typename peut être :

Si vous n'indiquez pas un type, Hibernate utilisera 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. 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 personnalisé).

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és en nommant une classe qui implémente l'interface org.hibernate.propertexige une instrumentation de code d'octets build-timey.PropertyAccessor.

Les propriétés dérivées représentent une fonctionnalité particulièrement intéressante. 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 mappage <formula> plutôt que d'utiliser l'attribut si vous le souhaitez.

Embeddable objects (or components) are objects whose properties are mapped to the same table as the owning entity's table. Components can, in turn, declare their own properties, components or collections

It is possible to declare an embedded component inside an entity and even override its column mapping. Component classes have to be annotated at the class level with the @Embeddable annotation. It is possible to override the column mapping of an embedded object for a particular entity using the @Embedded and @AttributeOverride annotation in the associated property:

@Entity

public class Person implements Serializable {
    // Persistent component using defaults
    Address homeAddress;
    @Embedded
    @AttributeOverrides( {
            @AttributeOverride(name="iso2", column = @Column(name="bornIso2") ),
            @AttributeOverride(name="name", column = @Column(name="bornCountryName") )
    } )
    Country bornIn;
    ...
}          
@Embeddable

public class Address implements Serializable {
    String city;
    Country nationality; //no overriding here
}            
@Embeddable

public class Country implements Serializable {
    private String iso2;
    @Column(name="countryName") private String name;
    public String getIso2() { return iso2; }
    public void setIso2(String iso2) { this.iso2 = iso2; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    ...
}            

An embeddable object inherits the access type of its owning entity (note that you can override that using the @Access annotation).

The Person entity has two component properties, homeAddress and bornIn. homeAddress property has not been annotated, but Hibernate will guess that it is a persistent component by looking for the @Embeddable annotation in the Address class. We also override the mapping of a column name (to bornCountryName) with the @Embedded and @AttributeOverride annotations for each mapped attribute of Country. As you can see, Country is also a nested component of Address, again using auto-detection by Hibernate and JPA defaults. Overriding columns of embedded objects of embedded objects is through dotted expressions.

@Embedded

    @AttributeOverrides( {
            @AttributeOverride(name="city", column = @Column(name="fld_city") ),
            @AttributeOverride(name="nationality.iso2", column = @Column(name="nat_Iso2") ),
            @AttributeOverride(name="nationality.name", column = @Column(name="nat_CountryName") )
            //nationality columns in homeAddress are overridden
    } )
    Address homeAddress;

Hibernate Annotations supports something that is not explicitly supported by the JPA specification. You can annotate a embedded object with the @MappedSuperclass annotation to make the superclass properties persistent (see @MappedSuperclass for more informations).

You can also use association annotations in an embeddable object (ie @OneToOne, @ManyToOne, @OneToMany or @ManyToMany). To override the association columns you can use @AssociationOverride.

If you want to have the same embeddable object type twice in the same entity, the column name defaulting will not work as several embedded objects would share the same set of columns. In plain JPA, you need to override at least one set of columns. Hibernate, however, allows you to enhance the default naming mechanism through the NamingStrategy interface. You can write a strategy that prevent name clashing in such a situation. DefaultComponentSafeNamingStrategy is an example of this.

If a property of the embedded object points back to the owning entity, annotate it with the @Parent annotation. Hibernate will make sure this property is properly loaded with the entity reference.

In XML, use the <component> element.

<component
        name="(1)propertyName"
        class=(2)"className"
        insert(3)="true|false"
        update(4)="true|false"
        access(5)="field|property|ClassName"
        lazy="(6)true|false"
        optimi(7)stic-lock="true|false"
        unique(8)="true|false"
        node="element-name|."
>

        <property ...../>
        <many-to-one .... />
        ........
</component>

1

name : le 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 (enfant) du composant.

3

insert : les colonnes mappées apparaissent-elles dans les SQL INSERT s ?

4

update: les colonnes mappées apparaissent-elles dans les SQL UPDATE s ?

5

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

6

lazy (optionnel - par défaut à false) : indique que ce composant doit être chargé en différé au premier accès à la variable d'instance (nécessite une instrumentation du bytecode lors de la phase de construction).

7

optimistic-lock (optionnel - par défaut à true) : spécifie si les mise à 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 balises enfant <property> mappent les propriétés de la classe enfant sur les colonnes de la table.

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

The <dynamic-component> element allows a Map to be mapped as a component, where the property names refer to keys of the map. See Section 9.5, « Les composants dynamiques » for more information. This feature is not supported in annotations.

Java is a language supporting polymorphism: a class can inherit from another. Several strategies are possible to persist a class hierarchy:

With this approach the properties of all the subclasses in a given mapped class hierarchy are stored in a single table.

Each subclass declares its own persistent properties and subclasses. Version and id properties are assumed to be inherited from the root class. Each subclass in a hierarchy must define a unique discriminator value. If this is not specified, the fully qualified Java class name is used.

@Entity

@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
    name="planetype",
    discriminatorType=DiscriminatorType.STRING
)
@DiscriminatorValue("Plane")
public class Plane { ... }
@Entity
@DiscriminatorValue("A320")
public class A320 extends Plane { ... }          

In hbm.xml, for the table-per-class-hierarchy mapping strategy, the <subclass> declaration is used. For example:

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

        <property .... />
        .....
</subclass>

1

name : le nom de classe 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 l'initialisation différée des proxies.

4

lazy (optionnel, par défaut à true) : spécifier lazy="false" désactive l'utilisation de l'extraction différée.

For information about inheritance mappings see Chapitre 10, Mapping d'héritage de classe .

Discriminators are required for polymorphic persistence using the table-per-class-hierarchy mapping strategy. It declares a discriminator column of the table. The discriminator column contains marker values that tell the persistence layer what subclass to instantiate for a particular row. Hibernate Core supports the follwoing restricted set of types as discriminator column: string, character, integer, byte, short, boolean, yes_no, true_false.

Use the @DiscriminatorColumn to define the discriminator column as well as the discriminator type.

You can also use @DiscriminatorFormula to express in SQL a virtual discriminator column. This is particularly useful when the discriminator value can be extracted from one or more columns of the table. Both @DiscriminatorColumn and @DiscriminatorFormula are to be set on the root entity (once per persisted hierarchy).

@org.hibernate.annotations.DiscriminatorOptions allows to optionally specify Hibernate specific discriminator options which are not standardized in JPA. The available options are force and insert. The force attribute is useful if the table contains rows with "extra" discriminator values that are not mapped to a persistent class. This could for example occur when working with a legacy database. If force is set to true Hibernate will specify the allowed discriminator values in the SELECT query, even when retrieving all instances of the root class. The second option - insert - tells Hibernate whether or not to include the discriminator column in SQL INSERTs. Usually the column should be part of the INSERT statement, but if your discriminator column is also part of a mapped composite identifier you have to set this option to false.

Finally, use @DiscriminatorValue on each class of the hierarchy to specify the value stored in the discriminator column for a given entity. If you do not set @DiscriminatorValue on a class, the fully qualified class name is used.

@Entity

@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
    name="planetype",
    discriminatorType=DiscriminatorType.STRING
)
@DiscriminatorValue("Plane")
public class Plane { ... }
@Entity
@DiscriminatorValue("A320")
public class A320 extends Plane { ... }          

In hbm.xml, the <discriminator> element is used to define the discriminator column or formula:

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

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 SQL INSERT s).

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

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

Each subclass can also be mapped to its own table. This is called the table-per-subclass mapping strategy. An inherited state is retrieved by joining with the table of the superclass. A discriminator column is not required for this mapping strategy. Each subclass must, however, declare a table column holding the object identifier. The primary key of this table is also a foreign key to the superclass table and described by the @PrimaryKeyJoinColumns or the <key> element.

@Entity @Table(name="CATS")

@Inheritance(strategy=InheritanceType.JOINED)
public class Cat implements Serializable { 
    @Id @GeneratedValue(generator="cat-uuid") 
    @GenericGenerator(name="cat-uuid", strategy="uuid")
    String getId() { return id; }
    ...
}
@Entity @Table(name="DOMESTIC_CATS")
@PrimaryKeyJoinColumn(name="CAT")
public class DomesticCat extends Cat { 
    public String getName() { return name; }
}            

In hbm.xml, use the <joined-subclass> element. For example:

<joined-subclass
        name="(1)ClassName"
        table=(2)"tablename"
        proxy=(3)"ProxyInterface"
        lazy="(4)true|false"
        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 : le nom de classe complet de la sous-classe.

2

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

3

proxy (optionnel) : indique une classe ou interface à utiliser pour l'initialisation différée des proxies.

4

lazy (optionnel, par défaut à true) : spécifier lazy="false" désactive l'utilisation de l'extraction différée.

Use the <key> element to declare the primary key / foreign key column. The mapping at the start of the chapter would then be re-written as:


<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD//EN"
        "http://www.hibernate.org/dtd/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>

For information about inheritance mappings see Chapitre 10, Mapping d'héritage de classe .

A third option is to map only the concrete classes of an inheritance hierarchy to tables. This is called the table-per-concrete-class strategy. Each table defines all persistent states of the class, including the inherited state. In Hibernate, it is not necessary to explicitly map such inheritance hierarchies. You can map each class as a separate entity root. However, if you wish use polymorphic associations (e.g. an association to the superclass of your hierarchy), you need to use the union subclass mapping.

@Entity

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Flight implements Serializable { ... }            

Or in hbm.xml:

<union-subclass
        name="(1)ClassName"
        table=(2)"tablename"
        proxy=(3)"ProxyInterface"
        lazy="(4)true|false"
        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 : le nom de classe complet de la sous-classe.

2

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

3

proxy (optionnel) : indique une classe ou interface à utiliser pour l'initialisation différée des proxies.

4

lazy (optionnel, par défaut à true) : spécifier lazy="false" désactive l'utilisation de l'extraction différée.

Aucune colonne discriminante ou colonne clé n'est requise pour cette stratégie de mappage.

For information about inheritance mappings see Chapitre 10, Mapping d'héritage de classe .

This is sometimes useful to share common properties through a technical or a business superclass without including it as a regular mapped entity (ie no specific table for this entity). For that purpose you can map them as @MappedSuperclass.

@MappedSuperclass

public class BaseEntity {
    @Basic
    @Temporal(TemporalType.TIMESTAMP)
    public Date getLastUpdate() { ... }
    public String getLastUpdater() { ... }
    ...
}
@Entity class Order extends BaseEntity {
    @Id public Integer getId() { ... }
    ...
}

In database, this hierarchy will be represented as an Order table having the id, lastUpdate and lastUpdater columns. The embedded superclass property mappings are copied into their entity subclasses. Remember that the embeddable superclass is not the root of the hierarchy though.

You can override columns defined in entity superclasses at the root entity level using the @AttributeOverride annotation.

@MappedSuperclass

public class FlyingObject implements Serializable {
    public int getAltitude() {
        return altitude;
    }
    @Transient
    public int getMetricAltitude() {
        return metricAltitude;
    }
    @ManyToOne
    public PropulsionType getPropulsion() {
        return metricAltitude;
    }
    ...
}
@Entity
@AttributeOverride( name="altitude", column = @Column(name="fld_altitude") )
@AssociationOverride( 
   name="propulsion", 
   joinColumns = @JoinColumn(name="fld_propulsion_fk") 
)
public class Plane extends FlyingObject {
    ...
}

The altitude property will be persisted in an fld_altitude column of table Plane and the propulsion association will be materialized in a fld_propulsion_fk foreign key column.

You can define @AttributeOverride(s) and @AssociationOverride(s) on @Entity classes, @MappedSuperclass classes and properties pointing to an @Embeddable object.

In hbm.xml, simply map the properties of the superclass in the <class> element of the entity that needs to inherit them.

While not recommended for a fresh schema, some legacy databases force your to map a single entity on several tables.

Using the @SecondaryTable or @SecondaryTables class level annotations. To express that a column is in a particular table, use the table parameter of @Column or @JoinColumn.

@Entity

@Table(name="MainCat")
@SecondaryTables({
    @SecondaryTable(name="Cat1", pkJoinColumns={
        @PrimaryKeyJoinColumn(name="cat_id", referencedColumnName="id")
    ),
    @SecondaryTable(name="Cat2", uniqueConstraints={@UniqueConstraint(columnNames={"storyPart2"})})
})
public class Cat implements Serializable {
    private Integer id;
    private String name;
    private String storyPart1;
    private String storyPart2;
    @Id @GeneratedValue
    public Integer getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    
    @Column(table="Cat1")
    public String getStoryPart1() {
        return storyPart1;
    }
    @Column(table="Cat2")
    public String getStoryPart2() {
        return storyPart2;
    }
}

In this example, name will be in MainCat. storyPart1 will be in Cat1 and storyPart2 will be in Cat2. Cat1 will be joined to MainCat using the cat_id as a foreign key, and Cat2 using id (ie the same column name, the MainCat id column has). Plus a unique constraint on storyPart2 has been set.

There is also additional tuning accessible via the @org.hibernate.annotations.Table annotation:

Make sure to use the secondary table name in the appliesto property

@Entity

@Table(name="MainCat")
@SecondaryTable(name="Cat1")
@org.hibernate.annotations.Table(
   appliesTo="Cat1",
   fetch=FetchMode.SELECT,
   optional=true)
public class Cat implements Serializable {
    private Integer id;
    private String name;
    private String storyPart1;
    private String storyPart2;
    @Id @GeneratedValue
    public Integer getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    
    @Column(table="Cat1")
    public String getStoryPart1() {
        return storyPart1;
    }
    @Column(table="Cat2")
    public String getStoryPart2() {
        return storyPart2;
    }
}

In hbm.xml, use the <join> element.

<join
        table=(1)"tablename"
        schema(2)="owner"
        catalo(3)g="catalog"
        fetch=(4)"join|select"
        invers(5)e="true|false"
        option(6)al="true|false">

        <key ... />

        <property ... />
        ...
</join>

1

table : le nom de la table jointe.

2

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

3

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

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, 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 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 extraire 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, 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 mappage d'héritage à une autre dans une hiérarchie simple, comme nous le verrons plus tard.

To link one entity to an other, you need to map the association property as a to one association. In the relational model, you can either use a foreign key or an association table, or (a bit less common) share the same primary key value between the two entities.

To mark an association, use either @ManyToOne or @OnetoOne.

@ManyToOne and @OneToOne have a parameter named targetEntity which describes the target entity name. You usually don't need this parameter since the default value (the type of the property that stores the association) is good in almost all cases. However this is useful when you want to use interfaces as the return type instead of the regular entity.

Setting a value of the cascade attribute to any meaningful value other than nothing will propagate certain operations to the associated object. The meaningful values are divided into three categories.

By default, single point associations are eagerly fetched in JPA 2. You can mark it as lazily fetched by using @ManyToOne(fetch=FetchType.LAZY) in which case Hibernate will proxy the association and load it when the state of the associated entity is reached. You can force Hibernate not to use a proxy by using @LazyToOne(NO_PROXY). In this case, the property is fetched lazily when the instance variable is first accessed. This requires build-time bytecode instrumentation. lazy="false" specifies that the association will always be eagerly fetched.

With the default JPA options, single-ended associations are loaded with a subsequent select if set to LAZY, or a SQL JOIN is used for EAGER associations. You can however adjust the fetching strategy, ie how data is fetched by using @Fetch. FetchMode can be SELECT (a select is triggered when the association needs to be loaded) or JOIN (use a SQL JOIN to load the association while loading the owner entity). JOIN overrides any lazy attribute (an association loaded through a JOIN strategy cannot be lazy).

An ordinary association to another persistent class is declared using a

and a foreign key in one table is referencing the primary key column(s) of the target table.

@Entity

public class Flight implements Serializable {
    @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
    @JoinColumn(name="COMP_ID")
    public Company getCompany() {
        return company;
    }
    ...
}            

The @JoinColumn attribute is optional, the default value(s) is the concatenation of the name of the relationship in the owner side, _ (underscore), and the name of the primary key column in the owned side. In this example company_id because the property name is company and the column id of Company is id.

@Entity

public class Flight implements Serializable {
    @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE}, targetEntity=CompanyImpl.class )
    @JoinColumn(name="COMP_ID")
    public Company getCompany() {
        return company;
    }
    ...
}
public interface Company {
    ...
}

You can also map a to one association through an association table. This association table described by the @JoinTable annotation will contains a foreign key referencing back the entity table (through @JoinTable.joinColumns) and a a foreign key referencing the target entity table (through @JoinTable.inverseJoinColumns).

@Entity

public class Flight implements Serializable {
    @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
    @JoinTable(name="Flight_Company",
        joinColumns = @JoinColumn(name="FLIGHT_ID"),
        inverseJoinColumns = @JoinColumn(name="COMP_ID")
    )
    public Company getCompany() {
        return company;
    }
    ...
}       

You can mark an association as mandatory by using the optional=false attribute. We recommend to use Bean Validation's @NotNull annotation as a better alternative however. As a consequence, the foreign key column(s) will be marked as not nullable (if possible).

When Hibernate cannot resolve the association because the expected associated element is not in database (wrong id on the association column), an exception is raised. This might be inconvenient for legacy and badly maintained schemas. You can ask Hibernate to ignore such elements instead of raising an exception using the @NotFound annotation.


Sometimes you want to delegate to your database the deletion of cascade when a given entity is deleted. In this case Hibernate generates a cascade delete constraint at the database level.


Foreign key constraints, while generated by Hibernate, have a fairly unreadable name. You can override the constraint name using @ForeignKey.


Sometimes, you want to link one entity to an other not by the target entity primary key but by a different unique key. You can achieve that by referencing the unique key column(s) in @JoinColumn.referenceColumnName.

@Entity

class Person {
   @Id Integer personNumber;
   String firstName;
   @Column(name="I")
   String initial;
   String lastName;
}
@Entity
class Home {
   @ManyToOne
   @JoinColumns({
      @JoinColumn(name="first_name", referencedColumnName="firstName"),
      @JoinColumn(name="init", referencedColumnName="I"),
      @JoinColumn(name="last_name", referencedColumnName="lastName"),
   })
   Person owner
}

This is not encouraged however and should be reserved to legacy mappings.

In hbm.xml, mapping an association is similar. The main difference is that a @OneToOne is mapped as <many-to-one unique="true"/>, let's dive into the subject.

<many-to-one
        name="(1)propertyName"
        column(2)="column_name"
        class=(3)"ClassName"
        cascad(4)e="cascade_style"
        fetch=(5)"join|select"
        update(6)="true|false"
        insert(6)="true|false"
        proper(7)ty-ref="propertyNameFromAssociatedClass"
        access(8)="field|property|ClassName"
        unique(9)="true|false"
        not-nu(10)ll="true|false"
        optimi(11)stic-lock="true|false"
        lazy="(12)proxy|no-proxy|false"
        not-fo(13)und="ignore|exception"
        entity(14)-name="EntityName"
        formul(15)a="arbitrary SQL expression"
        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 : le nom de la propriété.

2

column (optionnel) : le nom de la colonne de la clé étrangère. Cela peut être aussi spécifié par un ou des sous-élément(s) <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 cascadées de l'objet parent vers l'objet associé.

5

fetch (optionnel - par défaut à select) : choisit entre le chargement de type jointure externe (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 SQL UPDATE et/ou des déclarations INSERT. Mettre les deux à false, permet une association pure dérivée dont la valeur est initialisée à partir d'une autre propriété qui mappe à une ou plusieurs mêmes colonnes, ou par un trigger ou une autre application.

7

property-ref (optionnel) : le nom d'une propriété de la classe associée qui est jointe à cette clé étrangère. Si non-spécifiée, la clé primaire de la classe associée est utilisée.

8

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

9

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

10

not-null (optionnel) : active le DDL d'une contrainte de nullité pour les colonnes de clés étrangères.

11

optimistic-lock (optionnel - par défaut à true) : indique si les mise à jour de cette propriété nécessitent ou non 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).

12

lazy (optionnel - par défaut à proxy) : par défaut, les associations de point uniques utilisent des proxies. lazy="no-proxy" indique que cette propriété doit être chargée en différé au premier accès à la variable d'instance (nécessite une instrumentation du bytecode lors de la phase de construction). lazy="false" indique que l'association sera toujours chargée.

13

not-found (optionnel - par défaut = exception) : spécifie comment les clés étrangères qui référencent des lignes manquantes seront gérées : ignore traitera une ligne manquante comme une association nulle.

14

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

15

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

Setting a value of the cascade attribute to any meaningful value other than none will propagate certain operations to the associated object. The meaningful values are divided into three categories. First, basic operations, which include: persist, merge, delete, save-update, evict, replicate, lock and refresh; second, special values: delete-orphan; and third,all comma-separated combinations of operation names: cascade="persist,merge,evict" or cascade="all,delete-orphan". See Section 11.11, « Persistance transitive » for a full explanation. Note that single valued, many-to-one and one-to-one, associations do not support 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 clés étrangères font référence à une clé unique de la table associée et qui n'est pas la clé primaire. C'est un cas de mauvaise conception relationnelle. Par exemple, supposez que la classe Product ait un numéro de série unique qui n'est pas la clé 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 mappage 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 clé 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 nommé <properties>.

Si la clé unique référencée est la propriété d'un composant, vous pouvez spécifier le chemin de propriété :


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

The second approach is to ensure an entity and its associated entity share the same primary key. In this case the primary key column is also a foreign key and there is no extra column. These associations are always one to one.


Note

Many people got confused by these primary key based one to one associations. They can only be lazily loaded if Hibernate knows that the other side of the association is always present. To indicate to Hibernate that it is the case, use @OneToOne(optional=false).

In hbm.xml, use the following mapping.

<one-to-one
        name="(1)propertyName"
        class=(2)"ClassName"
        cascad(3)e="cascade_style"
        constr(4)ained="true|false"
        fetch=(5)"join|select"
        proper(6)ty-ref="propertyNameFromAssociatedClass"
        access(7)="field|property|ClassName"
        formul(8)a="any SQL expression"
        lazy="(9)proxy|no-proxy|false"
        entity(10)-name="EntityName"
        node="element-name|@attribute-name|element/@attribute|."
        embed-xml="true|false"
        foreign-key="foreign_key_name"
/>

1

name : le nom de la propriété.

2

class (optionnel - par défaut, le 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 parent vers l'objet associé.

4

constrained (optionnel) : indique qu'une contrainte de clé étrangère sur la clé 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() est cascadé et détermine si l'association peut utiliser un proxy (aussi utilisé par l'outil SchemaExport).

5

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

6

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

7

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

8

formula (optionnel) : presque toutes les associations un-à-un pointent sur la clé 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 soumises à proxy. lazy="no-proxy" spécifie que la propriété doit être chargée en différé au premier accès à l'instance. (nécessite l'instrumentation 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 (optionnel) : le nom de l'entité de la classe associée.

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

Pour une association par clé primaire, ajoutez les mappages 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 clés 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 clé primaire que l'instance de Employee référencée par la propriété employee de cette Person.

Although we recommend the use of surrogate keys as primary keys, you should try to identify natural keys for all entities. A natural key is a property or combination of properties that is unique and non-null. It is also immutable. Map the properties of the natural key as @NaturalId or map them inside the <natural-id> element. Hibernate will generate the necessary unique key and nullability constraints and, as a result, your mapping will be more self-documenting.

@Entity

public class Citizen {
    @Id
    @GeneratedValue
    private Integer id;
    private String firstname;
    private String lastname;
    
    @NaturalId
    @ManyToOne
    private State state;
    @NaturalId
    private String ssn;
    ...
}
//and later on query
List results = s.createCriteria( Citizen.class )
                .add( Restrictions.naturalId().set( "ssn", "1234" ).set( "state", ste ) )
                .list();

Or in XML,


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

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

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

There is one more type of property mapping. The @Any mapping defines a polymorphic association to classes from multiple tables. This type of mapping requires more than one column. The first column contains the type of the associated entity. The remaining columns contain the identifier. It is impossible to specify a foreign key constraint for this kind of association. This is not the usual way of mapping polymorphic associations and you should use this only in special cases. For example, for audit logs, user session data, etc.

The @Any annotation describes the column holding the metadata information. To link the value of the metadata information and an actual entity type, The @AnyDef and @AnyDefs annotations are used. The metaType attribute allows the application to specify a custom type that maps database column values to persistent classes that have identifier properties of the type specified by idType. You must specify the mapping from values of the metaType to class names.

@Any( metaColumn = @Column( name = "property_type" ), fetch=FetchType.EAGER )

@AnyMetaDef( 
    idType = "integer", 
    metaType = "string", 
    metaValues = {
        @MetaValue( value = "S", targetEntity = StringProperty.class ),
        @MetaValue( value = "I", targetEntity = IntegerProperty.class )
    } )
@JoinColumn( name = "property_id" )
public Property getMainProperty() {
    return mainProperty;
}

Note that @AnyDef can be mutualized and reused. It is recommended to place it as a package metadata in this case.

//on a package

@AnyMetaDef( name="property" 
    idType = "integer", 
    metaType = "string", 
    metaValues = {
        @MetaValue( value = "S", targetEntity = StringProperty.class ),
        @MetaValue( value = "I", targetEntity = IntegerProperty.class )
    } )
package org.hibernate.test.annotations.any;
//in a class
    @Any( metaDef="property", metaColumn = @Column( name = "property_type" ), fetch=FetchType.EAGER )
    @JoinColumn( name = "property_id" )
    public Property getMainProperty() {
        return mainProperty;
    }

The hbm.xml equivalent is:


<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="(1)propertyName"
        id-typ(2)e="idtypename"
        meta-t(3)ype="metatypename"
        cascad(4)e="cascade_style"
        access(5)="field|property|ClassName"
        optimi(6)stic-lock="true|false"
>
        <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 mappage par discriminateur.

4

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

5

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

6

optimistic-lock (optionnel - par défaut à true) : indique si les mise à 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.

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. Par exemple :

<properties
        name="(1)logicalName"
        insert(2)="true|false"
        update(3)="true|false"
        optimi(4)stic-lock="true|false"
        unique(5)="true|false"
>

        <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 : les colonnes mappées apparaissent-elles dans les SQL INSERT s ?

3

update: les colonnes mappées apparaissent-elles dans les SQL UPDATE s ?

4

optimistic-lock (optionnel - par défaut à true) : indique si les mise à 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 mappage 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 qui font référence à cette clé unique de la table Person au lieu de la clé primaire :


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

Nous ne recommandons pas une telle utilisation, en dehors du contexte de mappage de données héritées d'anciens systèmes.

The hbm.xml structure has some specificities naturally not present when using annotations, let's describe them briefly.

Tous les mappages XML devraient utiliser le doctype indiqué. En effet vous trouverez le fichier DTD à 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.

Comme mentionné précédemment, Hibernate tentera en premier lieu de résoudre les DTD dans leur classpath. Il réussit à le faire en enregistrant une implémentation personnalisée de org.xml.sax.EntityResolver avec le SAXReader qu'il utilise pour lire les fichiers xml. Cet EntityResolver personnalisé reconnaît deux espaces de nommage systemId différents :

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>

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

Cet élément a plusieurs attributs optionnels. Les attributs schema et catalog indiquent que les tables mentionnées dans ce mappage 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 de catalogue. L'attribut default-cascade indique quel type de cascade sera utilisé 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, par défaut.

<hibernate-mapping
         schem(1)a="schemaName"
         catal(2)og="catalogName"
         defau(3)lt-cascade="cascade_style"
         defau(4)lt-access="field|property|ClassName"
         defau(5)lt-lazy="true|false"
         auto-(6)import="true|false"
         packa(7)ge="package.name"
 />

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 des attributs lazy non spécifiés des mappages de classe 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 (de classes de ce mappage) dans le langage de requête.

7

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

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

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

The <key> element is featured a few times within this guide. It appears anywhere the parent mapping element defines a join to a new table that references the primary key of the original table. It also defines the foreign key in the joined table:

<key
        column(1)="columnname"
        on-del(2)ete="noaction|cascade"
        proper(3)ty-ref="propertyName"
        not-nu(4)ll="true|false"
        update(5)="true|false"
        unique(6)="true|false"
/>

1

column (optionnel) : le nom de la colonne de la clé étrangère. Cela peut être aussi spécifié par un ou des sous-élément(s) <column>.

2

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

3

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

4

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

5

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

6

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

Là où les suppressions doivent être performantes, nous recommandons pour les systèmes de définir toutes les clés 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 un-à-plusieurs unidirectionnelle. Si vous mappez un un-à-plusieurs unidirectionnel vers une clé étrangère non nulle, vous devez déclarer la colonne de la clé en utilisant <key not-null="true">.

Pour le service de persistance, les objets sont classés en deux groupes au niveau langage Java :

Une entité existe indépendamment de tout autre objet possédant des références 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é parent 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ées et supprimées par atteignabiliité. Comme les valeurs (et types primitifs) sont persistées et supprimées 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 la sémantique d'une valeur. Une propriété Java de type java.lang.String a aussi les caractéristiques d'une valeur. Selon cette définition, nous sommes en mesure de déclarer que tous les types (classes) fournis par JDK possèdent la sémantique d'une valeur dans Java, alors que les types définis par un utilisateur pourront être mappés avec des sémantiques entités ou valeur type. Cette décision est prise par le développeur d'application. Un bon conseil pour une classe entité dans un modèle de domaine sont des références partagées à une instance unique de cette classe, alors que la composition ou l'agrégation se traduit en général par une valeur type.

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

Le défi est de mapper les types 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 mappage Hibernate. Hibernate propose de nombreux mappages prêts à l'utilisation (pour les types de valeurs standards du JDK). Vous pouvez écrire vos propres types de mappages et implémenter aussi vos propres stratégies de conversion comme nous le verrons plus tard.

Tous les types proposés Hibernate à part les collections autorisent les sémantiques null.

Les types de mappage de base peuvent être classés de la façon suivante :

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

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

string

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

date, time, timestamp

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

calendar, calendar_date

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

big_decimal, big_integer

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

locale, timezone, currency

mappages 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 mappage de 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

Maps long Java strings to a SQL LONGVARCHAR or TEXT type.

image

Maps long byte arrays to a SQL LONGVARBINARY.

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

Mappages de type pour les classes JDBC java.sql.Clob et 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 comporte des lacunes).

materialized_clob

Maps long Java strings to a SQL CLOB type. When read, the CLOB value is immediately materialized into a Java string. Some drivers require the CLOB value to be read within a transaction. Once materialized, the Java string is available outside of the transaction.

materialized_blob

Maps long Java byte arrays to a SQL BLOB type. When read, the BLOB value is immediately materialized into a byte array. Some drivers require the BLOB value to be read within a transaction. Once materialized, the byte array is available outside of the transaction.

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

Mappages de type pour ceux qui sont habituellement considérés comme des types Java modifiables, et pour lesquels Hibernate effectue certaines optimisations convenant seulement aux types Java immuables. L'application les traite comme immuables. 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 en sorte 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 et définis dans org.hibernate.Hibernate. Par exemple, Hibernate.STRING représente le type string.

Il est assez facile pour les développeurs de créer leurs propres types de valeurs. Par exemple, vous aimeriez persister des propriétés du type java.lang.BigInteger dans des colonnes VARCHAR. Hibernate ne procure pas de type par défaut à cet effet. Toutefois, les types personnalisés ne se limitent pas à mapper des propriétés (ou élément collection) à une simple colonne de 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. Consultez org.hibernate.test.DoubleStringType pour étudier les possibilités.


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

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

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

Vous pouvez même fournir des paramètres en indiquant UserType dans le fichier de mappage. À cet effet, 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 mappage.


<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, il est utile de lui définir un nom plus court. Vous pouvez l'effectuer, 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 également possible de redéfinir les paramètres par défaut du typedef au cas par cas en utilisant des paramètres type sur le mappage de la propriété.

Alors que Hibernate offre une riche variété de types, et la prise en charge des composants, vous aurez très rarement besoin d'utiliser un type personnalisé, il est néanmoins recommandé d'utiliser des types personnalisés 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 si elle pourrait facilement être mappée en tant que composant. Une motivation pour cela est l'abstraction. Avec un type personnalisé, vos documents de mappage sont à l'abri des changements futurs dans votre façon de représenter des valeurs monétaires.

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 que Hibernate réalise un SQL INSERT ou UPDATE 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.

Properties marked as generated must additionally be non-insertable and non-updateable. Only versions, timestamps, and simple properties, can be marked as generated.

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

insert: the given property value is generated on insert, but is not regenerated on subsequent updates. Properties like created-date fall into this category. Even though version and timestamp properties can be marked as generated, this option is not available.

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

To mark a property as generated, use @Generated.

Hibernate allows you to customize the SQL it uses to read and write the values of columns mapped to simple properties. For example, if your database provides a set of data encryption functions, you can invoke them for individual columns like this:

@Entity

class CreditCard {
   @Column(name="credit_card_num")
   @ColumnTransformer(
      read="decrypt(credit_card_num)", 
      write="encrypt(?)")
   public String getCreditCardNumber() { return creditCardNumber; }
   public void setCreditCardNumber(String number) { this.creditCardNumber = number; }
   private String creditCardNumber;
}

or in XML


<property name="creditCardNumber">
        <column 
          name="credit_card_num"
          read="decrypt(credit_card_num)"
          write="encrypt(?)"/>
</property>

Note

You can use the plural form @ColumnTransformers if more than one columns need to define either of these rules.

If a property uses more that one column, you must use the forColumn attribute to specify which column, the expressions are targeting.

@Entity

class User {
   @Type(type