348
HIBERNATE - Persistance relationnelle en Java standard Documentation de référence d'Hibernate 3.3.1

HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Embed Size (px)

Citation preview

Page 1: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

HIBERNATE - Persistancerelationnelle en Java standard

Documentation de référence d'Hibernate

3.3.1

Page 2: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

HIBERNATE - Persistance relationnelle en Java standardCopyright © 2004 Red Hat Middleware, LLC.

Legal Notice

1801 Varsity Drive Raleigh, NC27606-2072USA Phone: +1 919 754 3700 Phone: 888 733 4281 Fax: +1 919 754 3701 PO Box 13588Research Triangle Park, NC27709USA

Copyright © 2007 by Red Hat, Inc. This copyrighted material is made available to anyone wishing to use,modify, copy, or redistribute it subject to the terms and conditions of the GNU Lesser General Public License[http://www.gnu.org/licenses/lgpl-2.1.html], as published by the Free Software Foundation.

Red Hat and the Red Hat "Shadow Man" logo are registered trademarks of Red Hat, Inc. in the United States and othercountries.

All other trademarks referenced herein are the property of their respective owners.

The GPG fingerprint of the [email protected] key is:

CA 20 86 86 2B D6 9D FC 65 F6 EC C4 21 91 80 CD DB 42 A6 0E

Page 3: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 iii

Préface ....................................................................................................... xiii1. Introduction à Hibernate ........................................................................ 1

1.1. Préface .......................................................................................... 11.2. Partie 1 - Première application Hibernate ...................................... 1

1.2.1. La première classe ............................................................. 21.2.2. Le fichier de mapping ......................................................... 41.2.3. Configuration d'Hibernate ................................................... 71.2.4. Construction avec Ant ......................................................... 91.2.5. Démarrage et aides .......................................................... 101.2.6. Charger et stocker des objets ........................................... 12

1.3. Partie 2 - Mapper des associations ............................................. 151.3.1. Mapper la classe Person .................................................. 151.3.2. Une association unidirectionnelle basée sur Set ............... 161.3.3. Travailler avec l'association .............................................. 181.3.4. Collection de valeurs ........................................................ 201.3.5. Associations bidirectionnelles ........................................... 221.3.6. Travailler avec des liens bidirectionnels ............................ 23

1.4. Part 3 - L'application web EventManager .................................... 241.4.1. Ecrire la servlet de base ................................................... 241.4.2. Procéder et rendre ............................................................ 251.4.3. Déployer et tester ............................................................. 27

1.5. Résumé ....................................................................................... 292. Architecture .......................................................................................... 31

2.1. Généralités .................................................................................. 312.2. Etats des instances ..................................................................... 342.3. Intégration JMX ........................................................................... 342.4. Support JCA ................................................................................ 352.5. Sessions Contextuelles ............................................................... 35

3. Configuration ........................................................................................ 393.1. Configuration par programmation ................................................ 393.2. Obtenir une SessionFactory ........................................................ 403.3. Connexions JDBC ....................................................................... 403.4. Propriétés de configuration optionnelles ...................................... 42

3.4.1. Dialectes SQL ................................................................... 493.4.2. Chargement par Jointure Ouverte ..................................... 493.4.3. Flux binaires ..................................................................... 503.4.4. Cache de second niveau et cache de requêtes ................ 503.4.5. Substitution dans le langage de requêtage ....................... 503.4.6. Statistiques Hibernate ....................................................... 50

3.5. Tracer .......................................................................................... 513.6. Implémenter une NamingStrategy ................................................. 523.7. Fichier de configuration XML ....................................................... 533.8. Intégration à un serveur d'application J2EE ................................ 54

Page 4: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

iv Hibernate 3.3.1

3.8.1. Configuration de la stratégie transactionnelle ................... 553.8.2. SessionFactory associée au JNDI ..................................... 563.8.3. Association automatique de la Session à JTA .................. 573.8.4. Déploiement JMX .............................................................. 57

4. Classes persistantes ............................................................................ 614.1. Un exemple simple de POJO ...................................................... 61

4.1.1. Implémenter un constructeur sans argument .................... 634.1.2. Fournir une propriété d'indentifiant (optionnel) .................. 634.1.3. Favoriser les classes non finales (optionnel) .................... 634.1.4. Déclarer les accesseurs et mutateurs des attributspersistants (optionnel) ................................................................. 64

4.2. Implémenter l'héritage ................................................................. 644.3. Implémenter equals() et hashCode() ............................................ 644.4. Modèles dynamiques ................................................................... 664.5. Tuplizers ...................................................................................... 684.6. Extentsions .................................................................................. 70

5. Mapping O/R basique .......................................................................... 715.1. Déclaration de Mapping ............................................................... 71

5.1.1. Doctype ............................................................................. 725.1.1.1. EntityResolver ........................................................ 73

5.1.2. hibernate-mapping ............................................................ 735.1.3. class .................................................................................. 745.1.4. id ....................................................................................... 78

5.1.4.1. Generator ............................................................... 795.1.4.2. algorithme Hi/lo ...................................................... 815.1.4.3. UUID algorithm ....................................................... 825.1.4.4. Colonnes identifiantes et séquences ...................... 825.1.4.5. Identifiants assignés ............................................... 825.1.4.6. Clefs primaires assignées par trigger ..................... 83

5.1.5. Enhanced identifier generators ......................................... 835.1.6. Identifier generator optimization ........................................ 855.1.7. composite-id ...................................................................... 855.1.8. discriminator ...................................................................... 875.1.9. version (optionnel) ............................................................ 885.1.10. timestamp (optionnel) ...................................................... 895.1.11. property ........................................................................... 905.1.12. many-to-one .................................................................... 925.1.13. Une association one-to-one vers une autre classepersistante est déclarée avec l'élément one-to-one. ................... 955.1.14. Bien que nous recommandions l'utilisation de cléprimaire générée, vous devriez toujours essayer d'identifierdes clé métier (naturelles) pour toutes vos entités. Une clénaturelle est une propriété ou une combinaison de propriétés

Page 5: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 v

uniques et non nulles. Si elle est aussi immuable, c'estencore mieux. Mappez les propriétés de la clé naturelledans l'élément <natural-id>. Hibernate générera la clé uniquenécessaire et les contraintes de non-nullité, et votre mappings'auto-documentera. .................................................................... 985.1.15. L'élément <component> mappe les propriétés d'un objetfils aux colonnes d'une classe parente. Les composantspeuvent en retour déclarer leurs propres propriétés,composants ou collections. Voir "Components" plus bas. ........... 985.1.16. L'élément <properties> permet la définition d'ungroupement logique nommé des propriétés d'une classe.L'utilisation la plus importante de cette construction est lapossibilité pour une combinaison de propriétés d'être la cibled'un property-ref. C'est aussi un moyen pratique de définirune contrainte d'unicité multi-colonnes. .................................... 1005.1.17. Pour finir, la persistance polymorphique nécessitela déclaration de chaque sous-classe de la classepersistante de base. pour la stratégie de mapping de typetable-per-class-hierarchy, on utilise la déclaration <subclass>..................................................................................................... 1015.1.18. joined-subclass ............................................................. 1025.1.19. union-subclass .............................................................. 1035.1.20. join ................................................................................ 1045.1.21. key ................................................................................ 1055.1.22. éléments column et formula .......................................... 1065.1.23. import ............................................................................ 1075.1.24. any ................................................................................ 107

5.2. Hibernate Types ........................................................................ 1095.2.1. Entités et valeurs ............................................................ 1095.2.2. Basic value types ............................................................ 1105.2.3. Types de valeur définis par l'utilisateur ........................... 111

5.3. Mapper une classe plus d'une fois ............................................ 1135.4. SQL quoted identifiers ............................................................... 1145.5. alternatives Metadata ................................................................ 114

5.5.1. utilisation de XDoclet ...................................................... 1145.5.2. Utilisation des annotations JDK 5.0 ................................ 116

5.6. Propriétés générées .................................................................. 1175.7. Objets auxiliaires de la base de données .................................. 118

6. Mapping des collections .................................................................... 1216.1. Collections persistantes ............................................................. 1216.2. Mapper une collection ............................................................... 122

6.2.1. Les clefs étrangères d'une collection .............................. 1246.2.2. Les éléments d'une collection ......................................... 124

Page 6: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

vi Hibernate 3.3.1

6.2.3. Collections indexées ....................................................... 1256.2.4. Collections de valeurs et associationsplusieurs-vers-plusieurs ............................................................. 1266.2.5. Association un-vers-plusieurs ......................................... 128

6.3. Mappings de collection avancés ................................................ 1296.3.1. Collections triées ............................................................. 1296.3.2. Associations bidirectionnelles ......................................... 1306.3.3. Associations bidirectionnelles avec des collectionsindexées .................................................................................... 1326.3.4. Associations ternaires ..................................................... 1336.3.5. Utiliser un <idbag> ......................................................... 134

6.4. Exemples de collections ............................................................ 1347. Mapper les associations .................................................................... 139

7.1. Introduction ................................................................................ 1397.2. Association unidirectionnelle ...................................................... 139

7.2.1. plusieurs à un ................................................................. 1397.2.2. one to one ...................................................................... 1407.2.3. un à plusieurs ................................................................. 141

7.3. Associations unidirectionnelles avec tables de jointure .............. 1417.3.1. un à plusieurs ................................................................. 1417.3.2. plusieurs à un ................................................................. 1427.3.3. one to one ...................................................................... 1437.3.4. plusieurs à plusieurs ....................................................... 143

7.4. Associations bidirectionnelles .................................................... 1447.4.1. un à plusieurs / plusieurs à un ........................................ 1447.4.2. one to one ...................................................................... 145

7.5. Associations bidirectionnelles avec table de jointure ................. 1467.5.1. un à plusieurs / plusieurs à un ........................................ 1467.5.2. one to one ...................................................................... 1477.5.3. plusieurs à plusieurs ....................................................... 148

7.6. Des mappings plus complexes .................................................. 1488. Mapping de composants ................................................................... 151

8.1. Objects dépendants ................................................................... 1518.2. Collection d'objets dépendants .................................................. 1538.3. Utiliser les composants comme index de map ........................... 1558.4. Utiliser un composant comme identifiant ................................... 1558.5. Composant Dynamique ............................................................. 157

9. Mapping d'héritage de classe ........................................................... 1599.1. Les trois stratégies .................................................................... 159

9.1.1. Une table par hiérarchie de classe ................................. 1609.1.2. Une table par classe fille ................................................ 1609.1.3. Une table par classe fille, en utilisant un discriminant ...... 161

Page 7: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 vii

9.1.4. Mélange d'une table par hiérarchie de classe avec unetable par classe fille .................................................................. 1629.1.5. Une table par classe concrète ........................................ 1629.1.6. Une table par classe concrète, en utilisant lepolymorphisme implicite ............................................................ 1639.1.7. Mélange du polymorphisme implicite avec d'autresmappings d'héritage .................................................................. 164

9.2. Limitations .................................................................................. 16510. Travailler avec des objets ............................................................... 167

10.1. États des objets Hibernate ...................................................... 16710.2. Rendre des objets persistants ................................................. 16810.3. Chargement d'un objet ............................................................ 16910.4. Requêtage ............................................................................... 170

10.4.1. Exécution de requêtes .................................................. 17110.4.1.1. Itération de résultats ........................................... 17110.4.1.2. Requêtes qui retournent des tuples .................... 17210.4.1.3. Résultats scalaires ............................................. 17210.4.1.4. Lier des paramètres ........................................... 17310.4.1.5. Pagination .......................................................... 17310.4.1.6. Itération "scrollable" ............................................ 17410.4.1.7. Externaliser des requêtes nommées .................. 174

10.4.2. Filtrer des collections .................................................... 17510.4.3. Requêtes Criteria .......................................................... 17610.4.4. Requêtes en SQL natif ................................................. 176

10.5. Modifier des objets persistants ................................................ 17710.6. Modifier des objets détachés ................................................... 17710.7. Détection automatique d'un état .............................................. 17910.8. Suppression d'objets persistants ............................................. 18010.9. Réplication d'objets entre deux entrepôts de données ............. 18010.10. Flush de la session ............................................................... 18110.11. Persistance transitive ............................................................. 18210.12. Utilisation des méta-données ................................................. 185

11. Transactions et accès concurrents ................................................ 18711.1. Gestion de session et délimitation de transactions .................. 187

11.1.1. Unité de travail .............................................................. 18811.1.2. Longue conversation ..................................................... 18911.1.3. L'identité des objets ...................................................... 19111.1.4. Problèmes communs .................................................... 192

11.2. Démarcation des transactions ................................................. 19311.2.1. Environnement non managé ......................................... 19411.2.2. Utilisation de JTA .......................................................... 19511.2.3. Gestion des exceptions ................................................. 19711.2.4. Timeout de transaction ................................................. 198

Page 8: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

viii Hibernate 3.3.1

11.3. Contrôle de consurrence optimiste .......................................... 19911.3.1. Gestion du versionnage au niveau applicatif ................. 19911.3.2. Les sessions longues et le versionnage automatique. ... 20011.3.3. Les objets détachés et le versionnage automatique ...... 20111.3.4. Personnaliser le versionnage automatique ................... 202

11.4. Verouillage pessimiste ............................................................. 20311.5. Mode de libération de Connection ........................................... 204

12. Les intercepteurs et les événements .............................................. 20712.1. Intercepteurs ............................................................................ 20712.2. Système d'événements ............................................................ 20912.3. Sécurité déclarative d'Hibernate .............................................. 211

13. Traitement par paquet ...................................................................... 21313.1. Paquet de mises à jour .......................................................... 21313.2. L'interface StatelessSession .................................................... 21413.3. Notez que dans le code de l'exemple, les intances deCustomer retournées par la requête sont immédiatementdétachées. Elles ne sont jamais associées à un contexte depersistance. ....................................................................................... 21513.4. La pseudo-syntaxe pour les expressions UPDATE et DELETEest : ( UPDATE | DELETE ) FROM? EntityName (WHEREwhere_conditions)?. Certains points sont à noter : .......................... 216

14. HQL: Langage de requêtage d'Hibernate ....................................... 22114.1. Sensibilité à la casse ............................................................... 22114.2. La clause from ......................................................................... 22114.3. Associations et jointures .......................................................... 22214.4. Formes de syntaxes pour les jointures .................................... 22414.5. Refering to identifier property .................................................. 22414.6. La clause select ....................................................................... 22414.7. Fonctions d'aggrégation ........................................................... 22614.8. Requêtes polymorphiques ....................................................... 22614.9. La clause where ...................................................................... 22714.10. Expressions ........................................................................... 22914.11. La clause order by ................................................................. 23214.12. La clause group by ................................................................ 23314.13. Sous-requêtes ........................................................................ 23314.14. Exemples HQL ....................................................................... 23414.15. Mise à jour et suppression .................................................... 23614.16. Trucs & Astuces .................................................................... 23614.17. translator-credits .................................................................... 23814.18. Row value constructor syntax ................................................ 238

15. Requêtes par critères ....................................................................... 24115.1. Créer une instance de Criteria .............................................. 24115.2. Restriction du résultat .............................................................. 241

Page 9: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 ix

15.3. Trier les résultats ..................................................................... 24215.4. Associations ............................................................................. 24315.5. Peuplement d'associations de manière dynamique ................. 24315.6. Requêtes par l'exemple ........................................................... 24415.7. Projections, agrégation et regroupement ................................. 24415.8. Requêtes et sous-requêtes détachées .................................... 24615.9. Requêtes par identifiant naturel ............................................... 247

16. SQL natif ........................................................................................... 24916.1. Utiliser une SQLQuery ................................................................ 249

16.1.1. Scalar queries ............................................................... 24916.1.2. Entity queries ................................................................ 25016.1.3. Handling associations and collections ........................... 25116.1.4. Returning multiple entities ............................................. 251

16.1.4.1. Alias and property references ............................ 25216.1.5. Returning non-managed entities ................................... 25316.1.6. Handling inheritance ..................................................... 25416.1.7. Parameters .................................................................... 254

16.2. Requêtes SQL nommées ........................................................ 25416.2.1. Utilisation de return-property pour spécifierexplicitement les noms des colonnes/alias ............................... 25616.2.2. Utilisation de procédures stockées pour les requêtes .... 257

16.2.2.1. Règles/limitations lors de l'utilisation desprocédures stockées ......................................................... 258

16.3. SQL personnalisé pour créer, mettre à jour et effacer ............. 25816.4. SQL personnalisé pour le chargement .................................... 260

17. Filtrer les données ........................................................................... 26317.1. Filtres Hibernate ...................................................................... 263

18. Mapping XML .................................................................................... 26718.1. Travailler avec des données XML ........................................... 267

18.1.1. Spécifier le mapping XML et le mapping d'une classeensemble ................................................................................... 26718.1.2. Spécifier seulement un mapping XML ........................... 268

18.2. Métadonnées du mapping XML ............................................... 26818.3. Manipuler des données XML ................................................... 270

19. Améliorer les performances ............................................................ 27319.1. Stratégies de chargement ........................................................ 273

19.1.1. Travailler avec des associations chargées tardivement.................................................................................................... 27419.1.2. Personnalisation des stratégies de chargement ............ 27519.1.3. Proxys pour des associations vers un seul objet ........... 27619.1.4. Initialisation des collections et des proxys ..................... 27819.1.5. Utiliser le chargement par lot ........................................ 28019.1.6. Utilisation du chargement par sous select ..................... 281

Page 10: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

x Hibernate 3.3.1

19.1.7. Utiliser le chargement tardif des propriétés ................... 28119.2. Le cache de second niveau ..................................................... 282

19.2.1. Mapping de Cache ........................................................ 28319.2.2. Strategie : lecture seule ................................................ 28419.2.3. Stratégie : lecture/écriture ............................................. 28419.2.4. Stratégie : lecture/écriture non stricte ........................... 28519.2.5. Stratégie : transactionelle ............................................. 28519.2.6. Cache-provider/concurrency-strategy compatibility ....... 285

19.3. Gérer les caches ..................................................................... 28619.4. Le cache de requêtes .............................................................. 28719.5. Comprendre les performances des Collections ....................... 288

19.5.1. Classification ................................................................. 28919.5.2. Les lists, les maps, les idbags et les sets sont lescollections les plus efficaces pour la mise à jour ...................... 29019.5.3. Les Bags et les lists sont les plus efficaces pour lescollections inverse ..................................................................... 29019.5.4. Suppression en un coup ............................................... 291

19.6. Moniteur de performance ......................................................... 29219.6.1. Suivi d'une SessionFactory ........................................... 29219.6.2. Métriques ...................................................................... 293

20. Guide des outils ............................................................................... 29520.1. Génération automatique du schéma ........................................ 295

20.1.1. Personnaliser le schéma ............................................... 29620.1.2. Exécuter l'outil ............................................................... 29920.1.3. Propriétés ...................................................................... 29920.1.4. Utiliser Ant .................................................................... 30020.1.5. Mises à jour incrémentales du schéma ......................... 30020.1.6. Utiliser Ant pour des mises à jour de schéma parincrément ................................................................................... 30120.1.7. Validation du schéma .................................................... 30120.1.8. Utiliser Ant pour la validation du Schéma ...................... 302

21. Exemple : Père/Fils .......................................................................... 30321.1. Une note à propos des collections .......................................... 30321.2. un-vers-plusieurs bidirectionnel ............................................... 30421.3. Cycle de vie en cascade ......................................................... 30521.4. Cascades et unsaved-value ..................................................... 30621.5. Conclusion ............................................................................... 307

22. Exemple : application Weblog ........................................................ 30922.1. Classes persistantes ................................................................ 30922.2. Mappings Hibernate ................................................................. 31022.3. Code Hibernate ....................................................................... 312

23. Exemple : quelques mappings ........................................................ 31723.1. Employeur/Employé (Employer/Employee) .............................. 317

Page 11: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 xi

23.2. Auteur/Travail (Author/Work) ................................................... 31923.3. Client/Commande/Produit (Customer/Order/Product) .............. 32123.4. Divers mappings d'exemple ..................................................... 323

23.4.1. "Typed" one-to-one association .................................... 32423.4.2. Exemple de clef composée ........................................... 32423.4.3. Many-to-many avec une clef composée partagée ......... 32623.4.4. Contenu basé sur une discrimination ............................ 32723.4.5. Associations sur des clefs alternées ............................. 328

24. Meilleures pratiques ......................................................................... 331

Page 12: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

xii Hibernate 3.3.1

Page 13: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 xiii

PréfaceTravailler dans les deux univers que sont l'orienté objet et la base dedonnées relationnelle peut être lourd et consommateur en temps dansle monde de l'entreprise d'aujourd'hui. Hibernate est un outil de mappingobjet/relationnel pour le monde Java. Le terme mapping objet/relationnel(ORM) décrit la technique consistant à faire le lien entre la représentationobjet des données et sa représentation relationnelle basée sur un schémaSQL.

Non seulement, Hibernate s'occupe du transfert des classes Java dansles tables de la base de données (et des types de données Java dansles types de données SQL), mais il permet de requêter les données etpropose des moyens de les récupérer. Il peut donc réduire de manièresignificative le temps de développement qui aurait été autrement perdu dansune manipulation manuelle des données via SQL et JDBC.

Le but d'Hibernate est de libérer le développeur de 95 pourcent des tâchesde programmation liées à la persistance des données communes. Hibernaten'est probablement pas la meilleure solution pour les applications centréessur les données qui n'utilisent que les procédures stockées pour implémenterla logique métier dans la base de données, il est le plus utile dans lesmodèles métier orientés objets dont la logique métier est implémentée dansla couche Java dite intermédiaire. Cependant, Hibernate vous aidera àsupprimer ou à encapsuler le code SQL spécifique à votre base de donnéeset vous aidera sur la tâche commune qu'est la transformation des donnéesd'une représentation tabulaire à une représentation sous forme de graphed'objets.

Si vous êtes nouveau dans Hibernate et le mapping Objet/Relationnel voiremême en Java, suivez ces quelques étapes :

1. Lisez Chapitre 1, Introduction à Hibernate pour un didacticiel plus longavec plus d'instructions étape par étape.

2. Lisez Chapitre 2, Architecture pour comprendre les environnements danslesquels Hibernate peut être utilisé.

3. Regardez le répertoire eg de la distribution Hibernate, il contient uneapplication simple et autonome. Copiez votre pilote JDBC dans lerépertoire lib/ et éditez src/hibernate.properties, en positionnantcorrectement les valeurs pour votre base de données. A partir d'une invitede commande dans le répertoire de la distribution, tapez ant eg (celautilise Ant), ou sous Windows tapez build eg.

Page 14: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

xiv Hibernate 3.3.1

4. Faîtes de cette documentation de référence votre principale sourced'information. Pensez à lire Java Persistence with Hibernate(http://www.manning.com/bauer2) si vous avez besoin de plus d'aideavec le design d'applications ou si vous préférez un tutoriel pas à pas.Visitez aussi http://caveatemptor.hibernate.org et téléchargez l'applicationexemple pour Java Persistence with Hibernate.

5. Les questions les plus fréquemment posées (FAQs) trouvent leur réponsesur le site web Hibernate.

6. Des démos, exemples et tutoriaux de tierces personnes sont référencéssur le site web Hibernate.

7. La zone communautaire (Community Area) du site web Hibernate estune bonne source d'information sur les design patterns et sur différentessolutions d'intégration d'Hibernate (Tomcat, JBoss, Spring Framework,Struts, EJB, etc).

Si vous avez des questions, utilisez le forum utilisateurs du site webHibernate. Nous utilisons également l'outil de gestion des incidents JIRApour tout ce qui est rapports de bogue et demandes d'évolution. Si vousêtes intéressé par le développement d'Hibernate, joignez-vous à la liste dediffusion de développement.

Le développement commercial, le support de production etles formations à Hibernate sont proposés par JBoss Inc (voirhttp://www.hibernate.org/SupportTraining/). Hibernate est un projet OpenSource professionnel et un composant critique de la suite de produits JBossEnterprise Middleware System (JEMS).

Page 15: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 1

Chapitre 1. Introduction à Hibernate

1.1. Préface

This chapter is an introduction to Hibernate by way of a tutorial, intendedfor new users of Hibernate. We start with a simple application using anin-memory database. We build the application in small, easy to understandsteps. The tutorial is based on another, earlier one developed by MichaelGloegl. All code is contained in the tutorials/web directory of the projectsource.

Important

This tutorial expects the user have knowledge of both Java and SQL.If you are new or uncomfortable with either, it is advised that you startwith a good introduction to that technology prior to attempting to learnHibernate. It will save time and effort in the long run.

Note

There is another tutorial/example application in the /tutorials/egdirectory of the project source. That example is console based andas such would not have the dependency on a servlet container toexecute. The basic setup is the same as the instructions below.

1.2. Partie 1 - Première application Hibernate

Let's assume we need a small database application that can store eventswe want to attend, and information about the host(s) of these events. Wewill use an in-memory, Java database named HSQLDB to avoid describinginstallation/setup of any particular database servers. Feel free to tweak thistutorial to use whatever database you feel comfortable using.

The first thing we need to do is set up our development environment, andspecifically to setup all the required dependencies to Hibernate as well asother libraries. Hibernate is built using Maven which amongst other featuresprovides dependecy management; moreover it provides transitive dependecymanagement which simply means that to use Hibernate we can simply defineour dependency on Hibernate, Hibernate itself defines the dependencies itneeds which then become transitive dependencies of our project.

.

<project xmlns="http://maven.apache.org/POM/4.0.0"

Page 16: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 1. Introduction à Hibernate

2 Hibernate 3.3.1

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

...

<dependencies>

<dependency>

<groupId>${groupId}</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>

</dependencies>

</project>

Note

Essentially we are describing here the /tutorials/web/pom.xml file.See the Maven [http://maven.org] site for more information.

Astuce

While not strictly necessary, most IDEs have integration with Mavento read these POM files and automatically set up a project for youwhich can save lots of time and effort.

Ensuite, nous créons une classe qui réprésente l'événement que nousvoulons stocker dans notre base de données.

1.2.1. La première classe

Notre première classe persistante est une simple classe JavaBean avecquelques propriétés :

package org.hibernate.tutorial.domain;

import java.util.Date;

public class Event {

private Long id;

private String title;

private Date date;

Page 17: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

La première classe

Hibernate 3.3.1 3

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 pouvez voir que cette classe utilise les conventions de nommagestandard JavaBean pour les méthodes getter/setter des propriétés,ainsi qu'une visibilité privée pour les champs. Ceci est la conceptionrecommandée - mais pas obligatoire. Hibernate peut aussi accéder auxchamps directement, le bénéfice des méthodes d'accès est la robustessepour la refonte de code. Le constructeur sans argument est requis pourinstancier un objet de cette classe via reflexion.

La propriété id contient la valeur d'un identifiant unique pour un événementparticulier. Toutes les classes d'entités persistantes (ainsi que les classesdépendantes de moindre importance) auront besoin d'une telle propriétéidentifiante si nous voulons utiliser l'ensemble complet des fonctionnalitésd'Hibernate. En fait, la plupart des applications (surtout les applicationsweb) ont besoin de distinguer des objets par des identifiants, donc vousdevriez considérer ça comme une fonctionnalité plutôt que comme unelimitation. Cependant, nous ne manipulons généralement pas l'identité d'unobjet, dorénavant la méthode setter devrait être privée. Seul Hibernateassignera les identifiants lorsqu'un objet est sauvegardé. Vous pouvez voirqu'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. Le choix vousest laissé, et vous pouvez l'ajuster à la conception de votre application.

Page 18: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 1. Introduction à Hibernate

4 Hibernate 3.3.1

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

Placez ce fichier source Java dans un répertoire appelé src dans le dossierde développement. Ce répertoire devrait maintenant ressembler à ça :

.

+lib

<Hibernate and third-party libraries>

+src

+events

Event.java

Dans la prochaine étape, nous informons Hibernate de cette classepersistante.

1.2.2. Le fichier de mapping

Hibernate a besoin de savoir comment charger et stocker des objets d'uneclasse persistante. C'est là qu'intervient le fichier de mapping Hibernate.Le fichier de mapping indique à Hibernate à quelle table dans la base dedonnées il doit accéder, et quelles colonnes de cette table il devra utiliser.

La structure basique de ce fichier de mapping ressemble à ça :

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

[...]

</hibernate-mapping>

Notez que la DTD Hibernate est très sophistiquée. Vous pouvez l'utiliserpour l'auto-complétement des éléments et des attributs de mapping XMLdans votre éditeur ou votre IDE. Vous devriez aussi ouvrir le fichier DTDdans votre éditeur de texte - c'est le moyen le plus facile d'obtenir une vued'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 lefichier DTD à partir du web, mais regardera d'abord dans le classpath del'application. Le fichier DTD est inclus dans hibernate3.jar ainsi que dans lerépertoire src de la distribution Hibernate.

Page 19: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Le fichier de mapping

Hibernate 3.3.1 5

Nous omettrons la déclaration de la DTD dans les exemples futurs pourraccourcir le code. Bien sûr il n'est pas optionnel.

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

<hibernate-mapping>

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

</class>

</hibernate-mapping>

Plus loin, nous disons à Hibernate comment persister et charger un objet dela classe Event dans la table EVENTS, chaque instance est représentée parune ligne dans cette table. Maintenant nous continuons avec le mapping dela propriété de l'identifiant unique vers la clef primaire de la table. 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 d'Hibernate pour lacolonne de la clef primaire subrogée :

<hibernate-mapping>

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

<id name="id" column="EVENT_ID">

<generator class="native"/>

</id>

</class>

</hibernate-mapping>

The id element is the declaration of the identifier property, name="id"declares the name of the Java property - Hibernate will use the getter andsetter methods to access the property. The column attribute tells Hibernatewhich column of the EVENTS table we use for this primary key. The nestedgenerator element specifies the identifier generation strategy, in this casewe used native, which picks the best strategy depending on the configureddatabase (dialect). Hibernate supports database generated, globally unique,as well as application assigned identifiers (or any strategy you have writtenan extension for).

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

Page 20: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 1. Introduction à Hibernate

6 Hibernate 3.3.1

<hibernate-mapping>

<class name="events.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 quels getters/setters utiliser.

Pourquoi le mapping de la propriété date inclut l'attribut column, maispas title ? Sans l'attribut column Hibernate utilise par défaut le nomde la propriété comme nom de colonne. Ca fonctionne bien pour title.Cependant, date est un mot clef réservé dans la plupart des bases dedonnées, donc nous utilisons un nom différent pour le mapping.

La prochaine chose intéressante est que le mapping de title manqueaussi d'un attribut type. Les types que nous déclarons et utilisons dans lesfichiers de mapping ne sont pas, comme vous pourriez vous y attendre,des types de données Java. Ce ne sont pas, non plus, des types de basede données SQL. Ces types sont donc appelés des types de mappingHibernate, des convertisseurs qui peuvent traduire des types Java en typesSQL et vice versa. De plus, Hibernate tentera de déterminer la bonneconversion et le type de mapping lui-même si l'attribut type n'est pas présentdans le mapping. Dans certains cas, cette détection automatique (utilisantla réflexion sur la classe Java) pourrait ne pas donner la valeur attendueou dont vous avez besoin. C'est le cas avec la propriété date. Hibernatene peut pas savoir si la propriété "mappera" une colonne SQL de typedate, timestamp ou time. Nous déclarons que nous voulons conserver desinformations avec une date complète et l'heure en mappant la propriété avecun timestamp.

Ce fichier de mapping devrait être sauvegardé en tant que Event.hbm.xml,juste dans le répertoire à côté du fichier source de la classe Java Event. Lenommage des fichiers de mapping peut être arbitraire, cependant le suffixehbm.xml est devenu une convention dans la communauté des développeursHibernate. La structure du répertoire devrait ressembler à ça :

.

+lib

<Hibernate and third-party libraries>

Page 21: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Configuration d'Hibernate

Hibernate 3.3.1 7

+src

+events

Event.java

Event.hbm.xml

Nous poursuivons avec la configuration principale d'Hibernate.

1.2.3. Configuration d'Hibernate

Nous avons maintenant une classe persistante et son fichier de mapping.Il est temps de configurer Hibernate. Avant ça, nous avons besoin d'unebase de données. HSQL DB, un SGBD SQL basé sur Java et travaillant enmémoire, peut être téléchargé à partir du site web de HSQL. En fait, vousavez seulement besoin de hsqldb.jar. Placez ce fichier dans le répertoirelib/ du dossier de développement.

Créez un répertoire appelé data à la racine du répertoire de développement- c'est là que HSQL DB stockera ses fichiers de données. Démarrezmaintenant votre base de données en exécutant java -classpathlib/hsqldb.jar org.hsqldb.Server dans votre répertoire de travail. Vousobservez qu'elle démarre et ouvre une socket TCP/IP, c'est là que notreapplication se connectera plus tard. Si vous souhaitez démarrez à partird'une nouvelle base de données pour ce tutoriel (faites CTRL + C dans lafenêtre the window), effacez le répertoire data/ et redémarrez HSQL DB ànouveau.

Hibernate est la couche de votre application qui se connecte à cette base dedonnées, donc il a besoin des informations de connexion. Les connexionssont établies à travers un pool de connexions JDBC, que nous devons aussiconfigurer. La distribution Hibernate contient différents outils de gestionde pools de connexions JDBC open source, mais pour ce didacticiel nousutiliserons le pool de connexions intégré à Hibernate. Notez que vousdevez copier les bibliothèques requises dans votre classpath et utiliser uneconfiguration de pool de connexions différente si vous voulez utiliser unlogiciel de gestion de pools JDBC tiers avec une qualité de production.

Pour la configuration d'Hibernate, nous pouvons utiliser un simple fichierhibernate.properties, un fichier hibernate.cfg.xml légèrement plussophistiqué, ou même une configuration complète par programmation. Laplupart 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://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

Page 22: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 1. Introduction à Hibernate

8 Hibernate 3.3.1

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

<mapping resource="events/Event.hbm.xml"/>

</session-factory>

</hibernate-configuration>

Notez que cette configuration XML utilise une DTD différente. Nousconfigurons une SessionFactory d'Hibernate - une fabrique globaleresponsable d'une base de données particulière. Si vous avez plusieursbase de données, utilisez plusieurs configurations <session-factory>,généralement dans des fichiers de configuration différents (pour undémarrage plus facile).

Les quatre premiers éléments property contiennent la configurationnécessaire pour la connexion JDBC. L'élément property du dialecte spécifiequelle variante du SQL Hibernate va générer. La gestion automatique dessessions d'Hibernate pour les contextes de persistance sera détaillée très

Page 23: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Construction avec Ant

Hibernate 3.3.1 9

vite. L'option hbm2ddl.auto active la génération automatique des schémasde base de données - directement dans la base de données. Cela peut biensûr aussi être désactivé (en supprimant l'option de configuration) ou redirigévers un fichier avec l'aide de la tâche Ant SchemaExport. Finalement, nousajoutons le(s) fichier(s) de mapping pour les classes persistantes.

Copiez ce fichier dans le répertoire source, il terminera dans la racine duclasspath. Hibernate cherchera automatiquement, au démarrage, un fichierappelé hibernate.cfg.xml dans la racine du classpath.

1.2.4. Construction avec Ant

Nous allons maintenant construire le didacticiel avec Ant. Vous aurezbesoin d'avoir Ant d'installé - récupérez-le à partir de la page detéléchargement de Ant [http://ant.apache.org/bindownload.cgi]. Commentinstaller Ant ne sera pas couvert ici. Référez-vous au manuel d'Ant[http://ant.apache.org/manual/index.html]. Après que vous aurez installé Ant,nous pourrons commencer à créer le fichier de construction. Il s'appellerabuild.xml et sera placé directement dans le répertoire de développement.

Un fichier de construction basique ressemble à ça :

<project name="hibernate-tutorial" default="compile">

<property name="sourcedir" value="${basedir}/src"/>

<property name="targetdir" value="${basedir}/bin"/>

<property name="librarydir" value="${basedir}/lib"/>

<path id="libraries">

<fileset dir="${librarydir}">

<include name="*.jar"/>

</fileset>

</path>

<target name="clean">

<delete dir="${targetdir}"/>

<mkdir dir="${targetdir}"/>

</target>

<target name="compile" depends="clean, copy-resources">

<javac srcdir="${sourcedir}"

destdir="${targetdir}"

classpathref="libraries"/>

</target>

<target name="copy-resources">

<copy todir="${targetdir}">

<fileset dir="${sourcedir}">

<exclude name="**/*.java"/>

</fileset>

Page 24: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 1. Introduction à Hibernate

10 Hibernate 3.3.1

</copy>

</target>

</project>

Cela dira à Ant d'ajouter tous les fichiers du répertoire lib finissant par .jardans le classpath utilisé pour la compilation. Cela copiera aussi tous lesfichiers source non Java dans le répertoire cible, par exemple les fichiersde configuration et de mapping d'Hibernate. Si vous lancez Ant maintenant,vous devriez obtenir cette sortie :

C:\hibernateTutorial\>ant

Buildfile: build.xml

copy-resources:

[copy] Copying 2 files to C:\hibernateTutorial\bin

compile:

[javac] Compiling 1 source file to C:\hibernateTutorial\bin

BUILD SUCCESSFUL

Total time: 1 second

1.2.5. Démarrage et aides

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

Nous créerons une classe d'aide HibernateUtil qui s'occupe du démarrageet rend la gestion des Sessions plus facile. Regardons l'implémentation :

package util;

import org.hibernate.*;

import org.hibernate.cfg.*;

public class HibernateUtil {

private static final SessionFactory sessionFactory;

static {

try {

// Create the SessionFactory from hibernate.cfg.xml

sessionFactory = new

Configuration().configure().buildSessionFactory();

Page 25: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Démarrage et aides

Hibernate 3.3.1 11

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

}

}

Cette classe ne produit pas seulement la SessionFactory globale dans uninitialiseur statique (appelé une seule fois par la JVM lorsque la classe estchargée), elle masque le fait qu'elle exploite un singleton. Elle pourrait aussiobtenir la SessionFactory depuis JNDI dans un serveur d'applications.

Si vous nommez la SessionFactory dans votre fichier de configuration,Hibernate tentera la récupération depuis JNDI. Pour éviter ce code, vouspouvez aussi utiliser un déploiement JMX et laisser le conteneur (compatibleJMX) instancier et lier un HibernateService à JNDI. Ces options avancéessont détaillées dans la documentation de référence Hibernate.

Placez HibernateUtil.java dans le répertoire source de développement, etensuite Event.java :

.

+lib

<Hibernate and third-party libraries>

+src

+events

Event.java

Event.hbm.xml

+util

HibernateUtil.java

hibernate.cfg.xml

+data

build.xml

Cela devrait encore compiler sans problème. Nous avons finalement besoinde configurer le système de "logs" - Hibernate utilise commons-logginget vous laisse le choix entre log4j et le système de logs du JDK 1.4. Laplupart des développeurs préfèrent log4j : copiez log4j.properties de ladistribution d'Hibernate (il est dans le répertoire etc/) dans votre répertoiresrc, puis faites de même avec hibernate.cfg.xml. Regardez la configurationd'exemple et changez les paramètres si vous voulez une sortie plus

Page 26: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 1. Introduction à Hibernate

12 Hibernate 3.3.1

verbeuse. Par défaut, seul le message de démarrage d'Hibernate est affichésur la sortie standard.

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

1.2.6. Charger et stocker des objets

Finalement nous pouvons utiliser Hibernate pour charger et stocker desobjets. Nous écrivons une classe EventManager avec une méthode main() :

package events;

import org.hibernate.Session;

import java.util.Date;

import 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, et le remettons à Hibernate. Hibernates'occupe maintenant du SQL et exécute les INSERTs dans la base de

Page 27: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Charger et stocker des objets

Hibernate 3.3.1 13

données. Regardons le code de gestion de la Session et de la Transactionavant de lancer ça.

Une Session est une unité de travail. Pour le moment, nous allons faire leschoses simplement et assumer une granularité un-un entre une Sessionhibernate et une transaction à la base de données. Pour isoler notre codedu système de transaction sous-jacent (dans notre cas, du pure JDBC, maiscela pourrait être JTA), nous utilisons l'API Transaction qui est disponibledepuis la Session Hibernate.

Que fait sessionFactory.getCurrentSession() ? Premièrement, vous pouvezl'invoquer autant de fois que vous le voulez et n'importe où du moment quevous avez votre SessionFactory (facile grâce à HibernateUtil). La méthodegetCurrentSession() renvoie toujours l'unité de travail courante. Souvenezvous que nous avons basculé notre option de configuration au mécanismebasé sur le "thread" dans hibernate.cfg.xml. Par conséquent, le scope del'unité de travail courante est le thread java courant d'exécution. Ceci n'estpas totalement vrai.

Une Session commence lorsqu'elle est vraiment utilisée la première fois,Lorsque nous appelons pour la première fois getCurrentSession(). Ensuite,elle est liée, par Hibernate, au thread courant. Lorsque la transactions'achève (commit ou rollback), Hibernate délie la Session du thread et laferme pour vous. Si vous invoquez getCurrentSession() une autre fois, vousobtenez une nouvelle Session et pouvez entamer une nouvelle unité detravail. Ce modèle de programmation "thread-bound" est le moyen le pluspopulaire d'utiliser Hibernate.

UNTRANSLATED ! Related to the unit of work scope, should the HibernateSession be used to execute one or several database operations? The aboveexample uses one Session for one operation. This is pure coincidence,the example is just not complex enough to show any other approach. Thescope of a Hibernate Session is flexible but you should never design yourapplication to use a new Hibernate Session for every database operation. Soeven if you see it a few more times in the following (very trivial) examples,consider session-per-operation an anti-pattern. A real (web) application isshown later in this tutorial.

Lisez Chapitre 11, Transactions et accès concurrents pour plusd'informations sur la gestion des transactions et leur démarcations. Nousn'avons pas géré les erreurs et rollback sur l'exemple précédent.

Pour lancer cette première routine, nous devons ajouter une cible appelabledans le fichier de construction de Ant :

<target name="run" depends="compile">

Page 28: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 1. Introduction à Hibernate

14 Hibernate 3.3.1

<java fork="true" classname="events.EventManager"

classpathref="libraries">

<classpath path="${targetdir}"/>

<arg value="${action}"/>

</java>

</target>

La valeur de l'argument action correspond à la ligne de commande quiappelle la cible :

C:\hibernateTutorial\>ant run -Daction=store

Vous devriez voir, après la compilation, Hibernate démarrer et, en fonctionde votre configuration, beaucoup de traces sur la sortie. À la fin voustrouverez la ligne suivante :

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

values (?, ?, ?)

C'est l'INSERT exécuté par Hibernate, les points d'interrogation représententles paramètres JDBC liés. Pour voir les valeurs liées aux arguments, ou pourréduire la verbosité des traces, vérifier votre log4j.properties.

Maintenant nous aimerions aussi lister les événements stockés, donc nousajoutons 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();

Page 29: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Partie 2 - Mapper des associations

Hibernate 3.3.1 15

return result;

}

Ce que nous faisons ici c'est utiliser une requête HQL (Hibernate QueryLanguage) pour charger tous les objets Event existants de la base dedonnées. Hibernate générera le SQL approprié, l'enverra à la base dedonnées et peuplera des objets Event avec les données. Vous pouvez créerdes requêtes plus complexes avec HQL, bien sûr.

Maintenant, pour exécuter et tester tout ça, suivez ces étapes :

• Exécutez ant run -Daction=store pour stocker quelque chose dans labase de données et, bien sûr, pour générer, avant, le schéma de la basede données grâce à hbm2ddl.

• Now disable hbm2ddl by commenting out the property in yourhibernate.cfg.xml file. Usually you only leave it turned on in continuousunit testing, but another run of hbm2ddl would drop everything you havestored - the create configuration setting actually translates into "drop alltables from the schema, then re-create all tables, when the SessionFactoryis build".

Si maintenant vous appelez Ant avec -Daction=list, vous devriez voir lesévénements que vous avez stockés jusque là. Vous pouvez bien sûr aussiappeler l'action store plusieurs fois.

UNTRANSLATED! Note: Most new Hibernate users fail at this point and wesee questions about Table not found error messages regularly. However,if you follow the steps outlined above you will not have this problem, ashbm2ddl creates the database schema on the first run, and subsequentapplication restarts will use this schema. If you change the mapping and/ordatabase schema, you have to re-enable hbm2ddl once again.

1.3. Partie 2 - Mapper des associations

Nous avons mappé une classe d'une entité persistante vers une table.Partons de là et ajoutons quelques associations de classe. D'abordnous ajouterons des gens à notre application, et stockerons une listed'événements auxquels ils participent.

1.3.1. Mapper la classe Person

La première version de la classe Person est simple :

package events;

public class Person {

Page 30: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 1. Introduction à Hibernate

16 Hibernate 3.3.1

private Long id;

private int age;

private String firstname;

private String lastname;

public Person() {}

// Accessor methods for all properties, private setter for 'id'

}

Créez un nouveau fichier de mapping appelé Person.hbm.xml (n'oubliez pasla référence à la DTD)

<hibernate-mapping>

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

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

<generator class="native"/>

</id>

<property name="age"/>

<property name="firstname"/>

<property name="lastname"/>

</class>

</hibernate-mapping>

Finalement, ajoutez la nouveau mapping à la configuration d'Hibernate :

<mapping resource="events/Event.hbm.xml"/>

<mapping resource="events/Person.hbm.xml"/>

Nous allons maintenant créer une association entre ces deux entités.Évidemment, des personnes peuvent participer aux événements, et desévénements ont des participants. Les questions de conception que nousdevons traiter sont : direction, cardinalité et comportement de la collection.

1.3.2. Une association unidirectionnelle basée sur Set

Nous allons ajouter une collection d'événements à la classe Person. De cettemanière nous pouvons facilement naviguer dans les événements d'unepersonne particulière, sans exécuter une requête explicite - en appelantaPerson.getEvents(). Nous utilisons une collection Java, un Set, parce que lacollection ne contiendra pas d'éléments dupliqués et l'ordre ne nous importepas.

Nous avons besoin d'une association unidirectionnelle, pluri-valuée,implémentée avec un Set. Écrivons le code pour ça dans les classes Java etmappons les :

Page 31: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Une association unidirectionnelle basée surSet

Hibernate 3.3.1 17

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 alors, nouspourrions créer une autre collection sur Event, si nous voulons êtrecapable de la parcourir de manière bidirectionnelle, c'est-à-dire avoiranEvent.getParticipants(). Ce n'est pas nécessaire d'un point devue fonctionnel. Vous pourrez toujours exécuter une requête explicitepour récupérer les participants d'un "event" particulier. Ce choix deconception vous est laissé, mais ce qui reste certains est la cardinalité del'association: "plusieurs" des deux côtés, nous appelons cela une associationmany-to-many. Par conséquent nous utilisons un mapping Hibernatemany-to-many:

<class name="events.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="events.Event"/>

</set>

</class>

Hibernate supporte toutes sortes de mapping de collection, un <set> étantle plus commun. Pour une association many-to-many (ou une relationd'entité n:m), une table d'association est requise. Chaque ligne dans cettetable représente un lien entre une personne et un événement. Le nom dela table est configuré avec l'attribut table de l'élément set. Le nom de lacolonne identifiant dans l'association, du côté de la personne, est défini avecl'élément <key>, et le nom de la colonne pour l'événement dans l'attributcolumn de <many-to-many>. Vous devez aussi donner à Hibernate la classe

Page 32: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 1. Introduction à Hibernate

18 Hibernate 3.3.1

des objets de votre collection (c'est-à-dire : la classe de l'autre côté de lacollection).

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

_____________ __________________

| | | | _____________

| EVENTS | | PERSON_EVENT | | |

|_____________| |__________________| | PERSON |

| | | | |_____________|

| *EVENT_ID | <--> | *EVENT_ID | | |

| EVENT_DATE | | *PERSON_ID | <--> | *PERSON_ID |

| TITLE | |__________________| | AGE |

|_____________| | FIRSTNAME |

| LASTNAME |

|_____________|

1.3.3. Travailler avec l'association

Réunissons quelques personnes et quelques événements dans une nouvellemé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 lacollection en utilisant les méthodes normales de la collection. Comme vouspouvez le voir, il n'y a pas d'appel explicite à update() ou save(), Hibernatedétecte automatiquement que la collection a été modifiée et a besoind'être mise à jour. Ceci est appelé la vérification sale automatique (NdT :"automatic dirty checking"), et vous pouvez aussi l'essayer en modifiant lenom ou la propriété date de n'importe lequel de vos objets. Tant qu'ils sontdans 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 correspondant.Le processus de synchronisation de l'état de la mémoire avec la base de

Page 33: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Travailler avec l'association

Hibernate 3.3.1 19

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 (ourollback) de la transaction avec la base de données - comme défini par notreoption thread de configuration pour la classe CurrentSessionContext.

Vous pourriez bien sûr charger une personne et un événement dansdifférentes unités de travail. Ou vous modifiez un objet à l'extérieur d'uneSession, s'il n'est pas dans un état persistant (s'il était persistant avant, nousappelons cet état détaché). Vous pouvez même modifier une collectionlorsqu'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 pourriezdire qu'il le lie à une unité de travail, ainsi toutes les modifications (ajout,suppression) que vous avez faites pendant qu'il était détaché peuvent êtresauvegardées dans la base de données (il se peut que vous ayez besoinde modifier quelques unes des méthodes précédentes pour retourner cetidentifiant).

Page 34: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 1. Introduction à Hibernate

20 Hibernate 3.3.1

Ce n'est pas très utile dans notre situation actuelle, mais c'est un conceptimportant que vous pouvez mettre dans votre propre application. Pourle moment, complétez cet exercice en ajoutant une nouvelle action à laméthode principale des EventManagers et appelez la à partir de la ligne decommande. Si vous avez besoin des identifiants d'une personne et d'unévénement - la méthode save() les retourne.

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êmeimportance, deux entités. Comme mentionné plus tôt, il y a d'autres classeset 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 appelonsces 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 personnesne référencent pas le même objet firstname, même si elles ont le mêmeprénom). Bien sûr, des types de valeur ne peuvent pas seulement êtretrouvés dans le JDK (en fait, dans une application Hibernate toutes lesclasses du JDK sont considérées comme des types de valeur), vouspouvez aussi écrire vous-même des classes dépendantes, Address ouMonetaryAmount, par exemple.

Vous pouvez aussi concevoir une collection de types de valeur. C'estconceptuellement très différent d'une collection de références vers d'autresentités, mais très ressemblant en Java.

1.3.4. Collection de valeurs

Nous ajoutons une collection d'objets de type de valeur à l'entité Person.Nous voulons stocker des adresses email, donc le type que nous utilisonsest String, et la collection est encore un Set :

private Set emailAddresses = new HashSet();

public Set getEmailAddresses() {

return emailAddresses;

}

public void setEmailAddresses(Set emailAddresses) {

this.emailAddresses = emailAddresses;

Page 35: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Collection de valeurs

Hibernate 3.3.1 21

}

Le mapping 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 mapping vu plus tôt est la partie element, laquelledit à Hibernate que la collection ne contient pas de références vers une autreentité, mais une collection d'éléments de type String (le nom en minusculevous indique que c'est un type/convertisseur du mapping Hibernate). Unefois encore, l'attribut table de l'élément set détermine le nom de la table pourla collection. L'élément key définit le nom de la colonne de la clef étrangèredans la table de la collection. L'attribut column dans l'élément element définitle nom de la colonne où les valeurs de String seront réellement stockées.

Regardons 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 clef primaire de la table de la collection est en faitune clef composée, utilisant deux colonnes. Ceci implique aussi qu'il ne peutpas y avoir d'adresses email dupliquées par personne, ce qui est exactementla sémantique dont nous avons besoin pour un ensemble en Java.

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

Page 36: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 1. Introduction à Hibernate

22 Hibernate 3.3.1

private void addEmailToPerson(Long personId, String emailAddress) {

Session session =

HibernateUtil.getSessionFactory().getCurrentSession();

session.beginTransaction();

Person aPerson = (Person) session.load(Person.class, personId);

// The getEmailAddresses() might trigger a lazy load of the

collection

aPerson.getEmailAddresses().add(emailAddress);

session.getTransaction().commit();

}

This time we didn't use a fetch query to initialize the collection. Hence, thecall to its getter method will trigger an additional select to initialize it, so wecan add an element to it. Monitor the SQL log and try to optimize this with aneager fetch.

1.3.5. Associations bidirectionnelles

Ensuite nous allons mapper une association bidirectionnelle - fairefonctionner l'association entre une personne et un événement à partir desdeux côtés en Java. Bien sûr, le schéma de la base de données ne changepas, nous avons toujours une pluralité many-to-many. Une base de donnéesrelationnelle est plus flexible qu'un langage de programmation réseau, doncelle n'a pas besoin de direction de navigation - les données peuvent êtrevues et récupérées de toutes les manières possibles.

D'abord, ajouter 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 mapper 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="events.Person"/>

</set>

Page 37: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Travailler avec des liens bidirectionnels

Hibernate 3.3.1 23

Comme vous le voyez, ce sont des mappings de sets normaux dans lesdeux documents de mapping. Notez que les noms de colonne dans key etmany-to-many sont inversés dans les 2 documents de mapping. L'ajout le plusimportant ici est l'attribut inverse="true" dans l'élément set du mapping de lacollection des Events.

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

1.3.6. Travailler avec des liens bidirectionnels

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

Many developers program defensively and create link management methodsto correctly set both sides, e.g. in 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 maintenantprotégées - ceci permet à des classes du même paquet et aux sous-classesd'accéder encore aux méthodes, mais empêche n'importe qui d'autre demettre le désordre directement dans les collections (enfin, presque). Vousdevriez probablement faire de même avec la collection de l'autre côté.

Page 38: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 1. Introduction à Hibernate

24 Hibernate 3.3.1

Et à propos de l'attribut de mapping inverse ? Pour vous, et pour Java, unlien bidirectionnel est simplement une manière de configurer correctementles références des deux côtés. Hibernate n'a cependant pas assezd'informations pour ordonner correctement les expressions SQL INSERTet UPDATE (pour éviter les violations de contrainte), et a besoin d'aide pourgérer proprement les associations bidirectionnelles. Rendre inverse uncôté d'une assocation dit à Hibernate de l'ignorer essentiellement, pour leconsidérer comme un miroir de l'autre côté. C'est tout ce qui est nécessaire àHibernate pour découvrir tout des problèmes de transformation d'un modèlede navigation directionnelle vers un schéma SQL de base de données.Les règles dont vous devez vous souvenir sont : toutes les associationsbidirectionnelles ont besoin d'un côté marqué inverse. Dans une associationun-vers-plusieurs vous pouvez choisir n'importe quel côté, il n'y a pas dedifférence.

1.4. Part 3 - L'application web EventManager

Let's turn the following discussion into a small web application...

Une application web Hibernate utilise la Session et Transaction comme uneapplication standalone. Cependant, quelques patterns sont utiles. Nousallons coder une EventManagerServlet. Cette servlet peut lister tous lesévènements stockés dans la base de données, et fournir une formulaireHTML pour saisir d'autres évènements.

1.4.1. Ecrire la servlet de base

Créons une nouvelle classe dans notre répertoire source, dans le packageevents:

package events;

// Imports

public class EventManagerServlet extends HttpServlet {

// Servlet code

}

The servlet handles HTTP GET requests only, hence, the method weimplement is doGet():

protected void doGet(HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException {

Page 39: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Procéder et rendre

Hibernate 3.3.1 25

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

throw new ServletException(ex);

}

}

The pattern we are applying here is called session-per-request. When arequest hits the servlet, a new Hibernate Session is opened through thefirst call to getCurrentSession() on the SessionFactory. Then a databasetransaction is started-all data access as to occur inside a transaction, nomatter if data is read or written (we don't use the auto-commit mode inapplications).

UNTRANSLATED Do not use a new Hibernate Session for every databaseoperation. Use one Hibernate Session that is scoped to the whole request.Use getCurrentSession(), so that it is automatically bound to the current Javathread.

Ensuite, les actions possibles de la requêtes sont exécutées et la réponseHTML est rendue. Nous en parlerons plus tard.

Finally, the unit of work ends when processing and rendering is complete.If any problem occurred during processing or rendering, an exception willbe thrown and the database transaction rolled back. This completes thesession-per-request pattern. Instead of the transaction demarcation code inevery servlet you could also write a servlet filter. See the Hibernate websiteand Wiki for more information about this pattern, called Open Session inView-you'll need it as soon as you consider rendering your view in JSP, not ina servlet.

1.4.2. Procéder et rendre

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

Page 40: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 1. Introduction à Hibernate

26 Hibernate 3.3.1

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

Granted, this coding style with a mix of Java and HTML would not scale ina more complex application-keep in mind that we are only illustrating basicHibernate concepts in this tutorial. The code prints an HTML header and afooter. Inside this page, an HTML form for event entry and a list of all eventsin the database are printed. The first method is trivial and only outputs 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 courantpour exécuter la requête:

Page 41: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Déployer et tester

Hibernate 3.3.1 27

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

for (Iterator it = result.iterator(); 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>");

}

}

FEnfin, l'action store renvoie à la méthode createAndStoreEvent(), qui utiliseaussi 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);

}

That's it, the servlet is complete. A request to the servlet will be processedin a single Session and Transaction. As earlier in the standalone application,Hibernate can automatically bind these objects to the current thread ofexecution. This gives you the freedom to layer your code and access theSessionFactory in any way you like. Usually you'd use a more sophisticateddesign and move the data access code into data access objects (the DAOpattern). See the Hibernate Wiki for more examples.

1.4.3. Déployer et tester

Pour déployer cette application, vous devez créer une archive Web, un War.Ajoutez la cible Ant suivante dans votre build.xml:

Page 42: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 1. Introduction à Hibernate

28 Hibernate 3.3.1

<target name="war" depends="compile">

<war destfile="hibernate-tutorial.war" webxml="web.xml">

<lib dir="${librarydir}">

<exclude name="jsdk*.jar"/>

</lib>

<classes dir="${targetdir}"/>

</war>

</target>

Cette cible créé un fichier nommé hibernate-tutorial.war dans le répertoirede votre projet. Elle package les bibliothèques et le descripteur web.xml quiest attendu dans le répertoire racine de votre projet:

<?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>events.EventManagerServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>Event Manager</servlet-name>

<url-pattern>/eventmanager</url-pattern>

</servlet-mapping>

</web-app>

Before you compile and deploy the web application, note that an additionallibrary is required: jsdk.jar. This is the Java servlet development kit, if youdon't have this library already, get it from the Sun website and copy it to yourlibrary directory. However, it will be only used for compilation and excludedfrom the WAR package.

Pour construire et déployer, appelez ant war dans votre projet et copier lefichier hibernate-tutorial.war dans le répertoire webapp de tomcat Si vousn'avez pas installé Tomcat, téléchargez le et suivez la notice d'installation.Vous n'avez pas à modifier la configuration Tomcat pour déployer cetteapplication.

Une fois l'application déployée et Tomcat lancé, accédez à l'applicationvia http://localhost:8080/hibernate-tutorial/eventmanager. Assurezvous de consulter les traces tomcat pour observer l'initialisation d'Hibernateà la première requête touchant votre servlet (l'initialisation statique dans

Page 43: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Résumé

Hibernate 3.3.1 29

HibernateUtil est invoquée) et pour vérifier qu'aucune exception nesurvienne.

1.5. Résumé

Ce didacticiel a couvert les bases de l'écriture d'une simple applicationHibernate ainsi qu'une petite application web.

Si vous êtes déjà confiants avec Hibernate, continuez à parcourir lessujets que vous trouvez intéressants à travers la table des matières dela documentation de référence - les plus demandés sont le traitementtransactionnel (Chapitre 11, Transactions et accès concurrents), laperformance des récupérations d'information (Chapitre 19, Améliorer lesperformances), ou l'utilisation de l'API (Chapitre 10, Travailler avec desobjets) et les fonctionnalités des requêtes (Section 10.4, « Requêtage »).

N'oubliez pas de vérifier le site web d'Hibernate pour d'autres didacticiels(plus spécialisés).

Page 44: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

30 Hibernate 3.3.1

Page 45: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 31

Chapitre 2. Architecture

2.1. Généralités

Voici une vue (très) haut niveau de l'architecture d'Hibernate :

Ce diagramme montre Hibernate utilisant une base de données et desdonnées de configuration pour fournir un service de persistance (et desobjets persistants) à l'application.

Nous aimerions décrire une vue plus détaillée de l'architecture.Malheureusement, Hibernate est flexible et supporte différentes approches.Nous allons en montrer les deux extrêmes. L'architecture légère laissel'application fournir ses propres connexions JDBC et gérer ses proprestransactions. Cette approche utilise le minimum des APIs Hibernate :

Page 46: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 2. Architecture

32 Hibernate 3.3.1

L'architecture la plus complète abstrait l'application des APIs JDBC/JTAsous-jacentes et laisse Hibernate s'occuper des détails.

Heres some definitions of the objects in the diagrams:

Page 47: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Généralités

Hibernate 3.3.1 33

SessionFactory (org.hibernate.SessionFactory)Un cache threadsafe (immuable) des mappings vers une (et une seule)base de données. Une factory (fabrique) de Session et un client deConnectionProvider. Peut contenir un cache optionnel de données (desecond niveau) qui est réutilisable entre les différentes transactions quecela soit au sein du même processus (JVLM) ou par plusieurs nœudsd'un cluster.

Session (org.hibernate.Session)Un objet mono-threadé, à durée de vie courte, qui représente uneconversation entre l'application et l'entrepôt de persistance. Encapsuleune connexion JDBC. Factory (fabrique) des objets Transaction. Contientun cache (de premier niveau) des objets persistants, ce cache estobligatoire. Il est utilisé lors de la navigation dans le graphe d'objets oulors de la récupération d'objets par leur identifiant.

Objets et Collections persistantsObjets mono-threadés à vie courte contenant l'état de persistance et lafonction métier. Ceux-ci sont en général les objets de type JavaBean(ou POJOs) ; la seule particularité est qu'ils sont associés avec une (etune seule) Session. Dès que la Session est fermée, ils seront détachéset libres d'être utilisés par n'importe laquelle des couches de l'application(ie. de et vers la présentation en tant que Data Transfer Objects - DTO :objet de transfert de données).

Objets et collections transientsInstances de classes persistantes qui ne sont actuellement pasassociées à une Session. Elles ont pu être instanciées par l'application etne pas avoir (encore) été persistées ou elle ont pu être instanciées parune Session fermée.

Transaction (org.hibernate.Transaction)(Optionnel) Un objet mono-threadé à vie courte utilisé par l'applicationpour définir une unité de travail atomique. Abstrait l'application destransactions sous-jacentes qu'elles soient JDBC, JTA ou CORBA.Une Session peut fournir plusieurs Transactions dans certains cas.Toutefois, la délimitation des transactions, via l'API d'Hibernate ou par laTransaction sous-jacente, n'est jamais optionnelle!

ConnectionProvider (org.hibernate.connection.ConnectionProvider)(Optionnel) Une fabrique de (pool de) connexions JDBC. Abstraitl'application de la Datasource ou du DriverManager sous-jacent. Nonexposé à l'application, mais peut être étendu/implémenté par ledéveloppeur.

Page 48: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 2. Architecture

34 Hibernate 3.3.1

TransactionFactory (org.hibernate.TransactionFactory)(Optionnel) Une fabrique d'instances de Transaction. Non exposé àl'application, mais peut être étendu/implémenté par le développeur.

Interfaces d'extensionHibernate fournit de nombreuses interfaces d'extensions optionnelles quevous pouvez implémenter pour personnaliser le comportement de votrecouche de persistance. Reportez vous à la documentation de l'API pourplus de détails.

Dans une architecture légère, l'application n'aura pas à utiliser lesAPIs Transaction/TransactionFactory et/ou n'utilisera pas les APIsConnectionProvider pour utiliser JTA ou JDBC.

2.2. Etats des instances

Une instance d'une classe persistante peut être dans l'un des trois étatssuivants, définis par rapport à un contexte de persistance. L'objet Sessiond'hibernate correspond à ce concept de contexte de persistance :

passager (transient)L'instance n'est pas et n'a jamais été associée à un contexte depersistance. Elle ne possède pas d'identité persistante (valeur de cléprimaire)

persistantL'instance est associée au contexte de persistance. Elle possèdeune identité persistante (valeur de clé primaire) et, peut-être, unenregistrement correspondant dans la base. Pour un contexte depersistance particulier, Hibernate garantit que l'identité persistante estéquivalente à l'identité Java (emplacement mémoire de l'objet)

détachéThe instance was once associated with a persistence context, but thatcontext was closed, or the instance was serialized to another process.It has a persistent identity and, perhaps, a corresponding row in thedatabase. For detached instances, Hibernate makes no guarantees aboutthe relationship between persistent identity and Java identity.

2.3. Intégration JMX

JMX est le standard J2EE de gestion des composants Java. Hibernate peutêtre géré via un service JMX standard. Nous fournissons une implémentationd'un MBean dans la distribution : org.hibernate.jmx.HibernateService.

Page 49: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Support JCA

Hibernate 3.3.1 35

Pour avoir un exemple sur la manière de déployer Hibernate en tant queservice JMX dans le serveur d'application JBoss Application Server, référezvous au guide utilisateur JBoss (JBoss User Guide). Si vous déployezHibernate via JMX sur JBoss AS, vous aurez également les bénéficessuivants :

• Gestion de la session : Le cycle de vie de la Session Hibernate peut êtreautomatiquement limitée à la portée d'une transaction JTA. Cela signifieque vous n'avez plus besoin d'ouvrir et de fermer la Session manuellement,cela devient le travail de l'intercepteur EJB de JBoss. Vous n'avez pasnon plus à vous occuper des démarcations des transactions dans votrecode (sauf si vous voulez écrire une couche de persistance qui soitportable, dans ce cas vous pouvez utiliser l'API optionnelle Transactiond'Hibernate). Vous appelez l'HibernateContext pour accéder à la Session.

• Déploiement HAR : Habituellement vous déployez le service JMXHibernate en utilisant le descripteur de déploiement de JBoss (dans unfichier EAR et/ou un SAR), il supporte toutes les options de configurationusuelles d'une SessionFactory Hibernate. Cependant, vous devez toujoursnommer tous vos fichiers de mapping dans le descripteur de déploiement.Si vous décidez d'utiliser le déploiement optionnel sous forme de HAR,JBoss détectera automatiquement tous vos fichiers de mapping dans votrefichier HAR.

Consultez le guide d'utilisation de JBoss AS pour plus d'informations sur cesoptions.

Les statistiques pendant l'exécution d'Hibernate (au runtime) sont uneautre fonctionnalité disponible en tant que service JMX. Voyez pour celaSection 3.4.6, « Statistiques Hibernate ».

2.4. Support JCA

Hibernate peut aussi être configuré en tant que connecteur JCA.Référez-vous au site web pour de plus amples détails. Il est importantde noter que le support JCA d'Hibernate est encore considéré commeexpérimental.

2.5. Sessions Contextuelles

Certaines applications utilisant Hibernate ont besoin d'une sorte de session"contextuelle", où une session est liée à la portée d'un contexte particulier.Cependant, les applications ne définissent pas toutes la notion de contextede la même manière, et différents contextes définissent différentes portéesà la notion de "courant". Les applications à base d'Hibernate, versions

Page 50: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 2. Architecture

36 Hibernate 3.3.1

précédentes à la 3.0 utilisaient généralement un principe maison de sessionscontextuelles basées sur le ThreadLocal, ainsi que sur des classes utilitairescomme HibernateUtil, ou utilisaient des framework tiers (comme Spring ouPico) qui fournissaient des sessions contextuelles basées sur l'utilisation deproxy/interception.

A partir de la version 3.0.1, Hibernate a ajouté la méthodeSessionFactory.getCurrentSession(). Initialement, cela demandait l'usage detransactions JTA, où la transaction JTA définissait la portée et le contexte dela session courante. L'équipe Hibernate pense que, étant donnée la maturitédes implémentations de JTA TransactionManager , la plupart (sinon toutes)des applications devraient utiliser la gestion des transactions par JTA qu'ellessoient ou non déployées dans un conteneur J2EE. Par conséquent, vousdevriez toujours contextualiser vos sessions, si vous en avez besoin, via laméthode basée sur JTA.

Cependant, depuis la version 3.1, la logique derrièreSessionFactory.getCurrentSession() est désormaisbranchable. A cette fin, une nouvelle interface d'extension(org.hibernate.context.CurrentSessionContext) et un nouveau paramètrede configuration (hibernate.current_session_context_class) ont été ajoutéspour permettre de configurer d'autres moyens de définir la portée et lecontexte des sessions courantes.

Allez voir les Javadocs de l'interfaceorg.hibernate.context.CurrentSessionContext pour une description détailléede son contrat. Elle définit une seule méthode, currentSession(), depuislaquelle l'implémentation est responsable de traquer la session courante ducontexte. Hibernate fournit deux implémentation de cette interface.

• org.hibernate.context.JTASessionContext - les sessions courantes sontassociées à une transaction JTA. La logique est la même que l'ancienneapproche basée sur JTA. Voir les javadocs pour les détails.

• org.hibernate.context.ThreadLocalSessionContext - les sessionscourantes sont associées au thread d'exécution. Voir les javadocs pour lesdétails.

• org.hibernate.context.ManagedSessionContext - current sessions aretracked by thread of execution. However, you are responsible to bind andunbind a Session instance with static methods on this class, it does neveropen, flush, or close a Session.

The first two implementations provide a "one session - onedatabase transaction" programming model, also known and used assession-per-request. The beginning and end of a Hibernate session is

Page 51: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Sessions Contextuelles

Hibernate 3.3.1 37

defined by the duration of a database transaction. If you use programmatictransaction demarcation in plain JSE without JTA, you are advised to usethe Hibernate Transaction API to hide the underlying transaction systemfrom your code. If you use JTA, use the JTA interfaces to demarcatetransactions. If you execute in an EJB container that supports CMT,transaction boundaries are defined declaratively and you don't need anytransaction or session demarcation operations in your code. Refer toChapitre 11, Transactions et accès concurrents for more information andcode examples.

Le paramètre de configuration hibernate.current_session_context_classdéfinit quelle implémentation deorg.hibernate.context.CurrentSessionContext doit être utilisée.Notez que pour assurer la compatibilité avec les versionsprécédentes, si ce paramètre n'est pas défini mais qu'unorg.hibernate.transaction.TransactionManagerLookup est configuré,Hibernate utilisera le org.hibernate.context.JTASessionContext. Lavaleur de ce paramètre devrait juste nommer la classe d'implémentation àutiliser, pour les deux implémentations fournies, il y a cependant deux aliascorrespondant: "jta" et "thread".

Page 52: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

38 Hibernate 3.3.1

Page 53: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 39

Chapitre 3. ConfigurationParce qu'Hibernate est conçu pour fonctionner dans différentsenvironnements, il existe beaucoup de paramètres de configuration.Heureusement, la plupart ont des valeurs par défaut appropriées et ladistribution d'Hibernate contient un exemple de fichier hibernate.propertiesdans le répertoire etc/ qui montre les différentes options. Vous n'avez qu'àplacer ce fichier dans votre classpath et à l'adapter.

3.1. Configuration par programmation

An instance of org.hibernate.cfg.Configuration represents an entireset of mappings of an application's Java types to an SQL database.The org.hibernate.cfg.Configuration is used to build an (immutable)org.hibernate.SessionFactory. The mappings are compiled from variousXML mapping files.

You may obtain a org.hibernate.cfg.Configuration instance by instantiatingit directly and specifying XML mapping documents. If the mapping files are inthe classpath, use addResource():

Configuration cfg = new Configuration()

.addResource("Item.hbm.xml")

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

Une alternative (parfois meilleure) est de spécifier les classes mappées et delaisser Hibernate trouver les documents de mapping pour vous :

Configuration cfg = new Configuration()

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

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

Then Hibernate will look for mapping files named/org/hibernate/auction/Item.hbm.xml and/org/hibernate/auction/Bid.hbm.xml in the classpath. This approacheliminates any hardcoded filenames.

A org.hibernate.cfg.Configuration also allows you to specify configurationproperties:

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

Page 54: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 3. Configuration

40 Hibernate 3.3.1

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

1. Pass an instance of java.util.Properties toConfiguration.setProperties().

2. Place a file named hibernate.properties in a root directory of theclasspath.

3. Positionner les propriétés System en utilisant java -Dproperty=value.

4. Inclure des éléments <property> dans le fichier hibernate.cfg.xml (voirplus loin).

hibernate.properties is the easiest approach if you want to get startedquickly.

The org.hibernate.cfg.Configuration is intended as a startup-time object, tobe discarded once a SessionFactory is created.

3.2. Obtenir une SessionFactory

When all mappings have been parsed by theorg.hibernate.cfg.Configuration, the application must obtain a factory fororg.hibernate.Session instances. This factory is intended to be shared by allapplication threads:

SessionFactory sessions = cfg.buildSessionFactory();

Hibernate does allow your application to instantiate more than oneorg.hibernate.SessionFactory. This is useful if you are using more than onedatabase.

3.3. Connexions JDBC

Usually, you want to have the org.hibernate.SessionFactory create andpool JDBC connections for you. If you take this approach, opening aorg.hibernate.Session is as simple as:

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

Dès que vous ferez quelquechose qui requiert un accès à la base dedonnées, une connexion JDBC sera récupérée dans le pool.

For this to work, we need to pass some JDBC connection properties toHibernate. All Hibernate property names and semantics are defined on

Page 55: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Connexions JDBC

Hibernate 3.3.1 41

the class org.hibernate.cfg.Environment. We will now describe the mostimportant settings for JDBC connection configuration.

Hibernate will obtain (and pool) connections using java.sql.DriverManager ifyou set the following properties:

Tableau 3.1. Propriétés JDBC d'Hibernate

Property name Purpose

hibernate.connection.driver_class Classe du driver jdbc

hibernate.connection.url URL jdbc

hibernate.connection.username utilisateur de la base de données

hibernate.connection.password mot de passe de la base de données

hibernate.connection.pool_size nombre maximum de connexionsdans le pool

Hibernate's own connection pooling algorithm is however quite rudimentary.It is intended to help you get started and is not intended for use in aproduction system or even for performance testing. You should usea third party pool for best performance and stability. Just replace thehibernate.connection.pool_size property with connection pool specificsettings. This will turn off Hibernate's internal pool. For example, you mightlike to use C3P0.

C3P0 is an open source JDBC connection pool distributedalong with Hibernate in the lib directory. Hibernate will use itsorg.hibernate.connection.C3P0ConnectionProvider for connection poolingif you set hibernate.c3p0.* properties. If you'd like to use Proxool refer tothe packaged hibernate.properties and the Hibernate web site for moreinformation.

Here is an example hibernate.properties file for 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

For use inside an application server, you should almost alwaysconfigure Hibernate to obtain connections from an application server

Page 56: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 3. Configuration

42 Hibernate 3.3.1

javax.sql.Datasource registered in JNDI. You'll need to set at least one ofthe following properties:

Tableau 3.2. Propriété d'une Datasource Hibernate

Property name Purpose

hibernate.connection.datasource Nom JNDI de la datasource

hibernate.jndi.url URL of the JNDI provider (optional)

hibernate.jndi.class class of the JNDIInitialContextFactory (optional)

hibernate.connection.username database user (optional)

hibernate.connection.password database user password (optional)

Here's an example hibernate.properties file for an application serverprovided JNDI datasource:

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 participerontautomatiquement aux transactions gérées par le conteneur du serveurd'applications.

Arbitrary connection properties may be given by prepending"hibernate.connection" to the connection property name. Forexample, you may specify a charSet connection property usinghibernate.connection.charSet.

You may define your own plugin strategy for obtaining JDBC connections byimplementing the interface org.hibernate.connection.ConnectionProvider,and specifying your custom implementation via thehibernate.connection.provider_class property.

3.4. Propriétés de configuration optionnelles

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

Warning: some of these properties are "system-level" only.System-level properties can be set only via java -Dproperty=value or

Page 57: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Propriétés de configuration optionnelles

Hibernate 3.3.1 43

hibernate.properties. They may not be set by the other techniquesdescribed above.

Page 58: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 3. Configuration

44 Hibernate 3.3.1

Tableau 3.3. Propriétés de configuration d'Hibernate

Property name Purpose

hibernate.dialect The classname of a Hibernateorg.hibernate.dialect.Dialect whichallows Hibernate to generate SQLoptimized for a particular relationaldatabase.

eg. full.classname.of.Dialect

In most cases Hibernate willactually be able to chose the correctorg.hibernate.dialect.Dialect

implementation to use based on theJDBC metadata returned by the JDBCdriver.

hibernate.show_sql Write all SQL statements to console.This is an alternative to setting thelog category org.hibernate.SQL todebug.

eg. true | false

hibernate.format_sql Pretty print the SQL in the log andconsole.

eg. true | false

hibernate.default_schema Qualify unqualified table names withthe given schema/tablespace ingenerated SQL.

eg. SCHEMA_NAME

hibernate.default_catalog Qualify unqualified table names withthe given catalog in generated SQL.

eg. CATALOG_NAME

hibernate.session_factory_name The org.hibernate.SessionFactorywill be automatically bound to thisname in JNDI after it has beencreated.

eg. jndi/composite/name

hibernate.max_fetch_depth Set a maximum "depth" for theouter join fetch tree for single-endedassociations (one-to-one,many-to-one). A 0 disables defaultouter join fetching.

ex. valeurs recommandées entre 0 et3

hibernate.default_batch_fetch_size Set a default size for Hibernate batchfetching of associations.

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

hibernate.default_entity_mode Set a default mode for entityrepresentation for all sessionsopened from this SessionFactory

dynamic-map, dom4j, pojo

hibernate.order_updates Force Hibernate to order SQLupdates by the primary key value ofthe items being updated. This willresult in fewer transaction deadlocksin highly concurrent systems.

eg. true | false

hibernate.generate_statistics If enabled, Hibernate will collectstatistics useful for performancetuning.

eg. true | false

hibernate.use_identifer_rollback If enabled, generated identifierproperties will be reset to defaultvalues when objects are deleted.

eg. true | false

hibernate.use_sql_comments If turned on, Hibernate will generatecomments inside the SQL, for easierdebugging, defaults to false.

eg. true | false

Page 59: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Propriétés de configuration optionnelles

Hibernate 3.3.1 45

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

Property name Purpose

hibernate.jdbc.fetch_size A non-zero value determinesthe JDBC fetch size (callsStatement.setFetchSize()).

hibernate.jdbc.batch_size A non-zero value enables use ofJDBC2 batch updates by Hibernate.

ex. les valeurs recommandées entre5 et 30

hibernate.jdbc.batch_versioned_data Set this property to true if yourJDBC driver returns correct rowcounts from executeBatch() (it isusually safe to turn this option on).Hibernate will then use batched DMLfor automatically versioned data.Defaults to false.

eg. true | false

hibernate.jdbc.factory_class Select a customorg.hibernate.jdbc.Batcher. Mostapplications will not need thisconfiguration property.

eg. classname.of.BatcherFactory

hibernate.jdbc.use_scrollable_resultsetEnables use of JDBC2 scrollableresultsets by Hibernate. This propertyis only necessary when usinguser supplied JDBC connections,Hibernate uses connection metadataotherwise.

eg. true | false

hibernate.jdbc.use_streams_for_binaryUse streams when writing/readingbinary or serializable types to/fromJDBC. *system-level property*

eg. true | false

hibernate.jdbc.use_get_generated_keysEnable use of JDBC3 PreparedStatement.getGeneratedKeys()to retrieve natively generated keysafter insert. Requires JDBC3+driver and JRE1.4+, set to false ifyour driver has problems with theHibernate identifier generators.By default, tries to determine thedriver capabilities using connectionmetadata.

eg. true|false

hibernate.connection.provider_class The classname of a custom org.hibernate.connection.ConnectionProviderwhich provides JDBC connections toHibernate.

eg. classname.of.ConnectionProvider

hibernate.connection.isolation Set the JDBC transaction isolationlevel. Check java.sql.Connection formeaningful values but note that mostdatabases do not support all isolationlevels and some define additional,non-standard isolations.

eg. 1, 2, 4, 8

hibernate.connection.autocommit Enables autocommit for JDBC pooledconnections (not recommended).

eg. true | false

hibernate.connection.release_mode Specify when Hibernate shouldrelease JDBC connections. Bydefault, a JDBC connection isheld until the session is explicitlyclosed or disconnected. For anapplication server JTA datasource,you should use after_statement toaggressively release connectionsafter every JDBC call. For a non-JTAconnection, it often makes senseto release the connection at theend of each transaction, by usingafter_transaction. auto will chooseafter_statement for the JTA andCMT transaction strategies andafter_transaction for the JDBCtransaction strategy.

eg. auto (default) | on_close |after_transaction | after_statement

Note that this setting onlyaffects Sessions returned fromSessionFactory.openSession.For Sessions obtained throughSessionFactory.getCurrentSession,the CurrentSessionContextimplementation configured for usecontrols the connection release modefor those Sessions. See Section 2.5,« Sessions Contextuelles »

hibernate.connection.<propertyName>Pass the JDBC property<propertyName> toDriverManager.getConnection().

hibernate.jndi.<propertyName> Pass the property <propertyName> tothe JNDI InitialContextFactory.

Page 60: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 3. Configuration

46 Hibernate 3.3.1

Tableau 3.5. Propriétés du Cache d'Hibernate

Property name Purpose

hibernate.cache.provider_class The classname of a customCacheProvider.

eg. classname.of.CacheProvider

hibernate.cache.use_minimal_puts Optimize second-level cacheoperation to minimize writes, at thecost of more frequent reads. Thissetting is most useful for clusteredcaches and, in Hibernate3, is enabledby default for clustered cacheimplementations.

eg. true|false

hibernate.cache.use_query_cache Enable the query cache, individualqueries still have to be set cachable.

eg. true|false

hibernate.cache.use_second_level_cacheMay be used to completely disablethe second level cache, which isenabled by default for classes whichspecify a <cache> mapping.

eg. true|false

hibernate.cache.query_cache_factory The classname of a customQueryCache interface, defaults to thebuilt-in StandardQueryCache.

eg. classname.of.QueryCache

hibernate.cache.region_prefix A prefix to use for second-level cacheregion names.

eg. prefix

hibernate.cache.use_structured_entriesForces Hibernate to store data inthe second-level cache in a morehuman-friendly format.

eg. true|false

Page 61: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Propriétés de configuration optionnelles

Hibernate 3.3.1 47

Tableau 3.6. Propriétés des transactions Hibernate

Property name Purpose

hibernate.transaction.factory_class The classname of aTransactionFactory to use withHibernate Transaction API (defaultsto JDBCTransactionFactory).

eg. classname.of.TransactionFactory

jta.UserTransaction A JNDI name used byJTATransactionFactory to obtainthe JTA UserTransaction from theapplication server.

eg. jndi/composite/name

hibernate.transaction.manager_lookup_classThe classname of aTransactionManagerLookup - requiredwhen JVM-level caching is enabledor when using hilo generator in a JTAenvironment.

eg. classname.of.TransactionManagerLookup

hibernate.transaction.flush_before_completionIf enabled, the session will beautomatically flushed during thebefore completion phase of thetransaction. Built-in and automaticsession context managementis preferred, see Section 2.5,« Sessions Contextuelles ».

eg. true | false

hibernate.transaction.auto_close_sessionIf enabled, the session will beautomatically closed during theafter completion phase of thetransaction. Built-in and utomaticsession context managementis preferred, see Section 2.5,« Sessions Contextuelles ».

eg. true | false

Page 62: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 3. Configuration

48 Hibernate 3.3.1

Tableau 3.7. Propriétés diverses

Property name Purpose

hibernate.current_session_context_classSupply a (custom) strategy for thescoping of the "current" Session.See Section 2.5, « SessionsContextuelles » for more informationabout the built-in strategies.

eg. jta | thread | managed |custom.Class

hibernate.query.factory_class Chooses the HQL parserimplementation.

eg. org.hibernate.hql.ast.ASTQueryTranslatorFactoryor org.hibernate.hql.classic.ClassicQueryTranslatorFactory

hibernate.query.substitutions Mapping from tokens in Hibernatequeries to SQL tokens (tokens mightbe function or literal names, forexample).

eg. hqlLiteral=SQL_LITERAL,hqlFunction=SQLFUNC

hibernate.hbm2ddl.auto Automatically validate or exportschema DDL to the database whenthe SessionFactory is created.With create-drop, the databaseschema will be dropped when theSessionFactory is closed explicitly.

eg. validate | update | create |create-drop

hibernate.cglib.use_reflection_optimizerEnables use of CGLIB instead ofruntime reflection (System-levelproperty). Reflection can sometimesbe useful when troubleshooting,note that Hibernate always requiresCGLIB even if you turn off theoptimizer. You can not set thisproperty in hibernate.cfg.xml.

eg. true | false

Page 63: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Dialectes SQL

Hibernate 3.3.1 49

3.4.1. Dialectes SQL

Vous devriez toujours positionner la propriété hibernate.dialect à lasous-classe de org.hibernate.dialect.Dialect appropriée à votre basede données. Si vous spécifiez un dialecte, Hibernate utilisera des valeursadaptées pour certaines autres propriétés listées ci-dessus, vous évitantl'effort de le faire à la main.

Tableau 3.8. Dialectes SQL d'Hibernate (hibernate.dialect)

SGBD Dialect

DB2 org.hibernate.dialect.DB2Dialect

DB2 AS/400 org.hibernate.dialect.DB2400Dialect

DB2 OS390 org.hibernate.dialect.DB2390Dialect

PostgreSQL org.hibernate.dialect.PostgreSQLDialect

MySQL org.hibernate.dialect.MySQLDialect

MySQL with InnoDB org.hibernate.dialect.MySQLInnoDBDialect

MySQL with MyISAM org.hibernate.dialect.MySQLMyISAMDialect

Oracle (any version) org.hibernate.dialect.OracleDialect

Oracle 9i/10g org.hibernate.dialect.Oracle9Dialect

Sybase org.hibernate.dialect.SybaseDialect

Sybase Anywhere org.hibernate.dialect.SybaseAnywhereDialect

Microsoft SQL Server org.hibernate.dialect.SQLServerDialect

SAP DB org.hibernate.dialect.SAPDBDialect

Informix org.hibernate.dialect.InformixDialect

HypersonicSQL org.hibernate.dialect.HSQLDialect

Ingres org.hibernate.dialect.IngresDialect

Progress org.hibernate.dialect.ProgressDialect

Mckoi SQL org.hibernate.dialect.MckoiDialect

Interbase org.hibernate.dialect.InterbaseDialect

Pointbase org.hibernate.dialect.PointbaseDialect

FrontBase org.hibernate.dialect.FrontbaseDialect

Firebird org.hibernate.dialect.FirebirdDialect

3.4.2. Chargement par Jointure Ouverte

Si votre base de données supporte les outer joins de type ANSI, Oracleou Sybase, le chargement par jointure ouverte devrait améliorer les

Page 64: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 3. Configuration

50 Hibernate 3.3.1

performances en limitant le nombre d'aller-retour avec la base de données(la base de données effectuant donc potentiellement plus de travail).Le chargement par jointure ouverte permet à un graphe entier d'objetsconnectés par une relation plusieurs-à-un, un-à-plusieurs ou un-à-un d'êtrechargé en un seul SELECT SQL.

Le chargement par jointure ouverte peut être désactiver globalement enmettant la propriété hibernate.max_fetch_depth à 0. Une valeur de 1 ou plusactive le chargement par jointure ouverte pour les associatiosn un-à-un etplusieurs-à-un qui ont été mappée avec fetch="join".

Reportez vous à Section 19.1, « Stratégies de chargement » pour plusd'information.

3.4.3. Flux binaires

Oracle limite la taille d'un tableau de byte qui peuvent être passéesà et vers son pilote JDBC. Si vous souhaitez utiliser des instanceslarges de type binary ou serializable, vous devez activer la propriétéhibernate.jdbc.use_streams_for_binary. C'est une fonctionalité de niveausystème uniquement.

3.4.4. Cache de second niveau et cache de requêtes

Les propriétés préfixées par hibernate.cache vous permettent d'utiliser unsystème de cache de second niveau. Ce cache peut avoir une portée dansle processus ou même être utilisable dans un système distribué. Référezvous au chapitre Section 19.2, « Le cache de second niveau » pour plus dedétails.

3.4.5. Substitution dans le langage de requêtage

Vous pouvez définir de nouveaux tokens dans les requêtes Hibernate enutilisant la propriété hibernate.query.substitutions. Par exemple :

hibernate.query.substitutions vrai=1, faux=0

remplacerait les tokens vrai et faux par des entiers dans le SQL généré.

hibernate.query.substitutions toLowercase=LOWER

permettrait de renommer la fonction SQL LOWER en toLowercase

3.4.6. Statistiques Hibernate

Si vous activez hibernate.generate_statistics, Hibernate va fournir uncertains nombre de métriques utiles pour régler les performances d'une

Page 65: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Tracer

Hibernate 3.3.1 51

application qui tourne via SessionFactory.getStatistics(). Hibernatepeut aussi être configuré pour exposer ces statistiques via JMX. Lisez lesJavadoc des interfaces dans le package org.hibernate.stats pour plusd'informations.

3.5. Tracer

Hibernate utilizes Simple Logging Facade for Java [http://www.slf4j.org/](SLF4J) in order to log various system events. SLF4J can direct your loggingoutput to several logging frameworks (NOP, Simple, log4j version 1.2, JDK1.4 logging, JCL or logback) depending on your chosen binding. In order tosetup logging properly you will need slf4j-api.jar in your classpath togetherwith the jar file for your preferred binding - slf4j-log4j12.jar in the case ofLog4J. See the SLF4J documentation [http://www.slf4j.org/manual.html] formore detail. To use Log4j you will also need to place a log4j.properties filein your classpath, an example properties file is distributed with Hibernate inthe src/ directory.

Nous vous recommandons fortement de vous familiariser avec les messagesdes traces d'Hibernate. Beaucoup de soins a été apporté pour donner le plusde détails possible sans les rendre illisibles. C'est un outil essentiel en cas desoucis. Les catégories de trace les plus intéressantes sont les suivantes :

Page 66: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 3. Configuration

52 Hibernate 3.3.1

Tableau 3.9. Catégories de trace d'Hibernate

Catégorie Fonction

org.hibernate.SQL Trace toutes les requêts SQL de type DML (gestiondes données) qui sont exécutées

org.hibernate.type Trace tous les paramètres JDBC

org.hibernate.tool.hbm2ddlTrace toutes les requêts SQL de type DDL (gestion dela structure de la base) qui sont exécutées

org.hibernate.pretty Trace l'état de toutes les entités (20 entités maximum)qui sont associées avec la session hibernate aumoment du flush

org.hibernate.cache Trace toute l'activité du cache de second niveau

org.hibernate.transactionTrace toute l'activité relative aux transactions

org.hibernate.jdbc Trace toute acquisition de ressource JDBC

org.hibernate.hql.ast.ASTTrace l'arbre syntaxique des requêtes HQL et SQLdurant l'analyse syntaxique des requêtes

org.hibernate.secure Trace toutes les demandes d'autorisation JAAS

org.hibernate Trace tout (beaucoupe d'informations, mais très utilepour résoudre les problèmes).

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

3.6. Implémenter une NamingStrategy

L'interface org.hibernate.cfg.NamingStrategy vous permet de spécifier une"stratégie de nommage" des objets et éléments de la base de données.

Vous pouvez fournir des règles pour automatiquement générer lesidentifiants de base de données à partir des identifiants Java, ou transformerune colonne ou table "logique" donnée dans le fichier de mapping en unecolonne ou table "physique". Cette fonctionnalité aide à réduire la verbositéde documents de mapping, en éliminant le bruit répétitif (les préfixes TBL_ parexemple). La stratégie par défaut utilisée par Hibernate est minimale.

Vous pouvez définir une stratégie différente en appelantConfiguration.setNamingStrategy() avant d'ajouter des mappings :

SessionFactory sf = new Configuration()

.setNamingStrategy(ImprovedNamingStrategy.INSTANCE)

.addFile("Item.hbm.xml")

Page 67: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Fichier de configuration XML

Hibernate 3.3.1 53

.addFile("Bid.hbm.xml")

.buildSessionFactory();

net.sf.hibernate.cfg.ImprovedNamingStrategy est une stratégie fournie quipeut être utile comme point de départ de quelques applications.

3.7. Fichier de configuration XML

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

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

<?xml version='1.0' encoding='utf-8'?>

<!DOCTYPE hibernate-configuration PUBLIC

"-//Hibernate/Hibernate Configuration DTD//EN"

"http://hibernate.sourceforge.net/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"/>

Page 68: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 3. Configuration

54 Hibernate 3.3.1

</session-factory>

</hibernate-configuration>

Commme vous pouvez le voir, l'avantage de cette approche estl'externalisation des noms des fichiers de mapping de la configuration. Lefichier hibernate.cfg.xml est également plus pratique quand on commenceà régler le cache d'Hibernate. Notez que vous pouvez choisir entre utiliserhibernate.properties ou hibernate.cfg.xml, les deux sont équivalents,sauf en ce qui concerne les bénéfices de l'utilisation de la syntaxe XMLmentionnés ci-dessus.

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

SessionFactory sf = new

Configuration().configure().buildSessionFactory();

You can pick a different XML configuration file using

SessionFactory sf = new Configuration()

.configure("catdb.cfg.xml")

.buildSessionFactory();

3.8. Intégration à un serveur d'application J2EE

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

• Source de données gérée par le conteneur : Hibernate peut utiliser desconnexions JDBC gérées par le conteneur et fournie par l'intermédiairede JNDI. Souvent, un TransactionManager compatible JTA et unResourceManager s'occupent de la gestion des transactions (CMT). Ils sontparticulièrement prévus pour pouvoir gérer des transactions distribuées surplusieurs sources de données. Vous pouvez bien sûr également définir voslimites de transaction dans votre programme (BMT) ou vous pouvez sinonaussi utiliser l'API optionnelle Transaction d'Hibernate qui vous garantira laportabilité de votre code entre plusieurs serveurs d'application.

• Association JNDI automatique: Hibernate peut associer sa SessionFactoryà JNDI après le démarrage.

• Association de la Session à JTA: La Session Hibernate peut être associéeautomatiquement à une transaction JTA si vous utilisez les EJBs. Vousavez juste à récupérer la SessionFactory depuis JNDI et à récupérer laSession courante. Hibernate s'occupe de vider et fermer la Session lorsque

Page 69: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Configuration de la stratégietransactionnelle

Hibernate 3.3.1 55

le transaction JTA se termine. La démarcation des transactions se fait demanière déclarative dans les descripteurs de déploiement.

• Déploiement JMX :Si vous avez un serveur d'application compatible JMX(JBoss AS par exemple), vous pouvez choisir de déployer Hibernateen temps que MBean géré par le serveur. Cela vous évite de coder laligne de démarrage qui permet de construire la SessionFactory depuis laConfiguration. Le conteneur va démarrer votre HibernateService, et vaidéalement s'occuper des dépendances entre les services (la source dedonnées doit être disponible avant qu'Hibernate ne démarre, etc).

En fonction de votre environnement, vous devrez peut être mettre l'option deconfiguration hibernate.connection.aggressive_release à vrai si le serveurd'application affiche des exceptions de type "connection containment".

3.8.1. Configuration de la stratégie transactionnelle

L'API de la Session Hibernate est indépendante de tout système dedémarcation des transactions qui peut être présent dans votre architecture.Si vous laissez Hibernate utiliser l'API JDBC directement via un pool deconnexion, vous devrez commencer et terminer vos transactions en utilisantl'API JDBC. Si votre application tourne à l'intérieur d'un serveur d'applicationJ2EE, vous voudrez peut être utiliser les transactions gérées par les beans(BMT) et appeller l'API JTA et UserTransaction lorsque cela est nécessaire.

Pour conserver votre code portable entre ces deux environnements (etd'autres éventuels) nous vous recommandons d'utiliser l'API optionnelleTransaction d'Hibernate, qui va encapsuler et masquer le système detransaction sous-jacent. Pour cela, vous devez préciser une classede fabrique d'instances de Transaction en positionnant la propriétéhibernate.transaction.factory_class.

Il existe trois choix standards (fournis) :

net.sf.hibernate.transaction.JDBCTransactionFactory

délègue aux transactions de la base de données (JDBC). Valeur pardéfaut.

org.hibernate.transaction.JTATransactionFactory

délègue à CMT si une transaction existante est sous ce contexte (ex:méthode d'un EJB session), sinon une nouvelle transaction est entaméeet une transaction gérée par le bean est utilisée.

org.hibernate.transaction.CMTTransactionFactory

délègue à aux transactions JTA gérées par le conteneur

Page 70: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 3. Configuration

56 Hibernate 3.3.1

Vous pouvez également définir votre propre stratégie transactionnelle (pourun service de transaction CORBA par exemple).

Certaines fonctionnalités d'Hibernate (i.e. le cache de second niveau,l'association automatique des Session à JTA, etc.) nécessitent l'accèsau TransactionManager JTA dans un environnement "managé". Dans unserveur d'application, vous devez indiquer comment Hibernate peut obtenirune référence vers le TransactionManager, car J2EE ne fournit pas un seulmécanisme standard.

Tableau 3.10. TransactionManagers JTA

Fabrique de Transaction Serveurd'application

org.hibernate.transaction.JBossTransactionManagerLookup JBoss

org.hibernate.transaction.WeblogicTransactionManagerLookupWeblogic

org.hibernate.transaction.WebSphereTransactionManagerLookupWebSphere

org.hibernate.transaction.WebSphereExtendedJTATransactionLookupWebSphere 6

org.hibernate.transaction.OrionTransactionManagerLookup Orion

org.hibernate.transaction.ResinTransactionManagerLookup Resin

org.hibernate.transaction.JOTMTransactionManagerLookup JOTM

org.hibernate.transaction.JOnASTransactionManagerLookup JOnAS

org.hibernate.transaction.JRun4TransactionManagerLookup JRun4

org.hibernate.transaction.BESTransactionManagerLookup Borland ES

3.8.2. SessionFactory associée au JNDI

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

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

Lorsqu'il associe la SessionFactory au JNDI, Hibernate utilisera les valeursde hibernate.jndi.url, hibernate.jndi.class pour instancier un contexte

Page 71: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Association automatique de la Session àJTA

Hibernate 3.3.1 57

d'initialisation. S'ils ne sont pas spécifiés, l'InitialContext par défaut serautilisé.

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

Si vous utilisez SessionFactory JNDI, un EJB ou n'importe quelle autre classepeut obtenir la SessionFactory en utilisant un lookup JNDI.

Nous recommandons que vous liiez la SessionFactory à JNDI dansles environnements managés et que vous utilisiez un singleton staticsi ce n'est pas le cas. Pour isoler votre application de ces détails,nous vous recommandons aussi de masquer le code de lookupactuel pour une SessionFactory dans une classe helper, commeHibernateUtil.getSessionFactory(). Notez qu'une telle classe est aussi unmoyen efficace de démarrer Hibernatevoir chapitre 1.

3.8.3. Association automatique de la Session à JTA

Le moyen le plus simple de gérer les Sessions et transactions estla gestion automatique de session "courante" offerte par Hibernate.Voir détail à Section 2.5, « Sessions Contextuelles ». En utilisant lecontexte de session "jta" session context, s'il n'y a pas de Sessionassociée à la transaction JTA courante, une session sera démarréeet associée à la transaction JTA courante la première fois que vousappelez sessionFactory.getCurrentSession(). Les Sessions obtenue viagetCurrentSession() dans une contexte "jta" seront automatiquementflushées avant la validation de la transaction, fermées une fois la transactioncomplétée, et libéreront les connexions JDBC de manière aggressive aprèschaque statement. Ceci permet aux Sessions d'être gérées par le cycle devie de la transaction JTA à la quelle est sont associées, laissant le code del'utilisateur propre de ce type de gestion. Votre code peut soit utiliser JTA demanière programmatique via UserTransaction, ou (ce qui est recommandépour la portabilité du code) utiliser l'API Transaction API pour marquer leslimites. Si vous exécutez sous un conteneur EJB, la démarcation déclarativedes transactions avec CMT est recommandée.

3.8.4. Déploiement JMX

La ligne cfg.buildSessionFactory() doit toujours être exécutée quelquepart pour avoir une SessionFactory dans JNDI. Vous pouvez faire cela dansun bloc d'initialisation static (comme celui qui se trouve dans la classe

Page 72: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 3. Configuration

58 Hibernate 3.3.1

HibernateUtil) ou vous pouvez déployer Hibernate en temps que servicemanagé.

Hibernate est distribué avec org.hibernate.jmx.HibernateService pour ledéploiement sur un serveur d'application avec le support de JMX commeJBoss AS. Le déploiement et la configuration sont spécifiques à chaquevendeur. 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>

Page 73: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Déploiement JMX

Hibernate 3.3.1 59

</mbean>

</server>

Ce fichier est déployé dans un répertoire META-INF et est packagé dans unfichier JAR avec l'extension .sar (service archive). Vous devez égalementpackager Hibernate, les librairies tierces requises, vos classes persistantescompilées et vos fichiers de mapping dans la même archive. Vos beansentreprise (souvent des EJBs session) peuvent rester dans leur propre fichierJAR mais vous pouvez inclure ce fichier JAR dans le jar principal du servicepour avoir une seule unité déployable à chaud. Vous pouvez consulter ladocumentation de JBoss AS pour plus d'information sur les services JMX etle déploiement des EJBs.

Page 74: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

60 Hibernate 3.3.1

Page 75: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 61

Chapitre 4. Classes persistantesLes classes persistantes sont les classes d'une application qui implémententles entités d'un problème métier (ex. Client et Commande dans uneapplication de commerce électronique). Toutes les instances d'une classepersistante ne sont pas forcément dans l'état persistant - au lieu de cela, uneinstance peut être éphémère (NdT : transient) ou détachée.

Hibernate fonctionne de manière optimale lorsque ces classes suiventquelques règles simples, aussi connues comme le modèle de programmationPlain Old Java Object (POJO). Cependant, aucune de ces règles ne sont desbesoins absolus. En effet, Hibernate3 suppose très peu de choses à proposde la nature de vos objets persistants. Vous pouvez exprimer un modèle dedomaine par d'autres moyens : utiliser des arbres d'instances de Map, parexemple.

4.1. Un exemple simple de POJO

Toute bonne application Java nécessite une classe persistante représentantles félins.

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

Page 76: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 4. Classes persistantes

62 Hibernate 3.3.1

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

}

}

Il y a quatre règles à suivre ici :

Page 77: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Implémenter un constructeur sansargument

Hibernate 3.3.1 63

4.1.1. Implémenter un constructeur sans argument

Cat a un constructeur sans argument. Toutes les classes persistantesdoivent avoir un constructeur par défaut (lequel peut ne pas être public) pourqu'Hibernate puissent les instancier en utilisant Constructor.newInstance().Nous recommandons fortement d'avoir un constructeur par défaut avec aumoins une visibilité paquet pour la génération du proxy à l'exécution dansHibernate.

4.1.2. Fournir une propriété d'indentifiant (optionnel)

Cat possède une propriété appelée id. Cette propriété mappe la valeur dela colonne de clé primaire de la table d'une base de données.La propriétéaurait pu s'appeler complètement autrement, et son type aurait pu êtren'importe quel type primitif, n'importe quel "encapsuleur" de type primitif,java.lang.String ou java.util.Date. (Si votre base de données héritéepossède des clés composites, elles peuvent être mappées en utilisantune classe définie par l'utilisateur et possédant les propriétés associéesaux types de la clé composite - voir la section concernant les identifiantscomposites plus tard).

La propriété d'identifiant est strictement optionnelle. Vous pouver l'oublier etlaisser Hibernate s'occuper des identifiants de l'objet en interne. Toutefois,nous ne le recommandons pas.

En fait, quelques fonctionnalités ne sont disponibles que pour les classesdéclarant un identifiant de propriété :

• Transitive reattachment for detached objects (cascade update or cascademerge) - see Section 10.11, « Persistance transitive »

• Session.saveOrUpdate()

• Session.merge()

Nous recommandons que vous déclariez les propriétés d'identifiant demanière uniforme. Nous recommandons également que vous utilisiez un typenullable (ie. non primitif).

4.1.3. Favoriser les classes non finales (optionnel)

Une fonctionnalité clef d'Hibernate, les proxies, nécessitent que la classepersistente soit non finale ou qu'elle soit l'implémentation d'une interface quidéclare toutes les méthodes publiques.

Vous pouvez persister, grâce à Hibernate, les classes final quin'implémentent pas d'interface, mais vous ne pourrez pas utiliser les proxies

Page 78: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 4. Classes persistantes

64 Hibernate 3.3.1

pour les chargements d'associations paresseuses - ce qui limitera vospossibilités d'ajustement des performances.

Vous devriez aussi éviter de déclarer des méthodes public final surdes classes non-finales. Si vous voulez utiliser une classe avec uneméthode public final, vous devez explicitement désactiver les proxies enparamétrant lazy="false".

4.1.4. Déclarer les accesseurs et mutateurs des attributspersistants (optionnel)

Cat déclare des mutateurs pour toutes ses champs persistants. Beaucoupd'autres solutions de mapping Objet/relationnel persistent directementles variables d'instance. Nous pensons qu'il est bien mieux de fournir uneindirection entre le schéma relationnel et les structures de données internesde la classe. Par défaut, Hibernate persiste les propriétés suivant le styleJavaBean, et reconnaît les noms de méthodes de la forme getFoo, isFoo etsetFoo. Nous pouvons changer pour un accès direct aux champs pour despropriétés particulières, si besoin est.

Les propriétés n'ont pas à être déclarées publiques - Hibernate peut persisterune propriété avec un paire de getter/setter de visibilité par défault, protectedou private.

4.2. Implémenter l'héritage

Une sous-classe doit également suivre la première et la seconde règle. Ellehérite sa propriété d'identifiant de Cat.

package eg;

public class DomesticCat extends Cat {

private String name;

public String getName() {

return name;

}

protected void setName(String name) {

this.name=name;

}

}

4.3. Implémenter equals() et hashCode()

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

Page 79: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Implémenter equals() et hashCode()

Hibernate 3.3.1 65

• avez l'intention de mettre des instances de classes persistantes dansun Set (la manière recommandée pour représenter des associationspluri-valuées) et

• avez l'intention d'utiliser le réattachement d'instances détachées

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

La manière la plus évidente est d'implémenter equals()/hashCode() encomparant la valeur de l'identifiant des deux objets. Si cette valeur estidentique, 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'unseul élément dans le Set). Malheureusement, nous ne pouvons pas utilisercette approche avec des identifiants générés ! Hibernate n'assignerade valeur d'identifiant qu'aux objets qui sont persistants, une instancenouvellement créée n'aura donc pas de valeur d'identifiant ! De plus, si uneinstance est non sauvegardée et actuellement dans un Set, le sauvegarderassignera une valeur d'identifiant à l'objet. Si equals() et hashCode() sontbasées sur la valeur de l'identifiant, le code de hachage devrait changer,rompant le contrat du Set. Regardez sur le site web d'Hibernate pour unediscussion complète de ce problème. Notez que ceci n'est pas un problèmed'Hibernate, mais la sémantique normale de Java pour l'identité d'un objet etl'égalité.

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éthodeequals() 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é candidatenaturelle) :

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;

Page 80: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 4. Classes persistantes

66 Hibernate 3.3.1

}

public int hashCode() {

int result;

result = getMother().hashCode();

result = 29 * result + getLitterId();

return result;

}

}

Notez qu'une clef métier ne doit pas être solide comme une clef primairede base de données (voir Section 11.1.3, « L'identité des objets »). Lespropriétés immuables ou uniques sont généralement de bonnes candidatespour une clef métier.

4.4. Modèles dynamiques

Notez que la fonctionnalités suivantes sont actuellement considérées commeexpérimentales et peuvent changer dans un futur proche.

Les entités persistantes ne doivent pas nécessairement être représentéescomme des classes POJO ou des objets JavaBean à l'exécution. Hibernatesupporte aussi les modèles dynamiques (en utilisant des Maps de Maps àl'exécution) et la représentation des entités comme des arbres DOM4J. Aveccette approche, vous n'écrivez pas de classes persistantes, seulement desfichiers de mapping.

Par défaut, Hibernate fonctionne en mode POJO normal. Vous pouvezparamétrer un mode de représentation d'entité par défaut pour uneSessionFactory particulière en utilisant l'option de configurationdefault_entity_mode (voir Tableau 3.3, « Propriétés de configurationd'Hibernate »).

Les exemples suivants démontrent la représentation utilisant des Maps.D'abord, dans le fichier de mapping, 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"

Page 81: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Modèles dynamiques

Hibernate 3.3.1 67

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 nomsde 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 laSessionFactory, nous pouvons lors de l'exécution fonctionner avec des Mapsde Maps :

Session s = openSession();

Transaction tx = s.beginTransaction();

Session s = openSession();

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

Page 82: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 4. Classes persistantes

68 Hibernate 3.3.1

Les avantages d'un mapping dynamique sont un gain de temps pour leprototypage 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 aurezplus d'exceptions à gérer lors de l'exécution. Grâce au mapping d'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 domaineplus tard.

Les modes de représentation d'une entité peut aussi être configuré parSession :

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 sefait sur l'API Session, pas SessionFactory. De cette manière, la nouvelleSession partage les connexions JDBC, transactions et autres informationsde contexte sous-jacentes. Cela signifie que vous n'avez pas à appelerflush() et close() sur la Session secondaire, et laissez aussi la gestion de latransaction et de la connexion à l'unité de travail primaire.

Plus d'informations à propos de la représentation XML peuvent être trouvéesdans Chapitre 18, Mapping XML.

4.5. Tuplizers

org.hibernate.tuple.Tuplizer, et ses sous-interfaces, sont responsablesde la gestion d'une représentation particulière d'un morceau de données,en fonction du org.hibernate.EntityMode de réprésentation. Si un morceaudonné de données est pensé comme une structure de données, alorsun tuplizer est la chose qui sait comment créer une telle structure dedonnées, comment extraire des valeurs et injecter des valeurs dans unetelle structure de données. Par exemple, pour le mode d'entité POJO,le tuplizer correspondant sait comment créer le POJO à travers sonconstructeur et comment accéder aux propriétés du POJO utilisant lesaccesseurs de la propriété définie. Il y a deux types de Tuplizers hautniveau, représenté par les interfaces org.hibernate.tuple.EntityTuplizer

Page 83: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Tuplizers

Hibernate 3.3.1 69

et org.hibernate.tuple.ComponentTuplizer. Les EntityTuplizers sontresponsables de la gestion des contrats mentionnés ci-dessus pour lesentités, alors que les ComponentTuplizers s'occupent des composants.

Les utilisateurs peuvent aussi brancher leurs propres tuplizers. Peut-êtrevous est-il nécessaire qu'une implémentation de java.util.Map autre quejava.util.HashMap soit utilisée dans le mode d'entité dynamic-map ; oupeut-être avez-vous besoin de définir une statégie de génération de proxydifférente de celle utilisée par défaut. Les deux devraient être effectuéesen définissant une implémentation de tuplizer utilisateur. Les définitions detuplizers sont attachées au mapping de l'entité ou du composant qu'ils sontcensés gérer. Retour à l'exemple de notre entité utilisateur :

<hibernate-mapping>

<class entity-name="Customer">

<!--

Override the dynamic-map entity-mode

tuplizer for the customer entity

-->

<tuplizer entity-mode="dynamic-map"

class="CustomMapTuplizerImpl"/>

<id name="id" type="long" column="ID">

<generator class="sequence"/>

</id>

<!-- other properties -->

...

</class>

</hibernate-mapping>

public class CustomMapTuplizerImpl

extends org.hibernate.tuple.entity.DynamicMapEntityTuplizer

{

// override the buildInstantiator() method to plug in our custom

map...

protected final Instantiator buildInstantiator(

org.hibernate.mapping.PersistentClass mappingInfo) {

return new CustomMapInstantiator( mappingInfo );

}

private static final class CustomMapInstantiator

extends org.hibernate.tuple.DynamicMapInstantitor {

// override the generateMap() method to return our custom

map...

protected final Map generateMap() {

return new CustomMap();

}

}

}

Page 84: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 4. Classes persistantes

70 Hibernate 3.3.1

4.6. Extentsions

TODO: Document user-extension framework in the property and proxypackages

Page 85: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 71

Chapitre 5. Mapping O/R basique

5.1. Déclaration de Mapping

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

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

Démarrons avec un exemple de mapping :

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="eg">

<class name="Cat"

table="cats"

discriminator-value="C">

<id name="id">

<generator class="native"/>

</id>

<discriminator column="subclass"

type="character"/>

<property name="weight"/>

<property name="birthdate"

type="date"

not-null="true"

update="false"/>

<property name="color"

type="eg.types.ColorUserType"

not-null="true"

update="false"/>

<property name="sex"

not-null="true"

update="false"/>

Page 86: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

72 Hibernate 3.3.1

<property name="litterId"

column="litterId"

update="false"/>

<many-to-one name="mother"

column="mother_id"

update="false"/>

<set name="kittens"

inverse="true"

order-by="litter_id">

<key column="mother_id"/>

<one-to-many class="Cat"/>

</set>

<subclass name="DomesticCat"

discriminator-value="D">

<property name="name"

type="string"/>

</subclass>

</class>

<class name="Dog">

<!-- mapping for Dog could go here -->

</class>

</hibernate-mapping>

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

5.1.1. Doctype

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

Page 87: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

hibernate-mapping

Hibernate 3.3.1 73

5.1.1.1. EntityResolver

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

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

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

An example of utilizing user namespacing:

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"

[

<!ENTITY types SYSTEM "classpath://your/domain/types.xml">

]>

<hibernate-mapping package="your.domain">

<class name="MyEntity">

<id name="id" type="my-custom-id-type">

...

</id>

<class>

&types;

</hibernate-mapping>

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

5.1.2. hibernate-mapping

Cet élément a plusieurs attributs optionnels. Les attributs schema et catalogindiquent que les tables référencées par ce mapping appartiennent auschéma nommé et/ou au catalogue. S'ils sont spécifiés, les noms detables seront qualifiés par les noms de schéma et catalogue. L'attributdefault-cascade indique quel type de cascade sera utlisé par défaut pour

Page 88: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

74 Hibernate 3.3.1

les propriétés et collections qui ne précisent pas l'attribut cascade. L'attributauto-import nous permet d'utiliser par défaut des noms de classes nonqualifiés dans le langage de requête.

<hibernate-mapping

schema="schemaName" (1)

catalog="catalogName" (2)

default-cascade="cascade_style" (3)

default-access="field|property|ClassName" (4)

default-lazy="true|false" (5)

auto-import="true|false" (6)

package="package.name" (7)

/>

(1) schema (optionnel) : Le nom d'un schéma de base de données.(2) catalog (optionnel) : Le nom d'un catalogue de base de données.(3) default-cascade (optionnel - par défaut vaut : none) : Un type de

cascade par défaut.(4) default-access (optionnel - par défaut vaut : property) : Comment

hibernate accèdera aux propriétés. On peut aussi redéfinir sa propreimplémentation de PropertyAccessor.

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

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

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

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

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

5.1.3. class

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

<class

Page 89: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

class

Hibernate 3.3.1 75

name="ClassName" (1)

table="tableName" (2)

discriminator-value="discriminator_value" (3)

mutable="true|false" (4)

schema="owner" (5)

catalog="catalog" (6)

proxy="ProxyInterface" (7)

dynamic-update="true|false" (8)

dynamic-insert="true|false" (9)

select-before-update="true|false" (10)

polymorphism="implicit|explicit" (11)

where="arbitrary sql where condition" (12)

persister="PersisterClass" (13)

batch-size="N" (14)

optimistic-lock="none|version|dirty|all" (15)

lazy="true|false" (16)

entity-name="EntityName" (17)

check="arbitrary sql check condition" (18)

rowid="rowid" (19)

subselect="SQL expression" (20)

abstract="true|false" (21)

node="element-name"

/>

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

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

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

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

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

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

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

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

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

Page 90: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

76 Hibernate 3.3.1

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

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

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

(13) persister (optionnel) : Spécifie un ClassPersister particulier.(14) batch-size (optionnel, par défaut = 1) : spécifie une taille de batch

pour remplir les instances de cette classe par identifiant en une seulerequête.

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

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

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

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

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

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

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

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

Page 91: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

class

Hibernate 3.3.1 77

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

L'attribut optionnnel proxy permet les intialisations différées des instancespersistantes de la classe. Hibernate retournera initialement des proxiesCGLIB qui implémentent l'interface nommée. Le véritable objet persistantne sera chargé que lorsque une méthode du proxy sera appelée. Voirplus bas le paragraphe abordant les proxies et le chargement différé (lazyinitialization).

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

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

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

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

Page 92: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

78 Hibernate 3.3.1

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

• version vérifie les colonnes version/timestamp

• all vérifie toutes les colonnes

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

• none pas de verrouillage optimiste

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

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

<class name="Summary">

<subselect>

select item.name, max(bid.amount), count(*)

from item

join bid on bid.item_id = item.id

group by item.name

</subselect>

<synchronize table="item"/>

<synchronize table="bid"/>

<id name="name"/>

...

</class>

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

5.1.4. id

Les classes mappées doivent déclarer la clef primaire de la table en base dedonnées. La plupart des classes auront aussi une propriété de type javabean

Page 93: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

id

Hibernate 3.3.1 79

présentant l'identifiant unique d'une instance. L'élément <id> sert à définir lemapping entre cette propriété et la clef primaire en base.

<id

name="propertyName"

(1)

type="typename"

(2)

column="column_name"

(3)

unsaved-value="null|any|none|undefined|id_value"

(4)

access="field|property|ClassName">

(5)

node="element-name|@attribute-name|element/@attribute|."

<generator class="generatorClass"/>

</id>

(1) name (optionnel) : Nom de la propriété qui sert d'identifiant.(2) type (optionnel) : Nom indiquant le type Hibernate.(3) column (optionnel - le nom de la propriété est pris par défaut) : Nom de

la clef primaire.(4) unsaved-value (optionnel - par défaut une valeur "bien choisie") :

Une valeur de la propriété d'identifiant qui indique que l'instance estnouvellement instanciée (non sauvegardée), et qui la distingue desinstances transients qui ont été sauvegardées ou chargées dans unesession précédente.

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

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

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

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

5.1.4.1. Generator

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

Page 94: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

80 Hibernate 3.3.1

<id name="id" type="long" column="cat_id">

<generator class="org.hibernate.id.TableHiLoGenerator">

<param name="table">uid_table</param>

<param name="column">next_hi_value_column</param>

</generator>

</id>

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

increment

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

identity

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

sequence

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

hilo

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

seqhilo

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

uuid

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

guid

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

Page 95: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

id

Hibernate 3.3.1 81

native

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

assigned

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

select

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

foreign

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

sequence-identity

a specialized sequence generation strategy which utilizes a databasesequence for the actual value generation, but combines this with JDBC3getGeneratedKeys to actually return the generated identifier value aspart of the insert statement execution. This strategy is only known to besupported on Oracle 10g drivers targetted for JDK 1.4. Note commentson these insert statements are disabled due to a bug in the Oracledrivers.

5.1.4.2. algorithme Hi/lo

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

<id name="id" type="long" column="cat_id">

<generator class="hilo">

<param name="table">hi_value</param>

<param name="column">next_value</param>

<param name="max_lo">100</param>

</generator>

</id>

<id name="id" type="long" column="cat_id">

<generator class="seqhilo">

<param name="sequence">hi_value</param>

<param name="max_lo">100</param>

Page 96: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

82 Hibernate 3.3.1

</generator>

</id>

Malheureusement, vous ne pouvez pas utilisez hilo quand vousapportez votre propre Connection à Hibernate. Quand Hibernateutilise une datasource du serveur d'application pour obtenir desconnexions inscrites avec JTA, vous devez correctement configurerhibernate.transaction.manager_lookup_class.

5.1.4.3. UUID algorithm

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

5.1.4.4. Colonnes identifiantes et séquences

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

<id name="id" type="long" column="person_id">

<generator class="sequence">

<param name="sequence">person_id_sequence</param>

</generator>

</id>

<id name="id" type="long" column="person_id" unsaved-value="0">

<generator class="identity"/>

</id>

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

5.1.4.5. Identifiants assignés

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

Page 97: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Enhanced identifier generators

Hibernate 3.3.1 83

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

5.1.4.6. Clefs primaires assignées par trigger

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

<id name="id" type="long" column="person_id">

<generator class="select">

<param name="key">socialSecurityNumber</param>

</generator>

</id>

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

5.1.5. Enhanced identifier generators

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

The first of these new generators isorg.hibernate.id.enhanced.SequenceStyleGenerator which is intendedfirstly as a replacement for the sequence generator and secondly as abetter portability generator than native (because native (generally)chooses between identity and sequence which have largely differentsemantics which can cause subtle isssues in applications eyeing portability).org.hibernate.id.enhanced.SequenceStyleGenerator however achievesportability in a different manner. It chooses between using a table or asequence in the database to store its incrementing values depending onthe capabilities of the dialect being used. The difference between this andnative is that table-based and sequence-based storage have the sameexact semantic (in fact sequences are exactly what Hibernate tries toemmulate with its table-based generators). This generator has a number ofconfiguration parameters:

Page 98: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

84 Hibernate 3.3.1

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

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

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

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

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

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

The second of these new generators isorg.hibernate.id.enhanced.TableGenerator which is intended firstly as areplacement for the table generator (although it actually functions muchmore like org.hibernate.id.MultipleHiLoPerTableGenerator) and secondlyas a re-implementation of org.hibernate.id.MultipleHiLoPerTableGeneratorutilizing the notion of pluggable optimiziers. Essentially this generatordefines a table capable of holding a number of different increment valuessimultaneously by using multiple distinctly keyed rows. This generator has anumber of configuration parameters:

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

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

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

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

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

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

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

Page 99: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Identifier generator optimization

Hibernate 3.3.1 85

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

5.1.6. Identifier generator optimization

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

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

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

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

5.1.7. composite-id

<composite-id

name="propertyName"

class="ClassName"

mapped="true|false"

access="field|property|ClassName">

node="element-name|."

<key-property name="propertyName" type="typename"

column="column_name"/>

<key-many-to-one name="propertyName class="ClassName"

column="column_name"/>

......

</composite-id>

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

Page 100: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

86 Hibernate 3.3.1

<composite-id>

<key-property name="medicareNumber"/>

<key-property name="dependent"/>

</composite-id>

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

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

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

<composite-id class="MedicareId" mapped="true">

<key-property name="medicareNumber"/>

<key-property name="dependent"/>

</composite-id>

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

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

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

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

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

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

Page 101: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

discriminator

Hibernate 3.3.1 87

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

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

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

5.1.8. discriminator

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

<discriminator

column="discriminator_column" (1)

type="discriminator_type" (2)

force="true|false" (3)

insert="true|false" (4)

formula="arbitrary sql expression" (5)

/>

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

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

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

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

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

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

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

Page 102: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

88 Hibernate 3.3.1

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

<discriminator

formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1

end"

type="integer"/>

5.1.9. version (optionnel)

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

<version

column="version_column"

(1)

name="propertyName"

(2)

type="typename"

(3)

access="field|property|ClassName"

(4)

unsaved-value="null|negative|undefined"

(5)

generated="never|always"

(6)

insert="true|false"

(7)

node="element-name|@attribute-name|element/@attribute|."

/>

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

(2) name : Le nom d'un attribut de la classe persistante.(3) type (optionnel - par défaut à integer) : Le type du numéro de version.(4) access (optional - defaults to property): The strategy Hibernate should

use for accessing the property value.(5) unsaved-value (optionnel - par défaut à undefined) : Une valeur de

la propriété d'identifiant qui indique que l'instance est nouvellementinstanciée (non sauvegardée), et qui la distingue des instancesdétachées qui ont été sauvegardées ou chargées dans une sessionprécédente (undefined indique que la valeur de l'atribut identifiantdevrait être utilisé).

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

Page 103: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

timestamp (optionnel)

Hibernate 3.3.1 89

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

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

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

5.1.10. timestamp (optionnel)

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

<timestamp

column="timestamp_column"

(1)

name="propertyName"

(2)

access="field|property|ClassName"

(3)

unsaved-value="null|undefined"

(4)

source="vm|db"

(5)

generated="never|always"

(6)

node="element-name|@attribute-name|element/@attribute|."

/>

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

(2) name : Le nom d'une propriété au sens JavaBean de type Date ouTimestamp de la classe persistante.

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

(4) unsaved-value (optionnel - par défaut à null) : Propriété dont la valeurest un numéro de version qui indique que l'instance est nouvellement

Page 104: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

90 Hibernate 3.3.1

instanciée (non sauvegardée), et qui la distingue des instancesdétachées qui ont été sauvegardées ou chargées dans une sessionprécédente (undefined indique que la valeur de l'attribut identifiantdevrait être utilisée).

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

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

Notez que <timestamp> est équivalent à <version type="timestamp">.

5.1.11. property

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

<property

name="propertyName"

(1)

column="column_name"

(2)

type="typename"

(3)

update="true|false"

(4)

insert="true|false"

(4)

formula="arbitrary SQL expression"

(5)

access="field|property|ClassName"

(6)

lazy="true|false"

(7)

unique="true|false"

(8)

not-null="true|false"

(9)

optimistic-lock="true|false"

(10)

generated="never|insert|always"

(11)

node="element-name|@attribute-name|element/@attribute|."

Page 105: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

property

Hibernate 3.3.1 91

index="index_name"

unique_key="unique_key_id"

length="L"

precision="P"

scale="S"

/>

(1) name : nom de la propriété, avec une lettre initiale en minuscule.(2) column (optionnel - par défaut au nom de la propriété) : le nom

de la colonne mappée. Cela peut aussi être indiqué dans le(s)sous-élément(s) <column>.

(3) type (optionnel) : nom indiquant le type Hibernate.(4) update, insert (optionnel - par défaut à true) : indique que les colonnes

mappées devraient être incluses dans des UPDATE SQL et/ou des INSERT.Mettre les deux à false empêche la propagation en base de données(utile si vous savez qu'un trigger affectera la valeur à la colonne).

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

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

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

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

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

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

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

typename peut être:

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

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

java.sql.Clob).

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

Page 106: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

92 Hibernate 3.3.1

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

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

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

Une fonctionnalité particulièrement intéressante est les propriétés dérivées.Ces propriétés sont par définition en lecture seule, la valeur de la propriétéest calculée au chargement. Le calcul est déclaré comme une expressionSQL, qui se traduit par une sous-requête SELECT dans la requête SQL quicharge 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 enne déclarant pas un alias sur une colonne particulière (customerId dansl'exemple donné). Notez aussi que vous pouvez utiliser le sous-élément demapping <formula> plutôt que d'utiliser l'attribut si vous le souhaitez.

5.1.12. many-to-one

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

<many-to-one

name="propertyName"

(1)

Page 107: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

many-to-one

Hibernate 3.3.1 93

column="column_name"

(2)

class="ClassName"

(3)

cascade="cascade_style"

(4)

fetch="join|select"

(5)

update="true|false"

(6)

insert="true|false"

(6)

property-ref="propertyNameFromAssociatedClass"

(7)

access="field|property|ClassName"

(8)

unique="true|false"

(9)

not-null="true|false"

(10)

optimistic-lock="true|false"

(11)

lazy="proxy|no-proxy|false"

(12)

not-found="ignore|exception"

(13)

entity-name="EntityName"

(14)

formula="arbitrary SQL expression"

(15)

node="element-name|@attribute-name|element/@attribute|."

embed-xml="true|false"

index="index_name"

unique_key="unique_key_id"

foreign-key="foreign_key_name"

/>

(1) name: The name of the property.(2) column (optional): The name of the foreign key column. This may also be

specified by nested <column> element(s).(3) class (optional - defaults to the property type determined by reflection):

The name of the associated class.(4) cascade (optionnel) : Indique quelles opérations doivent être propagées

de l'objet père vers les objets associés.(5) fetch (optional - defaults to select): Chooses between outer-join

fetching or sequential select fetching.(6) update, insert (optionnel - par défaut à true) : indique que les colonnes

mappées devraient être incluses dans des UPDATE SQL et/ou des INSERT.Mettre les deux à false empêche la propagation en base de données(utile si vous savez qu'un trigger affectera la valeur à la colonne).

Page 108: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

94 Hibernate 3.3.1

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

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

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

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

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

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

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

(14) entity-name (optional): The entity name of the associated class.(15) formula (optionnel) : une expression SQL qui définit la valeur pour une

clé étrangère calculée.

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

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

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

Page 109: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Une association one-to-one vers uneautre classe persistante est déclarée avec

l'élément one-to-one.

Hibernate 3.3.1 95

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

<property name="serialNumber" unique="true" type="string"

column="SERIAL_NUMBER"/>

Ainsi le mapping pour OrderItem peut utiliser :

<many-to-one name="product" property-ref="serialNumber"

column="PRODUCT_SERIAL_NUMBER"/>

bien que ce ne soit certainement pas encouragé.

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

one-to-one

<many-to-one name="owner" property-ref="identity.ssn"

column="OWNER_SSN"/>

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

name : Le nom de la propriété.

<one-to-one

name="propertyName"

(1)

class="ClassName"

(2)

cascade="cascade_style"

(3)

constrained="true|false"

(4)

fetch="join|select"

(5)

property-ref="propertyNameFromAssociatedClass"

(6)

access="field|property|ClassName"

(7)

formula="any SQL expression"

(8)

Page 110: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

96 Hibernate 3.3.1

lazy="proxy|no-proxy|false"

(9)

entity-name="EntityName"

(10)

node="element-name|@attribute-name|element/@attribute|."

embed-xml="true|false"

foreign-key="foreign_key_name"

/>

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

The name of the associated class.(3) constrained (optionnel) : Indique qu'une contrainte de clef étrangère

sur la clef primaire de la table mappée référence la table de la classeassociée. Cette option affecte l'ordre dans lequel chaque save() etchaque delete() sont cascadés et détermine si l'association peut utiliserun proxy (aussi utilisé par l'outil d'export de schéma).

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

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

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

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

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

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

associations par clef primaire

• association par clef étrangère unique

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

Page 111: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Une association one-to-one vers uneautre classe persistante est déclarée avec

l'élément one-to-one.

Hibernate 3.3.1 97

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

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

<one-to-one name="person" class="Person"/>

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

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

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

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

<generator class="foreign">

<param name="property">employee</param>

</generator>

</id>

...

<one-to-one name="employee"

class="Employee"

constrained="true"/>

</class>

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

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

<many-to-one name="person" class="Person" column="PERSON_ID"

unique="true"/>

natural-id

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

Page 112: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

98 Hibernate 3.3.1

5.1.14. Bien que nous recommandions l'utilisation declé primaire générée, vous devriez toujours essayerd'identifier des clé métier (naturelles) pour toutes vosentités. Une clé naturelle est une propriété ou unecombinaison de propriétés uniques et non nulles. Sielle est aussi immuable, c'est encore mieux. Mappezles propriétés de la clé naturelle dans l'élément<natural-id>. Hibernate générera la clé unique nécessaireet les contraintes de non-nullité, et votre mappings'auto-documentera.

<natural-id mutable="true|false"/>

<property ... />

<many-to-one ... />

......

</natural-id>

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

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

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

• component, dynamic-component

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

name : Nom de la propriété

<component

name="propertyName" (1)

class="className" (2)

insert="true|false" (3)

update="true|false" (4)

Page 113: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

L'élément <component> mappe lespropriétés d'un objet fils aux colonnes

d'une classe parente. Les composantspeuvent en retour déclarer leurs propres

propriétés, composants ou collections. Voir"Components" plus bas.

Hibernate 3.3.1 99

access="field|property|ClassName" (5)

lazy="true|false" (6)

optimistic-lock="true|false" (7)

unique="true|false" (8)

node="element-name|."

>

<property ...../>

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

........

</component>

(1) name: The name of the property.(2) insert : Est ce que les colonnes mappées apparaissent dans les

INSERTs ?(3) insert: Do the mapped columns appear in SQL INSERTs?(4) update: Do the mapped columns appear in SQL UPDATEs?(5) access (optional - defaults to property): The strategy Hibernate should

use for accessing the property value.(6) optimistic-lock (optionnel - par défaut à true) : Indique que les mises

à jour sur ce composant nécessitent ou non l'acquisition d'un verrouoptimiste. En d'autres termes, cela détermine si une incrémentation deversion doit avoir lieu quand la propriété est marquée obsolète (dirty).

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

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

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

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

properties

Page 114: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

100 Hibernate 3.3.1

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

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

<properties

name="logicalName" (1)

insert="true|false" (2)

update="true|false" (3)

optimistic-lock="true|false" (4)

unique="true|false" (5)

>

<property ...../>

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

........

</properties>

(1) insert : Est-ce que les colonnes mappées apparaissent dans lesINSERTs ?

(2) insert: Do the mapped columns appear in SQL INSERTs?(3) update: Do the mapped columns appear in SQL UPDATEs?(4) unique (optionnel - par défaut à false) : Indique qu'une contrainte

d'unicité existe sur toutes les colonnes mappées de ce composant.(5) unique (optional - defaults to false): Specifies that a unique constraint

exists upon all mapped columns of the component.

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

<class name="Person">

<id name="personNumber"/>

...

<properties name="name"

unique="true" update="false">

<property name="firstName"/>

<property name="initial"/>

<property name="lastName"/>

</properties>

</class>

Page 115: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Pour finir, la persistance polymorphiquenécessite la déclaration de chaque

sous-classe de la classe persistante debase. pour la stratégie de mapping de

type table-per-class-hierarchy, on utilise ladéclaration <subclass>.

Hibernate 3.3.1 101

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

<many-to-one name="person"

class="Person" property-ref="name">

<column name="firstName"/>

<column name="initial"/>

<column name="lastName"/>

</many-to-one>

subclass

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

name : Le nom complet de la sous-classe.

<subclass

name="ClassName" (1)

discriminator-value="discriminator_value" (2)

proxy="ProxyInterface" (3)

lazy="true|false" (4)

dynamic-update="true|false"

dynamic-insert="true|false"

entity-name="EntityName"

node="element-name"

extends="SuperclassName">

<property .... />

.....

</subclass>

(1) name: The fully qualified class name of the subclass.(2) proxy (optionnel) : Indique une classe ou interface à utiliser pour les

chargements à la demande des proxies (lazy).(3) proxy (optional): Specifies a class or interface to use for lazy initializing

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

lazy fetching.

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

Page 116: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

102 Hibernate 3.3.1

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

5.1.18. joined-subclass

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

<joined-subclass

name="ClassName" (1)

table="tablename" (2)

proxy="ProxyInterface" (3)

lazy="true|false" (4)

dynamic-update="true|false"

dynamic-insert="true|false"

schema="schema"

catalog="catalog"

extends="SuperclassName"

persister="ClassName"

subselect="SQL expression"

entity-name="EntityName"

node="element-name">

<key .... >

<property .... />

.....

</joined-subclass>

(1) name: The fully qualified class name of the subclass.(2) table: The name of the subclass table.(3) proxy (optional): Specifies a class or interface to use for lazy initializing

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

lazy fetching.

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

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

Page 117: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

union-subclass

Hibernate 3.3.1 103

<hibernate-mapping package="eg">

<class name="Cat" table="CATS">

<id name="id" column="uid" type="long">

<generator class="hilo"/>

</id>

<property name="birthdate" type="date"/>

<property name="color" not-null="true"/>

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

<property name="weight"/>

<many-to-one name="mate"/>

<set name="kittens">

<key column="MOTHER"/>

<one-to-many class="Cat"/>

</set>

<joined-subclass name="DomesticCat"

table="DOMESTIC_CATS">

<key column="CAT"/>

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

</joined-subclass>

</class>

<class name="eg.Dog">

<!-- mapping for Dog could go here -->

</class>

</hibernate-mapping>

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

5.1.19. union-subclass

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

<union-subclass

name="ClassName" (1)

table="tablename" (2)

proxy="ProxyInterface" (3)

lazy="true|false" (4)

dynamic-update="true|false"

dynamic-insert="true|false"

schema="schema"

Page 118: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

104 Hibernate 3.3.1

catalog="catalog"

extends="SuperclassName"

abstract="true|false"

persister="ClassName"

subselect="SQL expression"

entity-name="EntityName"

node="element-name">

<property .... />

.....

</union-subclass>

(1) name: The fully qualified class name of the subclass.(2) table: The name of the subclass table.(3) proxy (optional): Specifies a class or interface to use for lazy initializing

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

lazy fetching.

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

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

5.1.20. join

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

<join

table="tablename" (1)

schema="owner" (2)

catalog="catalog" (3)

fetch="join|select" (4)

inverse="true|false" (5)

optional="true|false"> (6)

<key ... />

<property ... />

...

</join>

(1) table : Le nom de la table jointe.(2) schema (optional): Override the schema name specified by the root

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

<hibernate-mapping> element.

Page 119: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

key

Hibernate 3.3.1 105

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

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

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

Par exemple, les informations d'adresse pour une personne peuvent êtremappées vers une table séparée (tout en préservant des sémantiques detype 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 dedonnées hérités d'anciens systèmes (legacy), nous recommandons d'utilisermoins de tables que de classes et un modèle de domaine à granularité fine.Cependant, c'est utile pour passer d'une stratégie de mapping d'héritage àune autre dans une hiérarchie simple ainsi qu'il est expliqué plus tard.

5.1.21. key

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

Page 120: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

106 Hibernate 3.3.1

<key

column="columnname" (1)

on-delete="noaction|cascade" (2)

property-ref="propertyName" (3)

not-null="true|false" (4)

update="true|false" (5)

unique="true|false" (6)

/>

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

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

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

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

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

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

Nous recommandons pour les systèmes où les suppressions doivent êtreperformantes de définir toutes les clefs on-delete="cascade", ainsi Hibernateutilisera 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éesversionnées.

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

5.1.22. éléments column et formula

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

<column

name="column_name"

Page 121: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

import

Hibernate 3.3.1 107

length="N"

precision="N"

scale="N"

not-null="true|false"

unique="true|false"

unique-key="multicolumn_unique_key_name"

index="index_name"

sql-type="sql_type_name"

check="SQL expression"

default="SQL expression"/>

<formula>SQL expression</formula>

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

<many-to-one name="homeAddress" class="Address"

insert="false" update="false">

<column name="person_id" not-null="true" length="10"/>

<formula>'MAILING'</formula>

</many-to-one>

5.1.23. import

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

<import class="java.lang.Object" rename="Universe"/>

<import

class="ClassName" (1)

rename="ShortName" (2)

/>

(1) class : Nom Java complet de la classe.(2) rename (optionnel - par défaut vaut le nom de la classe Java (sans

package)) : Nom pouvant être utilisé dans le langage de requête.

5.1.24. any

Il existe encore un type de mapping de propriété. L'élément de mapping<any> définit une association polymorphique vers des classes de tables

Page 122: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

108 Hibernate 3.3.1

multiples. Ce type de mapping requiert toujours plus d'une colonne. Lapremière colonne contient le type de l'entité associée. Les colonnesrestantes contiennent l'identifiant. il est impossible de spécifier une contraintede clef étrangère pour ce type d'association, donc ce n'est certainementpas considéré comme le moyen habituel de mapper des associations(polymorphiques). Vous devriez utiliser cela uniquement dans des casparticuliers (par exemple des logs d'audit, des données de sessionutilisateur, etc...).

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

<any name="being" id-type="long" meta-type="string">

<meta-value value="TBL_ANIMAL" class="Animal"/>

<meta-value value="TBL_HUMAN" class="Human"/>

<meta-value value="TBL_ALIEN" class="Alien"/>

<column name="table_name"/>

<column name="id"/>

</any>

<any

name="propertyName" (1)

id-type="idtypename" (2)

meta-type="metatypename" (3)

cascade="cascade_style" (4)

access="field|property|ClassName" (5)

optimistic-lock="true|false" (6)

>

<meta-value ... />

<meta-value ... />

.....

<column .... />

<column .... />

.....

</any>

(1) name : le nom de la propriété.(2) id-type : le type identifiant.(3) meta-type (optionnel - par défaut à string) : Tout type permis pour un

mapping par discriminateur.(4) cascade (optionnel - par défaut à none) : le style de cascade.(5) access (optional - defaults to property): The strategy Hibernate should

use for accessing the property value.(6) optimistic-lock (optionnel - par défaut à true) : Indique que les mises

à jour sur cette propriété nécessitent ou non l'acquisition d'un verrou

Page 123: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate Types

Hibernate 3.3.1 109

optimiste. En d'autres termes, définit si un incrément de version doitavoir lieu quand cette propriété est marquée dirty.

5.2. Hibernate Types

5.2.1. Entités et valeurs

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

Une entité existe indépendamment de tout autre objet possédant uneréférence vers l'entité. Comparez cela avec le modèle Java habituel où unobjet 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 (saufdans les cas où sauvegardes et suppressions sont cascadées d'une entitémère vers ses enfants). C'est différent du modèle ODMG de persistancepar atteignabilité - et correspond mieux à la façon dont les objets sonthabituellement utilisés dans des grands systèmes. Les entités permettent lesré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éset instances de types valeurs. Ces valeurs sont des types primitifs, descollections (et non le contenu d'une collection), des composants de certainsobjets immuables. Contrairement aux entités, les valeurs (et en particulierles collections et composants) sont persistés par atteignabiliité. Comme lesvaleurs (et types primitifs) sont persistés et supprimés avec l'entité qui lescontient, ils ne peuvent pas posséder leurs propres versions. Les valeursn'ont pas d'identité indépendantes, ainsi elles ne peuvent pas être partagéespar deux entités ou collections.

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

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

Page 124: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

110 Hibernate 3.3.1

Le défi est de mapper les type Javas (et la définition des développeurs desentité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 lesentités nous utilisons <class>, <subclass> et ainsi de suite. Pour les typesvaleurs nous utilisons <property>, <component>, etc., habituellement avecun attribut type. La valeur de cet attribut est le nom d'un type de mappingHibernate. Hibernate propose de base de nombreux mappings (pour lestypes de valeurs standards du JDK). Vous pouvez écrire vos propres typesde mappings et implémenter aussi vos propres stratégies de conversion,nous le verrons plus tard.

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

5.2.2. Basic value types

The built-in basic mapping types may be roughly categorized into

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

true_false

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

string

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

date, time, timestamp

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

calendar, calendar_date

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

big_decimal, big_integer

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

locale, timezone, currency

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

Page 125: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Types de valeur définis par l'utilisateur

Hibernate 3.3.1 111

class

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

binary

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

text

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

serializable

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

clob, blob

Mappings de type pour les classes JDBC java.sql.Clob andjava.sql.Blob. Ces types peuvent ne pas convenir pour certainesapplications car un objet blob ou clob peut ne pas être réutilisable endehors d'une transaction (de plus l'implémentation par les pilotes estmoyennement bonne).

imm_date, imm_time, imm_timestamp, imm_calendar, imm_calendar_date,

imm_serializable, imm_binary

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

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

Les types de base des valeurs ont des Type constants correspondants définisdans org.hibernate.Hibernate. Par exemple, Hibernate.STRING représenté letype string.

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

Il est assez facile pour les développeurs de créer leurs propres types devaleurs. Par exemple, vous pourriez vouloir persister des propriétés du type

Page 126: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

112 Hibernate 3.3.1

java.lang.BigInteger dans des colonnnes VARCHAR. Hibernate ne procurepas par défaut un type pour cela. Mais les types que vous pouvez créerne se limitent pas à mapper des propriétés (ou élément collection) à unesimple colonne d'une table. Donc, par exemple, vous pourriez avoir unepropriété Java getName()/setName() de type java.lang.String persistée dansles colonnes FIRST_NAME, INITIAL, SURNAME.

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

<property name="twoStrings"

type="org.hibernate.test.DoubleStringType">

<column name="first_string"/>

<column name="second_string"/>

</property>

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

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

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

<property name="priority">

<type name="com.mycompany.usertypes.DefaultValueIntegerType">

<param name="default">0</param>

</type>

</property>

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

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

<typedef class="com.mycompany.usertypes.DefaultValueIntegerType"

name="default_zero">

Page 127: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Mapper une classe plus d'une fois

Hibernate 3.3.1 113

<param name="default">0</param>

</typedef>

<property name="priority" type="default_zero"/>

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

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

5.3. Mapper une classe plus d'une fois

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

<class name="Contract" table="Contracts"

entity-name="CurrentContract">

...

<set name="history" inverse="true"

order-by="effectiveEndDate desc">

<key column="currentContractId"/>

<one-to-many entity-name="HistoricalContract"/>

</set>

</class>

<class name="Contract" table="ContractHistory"

entity-name="HistoricalContract">

...

<many-to-one name="currentContract"

column="currentContractId"

entity-name="CurrentContract"/>

</class>

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

Page 128: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

114 Hibernate 3.3.1

5.4. SQL quoted identifiers

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

<class name="LineItem" table="`Line Item`">

<id name="id" column="`Item Id`"/><generator

class="assigned"/></id>

<property name="itemNumber" column="`Item #`"/>

...

</class>

5.5. alternatives Metadata

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

5.5.1. utilisation de XDoclet

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

package eg;

import java.util.Set;

import java.util.Date;

/**

* @hibernate.class

* table="CATS"

*/

public class Cat {

private Long id; // identifier

private Date birthdate;

private Cat mother;

private Set kittens

private Color color;

private char sex;

private float weight;

/*

* @hibernate.id

* generator-class="native"

Page 129: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

utilisation de XDoclet

Hibernate 3.3.1 115

* column="CAT_ID"

*/

public Long getId() {

return id;

}

private void setId(Long id) {

this.id=id;

}

/**

* @hibernate.many-to-one

* column="PARENT_ID"

*/

public Cat getMother() {

return mother;

}

void setMother(Cat mother) {

this.mother = mother;

}

/**

* @hibernate.property

* column="BIRTH_DATE"

*/

public Date getBirthdate() {

return birthdate;

}

void setBirthdate(Date date) {

birthdate = date;

}

/**

* @hibernate.property

* column="WEIGHT"

*/

public float getWeight() {

return weight;

}

void setWeight(float weight) {

this.weight = weight;

}

/**

* @hibernate.property

* column="COLOR"

* not-null="true"

*/

public Color getColor() {

return color;

}

void setColor(Color color) {

this.color = color;

}

/**

* @hibernate.set

Page 130: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

116 Hibernate 3.3.1

* inverse="true"

* order-by="BIRTH_DATE"

* @hibernate.collection-key

* column="PARENT_ID"

* @hibernate.collection-one-to-many

*/

public Set getKittens() {

return kittens;

}

void setKittens(Set kittens) {

this.kittens = kittens;

}

// addKitten not needed by Hibernate

public void addKitten(Cat kitten) {

kittens.add(kitten);

}

/**

* @hibernate.property

* column="SEX"

* not-null="true"

* update="false"

*/

public char getSex() {

return sex;

}

void setSex(char sex) {

this.sex=sex;

}

}

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

5.5.2. Utilisation des annotations JDK 5.0

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

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

Page 131: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

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

Hibernate 3.3.1 117

@Entity(access = AccessType.FIELD)

public class Customer implements Serializable {

@Id;

Long id;

String firstName;

String lastName;

Date birthday;

@Transient

Integer age;

@Embedded

private Address homeAddress;

@OneToMany(cascade=CascadeType.ALL)

@JoinColumn(name="CUSTOMER_ID")

Set<Order> orders;

// Getter/setter and business methods

}

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

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

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

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

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

insert - indique que la valeur de la propriété donnée est générée à l'insertionmais pas lors des futures mises à jour de l'enregistrement. Les colonnes

Page 132: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 5. Mapping O/R basique

118 Hibernate 3.3.1

de type "date de création" sont le cas d'utilisation typique de cette option.Notez que même les propriétés Section 5.1.9, « version (optionnel) » etSection 5.1.10, « timestamp (optionnel) » peuvent être déclarées commegénérées, cette option n'est pas disponible à cet endroit...

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

5.7. Objets auxiliaires de la base de données

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

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

<hibernate-mapping>

...

<database-object>

<create>CREATE TRIGGER my_trigger ...</create>

<drop>DROP TRIGGER my_trigger</drop>

</database-object>

</hibernate-mapping>

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

<hibernate-mapping>

...

<database-object>

<definition class="MyTriggerDefinition"/>

</database-object>

</hibernate-mapping>

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

<hibernate-mapping>

...

<database-object>

Page 133: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Objets auxiliaires de la base de données

Hibernate 3.3.1 119

<definition class="MyTriggerDefinition"/>

<dialect-scope name="org.hibernate.dialect.Oracle9Dialect"/>

<dialect-scope name="org.hibernate.dialect.OracleDialect"/>

</database-object>

</hibernate-mapping>

Page 134: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

120 Hibernate 3.3.1

Page 135: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 121

Chapitre 6. Mapping des collections

6.1. Collections persistantes

Hibernate requiert que les champs contenant des collections persistantessoient déclarés comme des types d'interface, par exemple :

public class Product {

private String serialNumber;

private Set parts = new HashSet();

public Set getParts() { return parts; }

void setParts(Set parts) { this.parts = parts; }

public String getSerialNumber() { return serialNumber; }

void setSerialNumber(String sn) { serialNumber = sn; }

}

L'interface réelle devrait être java.util.Set, java.util.Collection,java.util.List, java.util.Map, java.util.SortedSet,java.util.SortedMap ou ... n'importe quoi d'autre ! (Où "n'importequoi d'autre" signifie que vous devrez écrire une implémentation deorg.hibernate.usertype.UserCollectionType.)

Notez comment nous avons initialisé les variables d'instance avec uneinstance de HashSet. C'est le meilleur moyen pour initialiser les collectionsd'instances nouvellement créées (non persistantes). Quand nous fabriquonsl'instance persistante - en appelant persist(), par exemple - Hibernateremplacera réellement le HashSet avec une instance d'une implémentationpropre à Hibernate de Set. Prenez garde aux erreurs :

Cat cat = new DomesticCat();

Cat kitten = new DomesticCat();

....

Set kittens = new HashSet();

kittens.add(kitten);

cat.setKittens(kittens);

session.persist(cat);

kittens = cat.getKittens(); // Okay, kittens collection is a Set

(HashSet) cat.getKittens(); // Error!

Les collections persistantes injectées par Hibernate se comportent de lamême manière que HashMap, HashSet, TreeMap, TreeSet ou ArrayList, selon letype de l'interface.

Les instances des collections ont le comportement habituel des types desvaleurs. Elles sont automatiquement persistées quand elles sont référencéespar un objet persistant et automatiquement effacées quand elles sont

Page 136: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 6. Mapping des collections

122 Hibernate 3.3.1

déréférencées. Si une collection est passée d'un objet persistant à un autre,ses éléments pourraient être déplacés d'une table à une autre. Deux entitésne peuvent pas partager une référence vers une même instance d'unecollection. Dû au modèle relationnel sous-jacent, les propriétés contenantdes collections ne supportent pas la sémantique de la valeur null ; Hibernatene distingue pas une référence vers une collection nulle d'une collection vide.

Vous ne devriez pas vous préoccuper trop de ça. Utilisez les collectionspersistantes de la même manière que vous utilisez des collections Javaordinaires. Assurez-vous de comprendre la sémantique des associationsbidirectionnelles (traitée plus loin).

6.2. Mapper une collection

Astuce

Il y a pas mal de variétés de mappings qui peuvent être généréspour les collections, couvrant beaucoup des modèles relationnelscommuns. Nous vous suggérons d'expérimenter avec l'outil degénération de schéma pour avoir une idée de comment traduire lesdifférentes déclarations de mapping vers des table de la base dedonnées.

L'élément de mapping d'Hibernate utilisé pour mapper une collection dépenddu type de l'interface. Par exemple, un élément <set> est utilisé pour mapperdes propriétés de type Set.

<class name="Product">

<id name="serialNumber" column="productSerialNumber"/>

<set name="parts">

<key column="productSerialNumber" not-null="true"/>

<one-to-many class="Part"/>

</set>

</class>

À part <set>, il y aussi les éléments de mapping <list>, <map>, <bag>, <array>et <primitive-array>. L'élément <map> est représentatif :

<map

name="propertyName" (1)

table="table_name" (2)

schema="schema_name" (3)

lazy="true|extra|false" (4)

inverse="true|false" (5)

cascade="all|none|save-update|delete|all-delete-orphan|delet(6)e-

orphan"

sort="unsorted|natural|comparatorClass" (7)

Page 137: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Mapper une collection

Hibernate 3.3.1 123

order-by="column_name asc|desc" (8)

where="arbitrary sql where condition" (9)

fetch="join|select|subselect" (10)

batch-size="N" (11)

access="field|property|ClassName" (12)

optimistic-lock="true|false" (13)

mutable="true|false" (14)

node="element-name|."

embed-xml="true|false"

>

<key .... />

<map-key .... />

<element .... />

</map>

(1) name : le nom de la propriété contenant la collection(2) table (optionnel - par défaut = nom de la propriété) : le nom de la table

de la collection (non utilisé pour les associations one-to-many)(3) schema (optionnel) : le nom du schéma pour surcharger le schéma

déclaré dans l'élément racine(4) lazy (optionnel - par défaut = true) : peut être utilisé pour désactiver

l'initialisation tardive et spécifier que l'association est toujours rapportée,ou pour activer la récupération extra-paresseuse (NdT : extra-lazy) où laplupart des opérations n'initialisent pas la collection (approprié pour detrès grosses collections)

(5) inverse (optionnel - par défaut = false) : définit cette collection commel'extrêmité "inverse" de l'association bidirectionnelle

(6) cascade (optionnel - par défaut = none) : active les opérations decascade vers les entités filles

(7) sort (optionnel) : spécifie une collection triée via un ordre de tri naturel,ou via une classe comparateur donnée (implémentant Comparator)

(8) order-by (optionnel, seulement à partir du JDK1.4) : spécifie unecolonne de table (ou des colonnes) qui définit l'ordre d'itération de Map,Set ou Bag, avec en option asc ou desc

(9) where (optionnel) : spécifie une condition SQL arbitraire WHERE à utiliserau chargement ou à la suppression d'une collection (utile si la collectionne doit contenir qu'un sous ensemble des données disponibles)

(10) fetch (optionnel, par défaut = select) : à choisir entre récupération parjointures externes, récupération par selects séquentiels, et récupérationpar sous-selects séquentiels

(11) batch-size (optionnel, par défaut = 1) : une taille de batch (batchsize) utilisée pour charger plusieurs instances de cette collection eninitialisation tardive

(12) access (optionnel - par défaut = property) : La stratégie qu'Hibernatedoit utiliser pour accéder à la valeur de la propriété

Page 138: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 6. Mapping des collections

124 Hibernate 3.3.1

(13) optimistic-lock (optionnel - par défaut = true) : spécifie que changerl'état de la collection entraîne l'incrémentation de la version appartenantà l'entité (Pour une association un vers plusieurs, il est souventraisonnable de désactiver ce paramètre)

(14) mutable (optionnel - par défaut = true) : une valeur à false spécifieque les éléments de la collection ne changent jamais (une optimisationmineure dans certains cas)

6.2.1. Les clefs étrangères d'une collection

Les instances d'une collection sont distinguées dans la base par la clefétrangère de l'entité qui possède la collection. Cette clef étrangère estréférencée comme la(es) colonne(s) de la clef de la collection de la table dela collection. La colonne de la clef de la collection est mappée par l'élément<key>.

Il peut y avoir une contrainte de nullité sur la colonne de la clef étrangère.Pour les associations unidirectionnelles un vers plusieurs, la colonne de laclef étrangère peut être nulle par défaut, donc vous pourriez avoir besoin despécifier not-null="true".

<key column="productSerialNumber" not-null="true"/>

La contraite de la clef étrangère peut utiliser ON DELETE CASCADE.

<key column="productSerialNumber" on-delete="cascade"/>

Voir le chapitre précédent pour une définition complète de l'élément <key>.

6.2.2. Les éléments d'une collection

Les collections peuvent contenir la plupart des autres types Hibernate, donttous les types basiques, les types utilisateur, les composants, et bien sûr,les références vers d'autres entités. C'est une distinction importante : unobjet dans une collection pourrait être géré avec une sémantique de "valeur"(sa durée de vie dépend complètement du propriétaire de la collection) ou ilpourrait avoir une référence vers une autre entité, avec sa propre durée devie. Dans le dernier cas, seul le "lien" entre les 2 objets est considéré êtrel'état retenu par la collection.

Le type contenu est référencé comme le type de l'élément de lacollection. Les éléments de la collections sont mappés par <element>ou <composite-element>, ou dans le cas des références d'entité, avec<one-to-many> ou <many-to-many>. Les deux premiers mappent des élémentsavec un sémantique de valeur, les deux suivants sont utilisés pour mapperdes associations d'entité.

Page 139: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Collections indexées

Hibernate 3.3.1 125

6.2.3. Collections indexées

Tous les mappings de collection, exceptés ceux avec les sémantiquesd'ensemble (NdT : set) et de sac (NdT : bag), ont besoin d'une colonned'index dans la table de la collection - une colonne qui mappe un index detableau, ou un index de List, ou une clef de Map. L'index d'une Map peutêtre n'importe quel type basique, mappé avec <map-key>, ça peut être uneréférence d'entité mappée avec <map-key-many-to-many>, ou ça peut être untype composé, mappé avec <composite-map-key>. L'index d'un tableau oud'une liste est toujours de type integer et est mappé en utilisant l'élément<list-index>. Les colonnes mappées contiennent des entiers séquentiels(numérotés à partir de zéro par défaut).

<list-index

column="column_name" (1)

base="0|1|..."/>

(1) column_name (required): The name of the column holding the collectionindex values.

(1) base (optional, defaults to 0): The value of the index column thatcorresponds to the first element of the list or array.

<map-key

column="column_name" (1)

formula="any SQL expression" (2)

type="type_name" (3)

node="@attribute-name"

length="N"/>

(1) column (optional): The name of the column holding the collection indexvalues.

(2) formula (optional): A SQL formula used to evaluate the key of the map.(3) type (reguired): The type of the map keys.

<map-key-many-to-many

column="column_name" (1)

formula="any SQL expression" (2)(3)

class="ClassName"

/>

(1) column (optional): The name of the foreign key column for the collectionindex values.

(2) formula (optional): A SQL formula used to evaluate the foreign key ofthe map key.

(3) class (required): The entity class used as the map key.

Page 140: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 6. Mapping des collections

126 Hibernate 3.3.1

Si votre table n'a pas de colonne d'index, et que vous souhaitez tout demême utiliser List comme type de propriété, vous devriez mapper lapropriété comme un <bag> Hibernate. Un sac (NdT : bag) ne garde passon ordre quand il est récupéré de la base de données, mais il peut êtreoptionnellement trié ou ordonné.

6.2.4. Collections de valeurs et associationsplusieurs-vers-plusieurs

N'importe quelle collection de valeurs ou association plusieurs-vers-plusieursrequiert une table de collection avec une(des) colonne(s) de clef étrangère,une(des) colonne(s) d'élément de la collection ou des colonnes etpossiblement une(des) colonne(s) d'index.

Pour une collection de valeurs, nous utilisons la balise <element>.

<element

column="column_name" (1)

formula="any SQL expression" (2)

type="typename" (3)

length="L"

precision="P"

scale="S"

not-null="true|false"

unique="true|false"

node="element-name"

/>

(1) column (optional): The name of the column holding the collectionelement values.

(2) formula (optional): An SQL formula used to evaluate the element.(3) type (required): The type of the collection element.

A many-to-many association is specified using the <many-to-many> element.

<many-to-many

column="column_name" (1)

formula="any SQL expression" (2)

class="ClassName" (3)

fetch="select|join" (4)

unique="true|false" (5)

not-found="ignore|exception" (6)

entity-name="EntityName" (7)

property-ref="propertyNameFromAssociatedClass" (8)

node="element-name"

embed-xml="true|false"

/>

(1) column (optional): The name of the element foreign key column.

Page 141: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Collections de valeurs et associationsplusieurs-vers-plusieurs

Hibernate 3.3.1 127

(2) formula (optional): An SQL formula used to evaluate the element foreignkey value.

(3) class (required): The name of the associated class.(4) fetch (optional - defaults to join): enables outer-join or sequential select

fetching for this association. This is a special case; for full eager fetching(in a single SELECT) of an entity and its many-to-many relationships toother entities, you would enable join fetching not only of the collectionitself, but also with this attribute on the <many-to-many> nested element.

(5) unique (optional): Enable the DDL generation of a unique constraintfor the foreign-key column. This makes the association multiplicityeffectively one to many.

(6) not-found (optional - defaults to exception): Specifies how foreign keysthat reference missing rows will be handled: ignore will treat a missingrow as a null association.

(7) entity-name (optional): The entity name of the associated class, as analternative to class.

(8) property-ref: (optional) The name of a property of the associated classthat is joined to this foreign key. If not specified, the primary key of theassociated class is used.

Quelques exemples, d'abord, un ensemble de chaînes de caractères :

<set name="names" table="person_names">

<key column="person_id"/>

<element column="person_name" type="string"/>

</set>

Un bag contenant des entiers (avec un ordre d'itération déterminé parl'attribut order-by) :

<bag name="sizes"

table="item_sizes"

order-by="size asc">

<key column="item_id"/>

<element column="size" type="integer"/>

</bag>

Un tableau d'entités - dans ce cas, une association plusieurs-vers-plusieurs :

<array name="addresses"

table="PersonAddress"

cascade="persist">

<key column="personId"/>

<list-index column="sortOrder"/>

<many-to-many column="addressId" class="Address"/>

</array>

Une map de chaînes de caractères vers des dates :

Page 142: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 6. Mapping des collections

128 Hibernate 3.3.1

<map name="holidays"

table="holidays"

schema="dbo"

order-by="hol_name asc">

<key column="id"/>

<map-key column="hol_name" type="string"/>

<element column="hol_date" type="date"/>

</map>

Une liste de composants (discute dans le prochain chapitre) :

<list name="carComponents"

table="CarComponents">

<key column="carId"/>

<list-index column="sortOrder"/>

<composite-element class="CarComponent">

<property name="price"/>

<property name="type"/>

<property name="serialNumber" column="serialNum"/>

</composite-element>

</list>

6.2.5. Association un-vers-plusieurs

Une association un vers plusieurs lie les tables de deux classes par une clefétrangère, sans l'intervention d'une table de collection. Ce mapping perdcertaines sémantiques des collections Java normales :

• Une instance de la classe de l'entité contenue ne peut pas appartenir àplus d'une instance de la collection

• Une instance de la classe de l'entité contenue ne peut pas apparaître plusplus d'une valeur d'index de la collection

Une association de Product vers Part requiert l'existence d'une clefétrangère et possiblement une colonne d'index pour la table Part. Une balise<one-to-many> indique que c'est une association un vers plusieurs.

<one-to-many

class="ClassName" (1)

not-found="ignore|exception" (2)

entity-name="EntityName" (3)

node="element-name"

embed-xml="true|false"

/>

(1) class (requis) : le nom de la classe associée(2) not-found (optionnel - par défaut exception) : spécifie comment les

identifiants cachés qui référencent des lignes manquantes seront gérés: ignore traitera une ligne manquante comme une association nulle

Page 143: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Mappings de collection avancés

Hibernate 3.3.1 129

(3) entity-name (optional): The entity name of the associated class, as analternative to class.

Notez que l'élément <one-to-many> n'a pas besoin de déclarer de colonnes. Iln'est pas non plus nécessaire de spécifier le nom de la table nulle part.

Note très importante : si la colonne de la clef d'une association <one-to-many>est déclarée NOT NULL, vous devez déclarer le mapping de <key> avecnot-null="true" ou utiliser une association bidirectionnelle avec lemapping de la collection marqué inverse="true". Voir la discussion sur lesassociations bidirectionnelles plus tard dans ce chapitre.

Cet exemple montre une map d'entités Part par nom (où partName est unepropriété persistante de Part). Notez l'utilisation d'un index basé sur uneformule.

<map name="parts"

cascade="all">

<key column="productId" not-null="true"/>

<map-key formula="partName"/>

<one-to-many class="Part"/>

</map>

6.3. Mappings de collection avancés

6.3.1. Collections triées

Hibernate supporte des collections implémentant java.util.SortedMap etjava.util.SortedSet. Vous devez spécifier un comparateur dans le fichier demapping :

<set name="aliases"

table="person_aliases"

sort="natural">

<key column="person"/>

<element column="name" type="string"/>

</set>

<map name="holidays" sort="my.custom.HolidayComparator">

<key column="year_id"/>

<map-key column="hol_name" type="string"/>

<element column="hol_date" type="date"/>

</map>

Les valeurs permises pour l'attribut sort sont unsorted, natural et le nomd'une classe implémentant java.util.Comparator.

Les collections triées se comportent réellement comme java.util.TreeSet oujava.util.TreeMap.

Page 144: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 6. Mapping des collections

130 Hibernate 3.3.1

Si vous voulez que la base de données elle-même ordonne les éléments dela collection, utilisez l'attribut order-by des mappings set, bag ou map. Cettesolution est seulement disponible à partir du JDK 1.4 (c'est implémenté enutilisant LinkedHashSet ou LinkedHashMap). Ceci exécute le tri dans la requêteSQL, pas en mémoire.

<set name="aliases" table="person_aliases" order-by="lower(name)

asc">

<key column="person"/>

<element column="name" type="string"/>

</set>

<map name="holidays" order-by="hol_date, hol_name">

<key column="year_id"/>

<map-key column="hol_name" type="string"/>

<element column="hol_date type="date"/>

</map>

Notez que la valeur de l'attribut order-by est un ordre SQL, pas un ordre HQL!

Les associations peuvent même être triées sur des critères arbitraires àl'exécution en utilisant un filter() de collection.

sortedUsers = s.createFilter( group.getUsers(), "order by this.name"

).list();

6.3.2. Associations bidirectionnelles

A bidirectional association allows navigation from both "ends" of theassociation. Two kinds of bidirectional association are supported:

un-vers-plusieurs (NdT : one-to-many)ensemble ou sac à une extrémité, une seule valeur à l'autre

plusieurs-vers-plusieurs (NdT : many-to-many)ensemble ou sac aux deux extrémités

Vous pouvez spécifier une association plusieurs-vers-plusieursbidirectionnelle simplement en mappant deux associationsplusieurs-vers-plusieurs vers la même table de base de données et endéclarant une extrémité comme inverse (celle de votre choix, mais ça nepeut pas être une collection indexée).

Voici un exemple d'association bidirectionnelle plusieurs-vers-plusieurs ;chaque catégorie peut avoir plusieurs objets et chaque objet peut être dansplusieurs catégories :

Page 145: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Associations bidirectionnelles

Hibernate 3.3.1 131

<class name="Category">

<id name="id" column="CATEGORY_ID"/>

...

<bag name="items" table="CATEGORY_ITEM">

<key column="CATEGORY_ID"/>

<many-to-many class="Item" column="ITEM_ID"/>

</bag>

</class>

<class name="Item">

<id name="id" column="ITEM_ID"/>

...

<!-- inverse end -->

<bag name="categories" table="CATEGORY_ITEM" inverse="true">

<key column="ITEM_ID"/>

<many-to-many class="Category" column="CATEGORY_ID"/>

</bag>

</class>

Les changements faits uniquement sur l'extréminté inverse de l'associationne sont pas persistés. Ceci signifie qu'Hibernate a deux représentations enmémoire pour chaque association bidirectionnelles, un lien de A vers B et unautre de B vers A. C'est plus facile à comprendre si vous pensez au modèleobjet de Java et comment nous créons une relation plusieurs-vers-plusieursen Java :

category.getItems().add(item); // The category now "knows"

about the relationship

item.getCategories().add(category); // The item now "knows"

about the relationship

session.persist(item); // The relationship won't

be saved!

session.persist(category); // The relationship will be

saved

La partie non-inverse est utilisée pour sauvegarder la représentation enmémoire dans la base de données.

Vous pouvez définir une association un-vers-plusieurs bidirectionnelle enmappant une association un-vers-plusieurs vers la(es) même(s) colonne(s)de table qu'une association plusieurs-vers-un et en déclarant l'extrémitépluri-valuée inverse="true".

<class name="Parent">

<id name="id" column="parent_id"/>

....

<set name="children" inverse="true">

<key column="parent_id"/>

Page 146: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 6. Mapping des collections

132 Hibernate 3.3.1

<one-to-many class="Child"/>

</set>

</class>

<class name="Child">

<id name="id" column="child_id"/>

....

<many-to-one name="parent"

class="Parent"

column="parent_id"

not-null="true"/>

</class>

Mapper une extrémité d'une association avec inverse="true" n'affecte pasl'opération de cascades, ce sont des concepts orthogonaux !

6.3.3. Associations bidirectionnelles avec des collectionsindexées

Une association bidirectionnelle où une extrémité est représentée commeune <list> ou une <map> requiert une considération spéciale. Si il y aune propriété de la classe enfant qui mappe la colonne de l'index, pas deproblème, nous pouvons continuer à utiliser inverse="true" sur le mappingde la collection :

<class name="Parent">

<id name="id" column="parent_id"/>

....

<map name="children" inverse="true">

<key column="parent_id"/>

<map-key column="name"

type="string"/>

<one-to-many class="Child"/>

</map>

</class>

<class name="Child">

<id name="id" column="child_id"/>

....

<property name="name"

not-null="true"/>

<many-to-one name="parent"

class="Parent"

column="parent_id"

not-null="true"/>

</class>

Mais, si il n'y a pas de telle prorpriété sur la classe enfant, nous ne pouvonspas penser à l'association comme vraiment bidirectionnelle (il y a desinformations disponibles à une extrémité de l'association qui ne sont pas

Page 147: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Associations ternaires

Hibernate 3.3.1 133

disponibles à l'autre extrémité). Dans ce cas, nous ne pouvons pas mapperla collection inverse="true". À la place, nous pourrions utiliser le mappingsuivant :

<class name="Parent">

<id name="id" column="parent_id"/>

....

<map name="children">

<key column="parent_id"

not-null="true"/>

<map-key column="name"

type="string"/>

<one-to-many class="Child"/>

</map>

</class>

<class name="Child">

<id name="id" column="child_id"/>

....

<many-to-one name="parent"

class="Parent"

column="parent_id"

insert="false"

update="false"

not-null="true"/>

</class>

Notez que dans ce mapping, l'extrémité de l'association contenant lacollection est responsable des mises à jour de la clef étrangère. À faire : celaentraîne-t-il réellement des expressions updates inutiles ?

6.3.4. Associations ternaires

Il y a trois approches possibles pour mapper une association ternaire. L'uneest d'utiliser une Map avec une association en tant qu'index :

<map name="contracts">

<key column="employer_id" not-null="true"/>

<map-key-many-to-many column="employee_id" class="Employee"/>

<one-to-many class="Contract"/>

</map>

<map name="connections">

<key column="incoming_node_id"/>

<map-key-many-to-many column="outgoing_node_id" class="Node"/>

<many-to-many column="connection_id" class="Connection"/>

</map>

Une seconde approche est simplement de remodeler l'association commeune classe d'entité. C'est l'approche la plus commune.

Page 148: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 6. Mapping des collections

134 Hibernate 3.3.1

Une alternative finale est d'utiliser des éléments composites, dont nousdiscuterons plus tard.

6.3.5. Utiliser un <idbag>

Si vous embrassez pleinement notre vue que les clefs composées sont unemauvaise chose et que des entités devraient avoir des identifiants artificiels(des clefs subrogées), alors vous pourriez trouver un peu curieux que lesassociations plusieurs-vers-plusieurs et les collections de valeurs que nousavons montré jusqu'ici mappent toutes des tables avec des clefs composées! Maintenant, ce point est assez discutable ; une table d'association purene semble pas beaucoup bénéficier d'une clef subrogée (bien qu'unecollection de valeur composées le pourrait). Néanmoins, Hibernatefournit une foncionnalité qui vous permet de mapper des associationsplusieurs-vers-plusieurs et des collections de valeurs vers une table avecune clef subrogée.

L'élément <idbag> vous laisse mapper une List (ou une Collection) avecune sémantique de sac.

<idbag name="lovers" table="LOVERS">

<collection-id column="ID" type="long">

<generator class="sequence"/>

</collection-id>

<key column="PERSON1"/>

<many-to-many column="PERSON2" class="Person" fetch="join"/>

</idbag>

Comme vous pouvez voir, un <idbag> a un généréteur d'id artificiel, commeune classe d'entité ! Une clef subrogée différente est assignée à chaqueligne de la collection. Cependant, Hibernate ne fournit pas de mécanismepour découvrir la valeur d'une clef subrogée d'une ligne particulière.

Notez que les performances de la mise à jour d'un <idbag> sont bienmeilleures qu'un <bag> ordinaire ! Hibernate peut localiser des lignesindividuelles efficacement et les mettre à jour ou les effacer individuellement,comme une liste, une map ou un ensemble.

Dans l'implémentation actuelle, la stratégie de la génération de l'identifiantnative n'est pas supportée pour les identifiants de collection <idbag>.

6.4. Exemples de collections

Les sections précédentes sont assez confuses. Donc prenons un exemple.Cette classe :

package eg;

Page 149: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Exemples de collections

Hibernate 3.3.1 135

import java.util.Set;

public class Parent {

private long id;

private Set children;

public long getId() { return id; }

private void setId(long id) { this.id=id; }

private Set getChildren() { return children; }

private void setChildren(Set children) { this.children=children;

}

....

....

}

a une collection d'instances de Child. Si chaque enfant a au plus un parent,le mapping le plus naturel est une association un-vers-plusieurs :

<hibernate-mapping>

<class name="Parent">

<id name="id">

<generator class="sequence"/>

</id>

<set name="children">

<key column="parent_id"/>

<one-to-many class="Child"/>

</set>

</class>

<class name="Child">

<id name="id">

<generator class="sequence"/>

</id>

<property name="name"/>

</class>

</hibernate-mapping>

Ceci mappe les définitions de tables suivantes :

create table parent ( id bigint not null primary key )

create table child ( id bigint not null primary key, name

varchar(255), parent_id bigint )

alter table child add constraint childfk0 (parent_id) references

parent

Si le parent est requis, utilisez une association un-vers-plusieursunidirectionnelle :

Page 150: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 6. Mapping des collections

136 Hibernate 3.3.1

<hibernate-mapping>

<class name="Parent">

<id name="id">

<generator class="sequence"/>

</id>

<set name="children" inverse="true">

<key column="parent_id"/>

<one-to-many class="Child"/>

</set>

</class>

<class name="Child">

<id name="id">

<generator class="sequence"/>

</id>

<property name="name"/>

<many-to-one name="parent" class="Parent" column="parent_id"

not-null="true"/>

</class>

</hibernate-mapping>

Notez la contrainte NOT NULL :

create table parent ( id bigint not null primary key )

create table child ( id bigint not null

primary key,

name varchar(255),

parent_id bigint not null )

alter table child add constraint childfk0 (parent_id) references

parent

Alternativement, si vous insistez absolument pour que cette association soitunidirectionnelle, vous pouvez déclarer la contrainte NOT NULL sur le mapping<key> :

<hibernate-mapping>

<class name="Parent">

<id name="id">

<generator class="sequence"/>

</id>

<set name="children">

<key column="parent_id" not-null="true"/>

<one-to-many class="Child"/>

</set>

</class>

<class name="Child">

<id name="id">

<generator class="sequence"/>

Page 151: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Exemples de collections

Hibernate 3.3.1 137

</id>

<property name="name"/>

</class>

</hibernate-mapping>

D'un autre côté, si un enfant pouvait avoir plusieurs parent, une associationplusieurs-vers-plusieurs est plus appropriée :

<hibernate-mapping>

<class name="Parent">

<id name="id">

<generator class="sequence"/>

</id>

<set name="children" table="childset">

<key column="parent_id"/>

<many-to-many class="Child" column="child_id"/>

</set>

</class>

<class name="Child">

<id name="id">

<generator class="sequence"/>

</id>

<property name="name"/>

</class>

</hibernate-mapping>

Définitions des tables :

create table parent ( id bigint not null primary key )

create table child ( id bigint not null primary key, name

varchar(255) )

create table childset ( parent_id bigint not null,

child_id bigint not null,

primary key ( parent_id, child_id ) )

alter table childset add constraint childsetfk0 (parent_id)

references parent

alter table childset add constraint childsetfk1 (child_id)

references child

Pour plus d'exemples et une revue complète du mapping de la relationparent/enfant, voir see Chapitre 21, Exemple : Père/Fils.

Des mappings d'association plus exotiques sont possibles, nouscataloguerons toutes les possibilités dans le prochain chapitre.

Page 152: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

138 Hibernate 3.3.1

Page 153: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 139

Chapitre 7. Mapper les associations

7.1. Introduction

Correctement mapper les associations est souvent la tâche la plus difficile.Dans cette section nous traiterons les cas classiques les uns après lesautres. Nous commencerons d'abbord par les mappings unidirectionnels,puis nous aborderons la question des mappings bidirectionnels. Nousillustrerons tous nos exemples avec les classes Person et Address.

Nous utiliserons deux critères pour classer les associations : le premier serade savoir si l'association est bâti sur une table supplémentaire d'associationet le deuxieme sera basé sur la multiplicité de cette association.

Autoriser une clé étrangère nulle est considéré comme un mauvais choixdans la construction d'un modèle de données. Nous supposerons doncque dans tous les exemples qui vont suivre on aura interdit la valeur nullepour les clés étrangères. Attention, ceci ne veut pas dire que Hibernate nesupporte pas les clés étrangères pouvant prendre des valeurs nulles, lesexemples qui suivent continueront de fonctionner si vous décidiez ne plusimposer la contrainte de non-nullité sur les clés étrangères.

7.2. Association unidirectionnelle

7.2.1. plusieurs à un

Une association plusieurs-à-un (many-to-one) unidirectionnelle est le typeque l'on rencontre le plus souvent dans les associations unidirectionnelles.

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

</id>

<many-to-one name="address"

column="addressId"

not-null="true"/>

</class>

<class name="Address">

<id name="id" column="addressId">

<generator class="native"/>

</id>

</class>

create table Person ( personId bigint not null primary key,

addressId bigint not null )

Page 154: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 7. Mapper les associations

140 Hibernate 3.3.1

create table Address ( addressId bigint not null primary key )

7.2.2. one to one

une association un-à-un (one-to-one) sur une clé étrangère est presqueidentique. La seule différence est sur la contrainte d'unicité que l'on impose àcette colonne.

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

</id>

<many-to-one name="address"

column="addressId"

unique="true"

not-null="true"/>

</class>

<class name="Address">

<id name="id" column="addressId">

<generator class="native"/>

</id>

</class>

create table Person ( personId bigint not null primary key,

addressId bigint not null unique )

create table Address ( addressId bigint not null primary key )

Une association un-à-un (one-to-one) unidirectionnelle sur une clé primaireutilise un générateur d'identifiant particulier. (Remarquez que nous avonsinversé le sens de cette association dans cet exemple.)

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

</id>

</class>

<class name="Address">

<id name="id" column="personId">

<generator class="foreign">

<param name="property">person</param>

</generator>

</id>

<one-to-one name="person" constrained="true"/>

</class>

Page 155: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

un à plusieurs

Hibernate 3.3.1 141

create table Person ( personId bigint not null primary key )

create table Address ( personId bigint not null primary key )

7.2.3. un à plusieurs

Une association un-à-plusieurs (one-to-many) unidirectionnelle sur une cléétrangère est vraiment inhabituelle, et n'est pas vraiment recommandée.

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

</id>

<set name="addresses">

<key column="personId"

not-null="true"/>

<one-to-many class="Address"/>

</set>

</class>

<class name="Address">

<id name="id" column="addressId">

<generator class="native"/>

</id>

</class>

create table Person ( personId bigint not null primary key )

create table Address ( addressId bigint not null primary key,

personId bigint not null )

Nous pensons qu'il est préférable d'utiliser une table de jointure pour ce typed'association.

7.3. Associations unidirectionnelles avec tables dejointure

7.3.1. un à plusieurs

Une association unidirectionnelle un-à-plusieurs (one-to-many) avecune table de jointure est un bien meilleur choix. Remarquez qu'enspécifiant unique="true", on a changé la multiplicité plusieurs-à-plusieurs(many-to-many) pour un-à-plusieurs (one-to-many).

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

Page 156: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 7. Mapper les associations

142 Hibernate 3.3.1

</id>

<set name="addresses" table="PersonAddress">

<key column="personId"/>

<many-to-many column="addressId"

unique="true"

class="Address"/>

</set>

</class>

<class name="Address">

<id name="id" column="addressId">

<generator class="native"/>

</id>

</class>

create table Person ( personId bigint not null primary key )

create table PersonAddress ( personId not null, addressId bigint not

null primary key )

create table Address ( addressId bigint not null primary key )

7.3.2. plusieurs à un

Une assiociation plusieurs-à-un (many-to-one) unidirectionnelle sur une tablede jointure est très fréquente quand l'association est optionnelle.

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

</id>

<join table="PersonAddress"

optional="true">

<key column="personId" unique="true"/>

<many-to-one name="address"

column="addressId"

not-null="true"/>

</join>

</class>

<class name="Address">

<id name="id" column="addressId">

<generator class="native"/>

</id>

</class>

create table Person ( personId bigint not null primary key )

create table PersonAddress ( personId bigint not null primary key,

addressId bigint not null )

create table Address ( addressId bigint not null primary key )

Page 157: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

one to one

Hibernate 3.3.1 143

7.3.3. one to one

Une association unidirectionnelle un-à-un (one-to-one) sur une table dejointure est extrèmement rare mais envisageable.

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

</id>

<join table="PersonAddress"

optional="true">

<key column="personId"

unique="true"/>

<many-to-one name="address"

column="addressId"

not-null="true"

unique="true"/>

</join>

</class>

<class name="Address">

<id name="id" column="addressId">

<generator class="native"/>

</id>

</class>

create table Person ( personId bigint not null primary key )

create table PersonAddress ( personId bigint not null primary key,

addressId bigint not null unique )

create table Address ( addressId bigint not null primary key )

7.3.4. plusieurs à plusieurs

Finallement, nous avons l'association unidirectionnelle plusieurs-à-plusieurs(many-to-many).

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

</id>

<set name="addresses" table="PersonAddress">

<key column="personId"/>

<many-to-many column="addressId"

class="Address"/>

</set>

</class>

<class name="Address">

<id name="id" column="addressId">

Page 158: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 7. Mapper les associations

144 Hibernate 3.3.1

<generator class="native"/>

</id>

</class>

create table Person ( personId bigint not null primary key )

create table PersonAddress ( personId bigint not null, addressId

bigint not null, primary key (personId, addressId) )

create table Address ( addressId bigint not null primary key )

7.4. Associations bidirectionnelles

7.4.1. un à plusieurs / plusieurs à un

Une association bidirectionnelle plusieurs à un (many-to-one) est le typed'association que l'on rencontre le plus souvent. (c'est la façon standard decréer des relations parents/enfants.)

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

</id>

<many-to-one name="address"

column="addressId"

not-null="true"/>

</class>

<class name="Address">

<id name="id" column="addressId">

<generator class="native"/>

</id>

<set name="people" inverse="true">

<key column="addressId"/>

<one-to-many class="Person"/>

</set>

</class>

create table Person ( personId bigint not null primary key,

addressId bigint not null )

create table Address ( addressId bigint not null primary key )

Si vous utilisez une List (ou toute autre collection indexée) vous devezparamétrer la colonne key de la clé étrangère à not null, et laisser Hibernategérer l'association depuis l'extrémité collection pour maintenir l'indexde chaque élément (rendant l'autre extrémité virtuellement inverse enparamétrant update="false" et insert="false"):

Page 159: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

one to one

Hibernate 3.3.1 145

<class name="Person">

<id name="id"/>

...

<many-to-one name="address"

column="addressId"

not-null="true"

insert="false"

update="false"/>

</class>

<class name="Address">

<id name="id"/>

...

<list name="people">

<key column="addressId" not-null="true"/>

<list-index column="peopleIdx"/>

<one-to-many class="Person"/>

</list>

</class>

It is important that you define not-null="true" on the <key> element of thecollection mapping if the underlying foreign key column is NOT NULL. Don'tonly declare not-null="true" on a possible nested <column> element, but onthe <key> element.

7.4.2. one to one

Une association bidirectionnelle un à un (one-to-one) sur une clé étrangèreest aussi très fréquente.

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

</id>

<many-to-one name="address"

column="addressId"

unique="true"

not-null="true"/>

</class>

<class name="Address">

<id name="id" column="addressId">

<generator class="native"/>

</id>

<one-to-one name="person"

property-ref="address"/>

</class>

create table Person ( personId bigint not null primary key,

addressId bigint not null unique )

Page 160: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 7. Mapper les associations

146 Hibernate 3.3.1

create table Address ( addressId bigint not null primary key )

Une association bidirectionnelle un-à-un (one-to-one) sur une clé primaireutilise un générateur particulier d'id.

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

</id>

<one-to-one name="address"/>

</class>

<class name="Address">

<id name="id" column="personId">

<generator class="foreign">

<param name="property">person</param>

</generator>

</id>

<one-to-one name="person"

constrained="true"/>

</class>

create table Person ( personId bigint not null primary key )

create table Address ( personId bigint not null primary key )

7.5. Associations bidirectionnelles avec table dejointure

7.5.1. un à plusieurs / plusieurs à un

Une association bidirectionnelle un-à-plusieurs (one-to-many) sur une tablede jointure . Remarquez que inverse="true" peut s'appliquer sur les deuxextrémités de l' association, sur la collection, ou sur la jointure.

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

</id>

<set name="addresses"

table="PersonAddress">

<key column="personId"/>

<many-to-many column="addressId"

unique="true"

class="Address"/>

</set>

</class>

Page 161: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

one to one

Hibernate 3.3.1 147

<class name="Address">

<id name="id" column="addressId">

<generator class="native"/>

</id>

<join table="PersonAddress"

inverse="true"

optional="true">

<key column="addressId"/>

<many-to-one name="person"

column="personId"

not-null="true"/>

</join>

</class>

create table Person ( personId bigint not null primary key )

create table PersonAddress ( personId bigint not null, addressId

bigint not null primary key )

create table Address ( addressId bigint not null primary key )

7.5.2. one to one

Une association bidirectionnelle un-à-un (one-to-one) sur une table dejointure est extrèmement rare mais envisageable.

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

</id>

<join table="PersonAddress"

optional="true">

<key column="personId"

unique="true"/>

<many-to-one name="address"

column="addressId"

not-null="true"

unique="true"/>

</join>

</class>

<class name="Address">

<id name="id" column="addressId">

<generator class="native"/>

</id>

<join table="PersonAddress"

optional="true"

inverse="true">

<key column="addressId"

unique="true"/>

<many-to-one name="person"

column="personId"

Page 162: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 7. Mapper les associations

148 Hibernate 3.3.1

not-null="true"

unique="true"/>

</join>

</class>

create table Person ( personId bigint not null primary key )

create table PersonAddress ( personId bigint not null primary key,

addressId bigint not null unique )

create table Address ( addressId bigint not null primary key )

7.5.3. plusieurs à plusieurs

Finallement nous avons l'association bidirectionnelle plusieurs à plusieurs.

<class name="Person">

<id name="id" column="personId">

<generator class="native"/>

</id>

<set name="addresses" table="PersonAddress">

<key column="personId"/>

<many-to-many column="addressId"

class="Address"/>

</set>

</class>

<class name="Address">

<id name="id" column="addressId">

<generator class="native"/>

</id>

<set name="people" inverse="true" table="PersonAddress">

<key column="addressId"/>

<many-to-many column="personId"

class="Person"/>

</set>

</class>

create table Person ( personId bigint not null primary key )

create table PersonAddress ( personId bigint not null, addressId

bigint not null, primary key (personId, addressId) )

create table Address ( addressId bigint not null primary key )

7.6. Des mappings plus complexes

Des associations encore plus complexes sont extrêmement rares.Hibernate permet de gérer des situations plus complexes en utilisant desparties SQL dans les fichiers de mapping. Par exemple, si une table avec

Page 163: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Des mappings plus complexes

Hibernate 3.3.1 149

l'historiques des informations d'un compte définit les colonnes accountNumber,effectiveEndDate et effectiveStartDate, mappées de telle sorte:

<properties name="currentAccountKey">

<property name="accountNumber" type="string" not-null="true"/>

<property name="currentAccount" type="boolean">

<formula>case when effectiveEndDate is null then 1 else 0

end</formula>

</property>

</properties>

<property name="effectiveEndDate" type="date"/>

<property name="effectiveStateDate" type="date" not-null="true"/>

alors nous pouvons mapper une association à l'instance courante (celle avecune effectiveEndDate) nulle en utilisant:

<many-to-one name="currentAccountInfo"

property-ref="currentAccountKey"

class="AccountInfo">

<column name="accountNumber"/>

<formula>'1'</formula>

</many-to-one>

Dans un exemple plus complexe, imaginez qu'une association entre Employeeet Organization est gérée dans une table Employment pleines de donnéeshistoriques. Dans ce cas, une association vers l'employeur le plus récent(celui avec la startDate la plus récente) pourrait être mappée comme cela:

<join>

<key column="employeeId"/>

<subselect>

select employeeId, orgId

from Employments

group by orgId

having startDate = max(startDate)

</subselect>

<many-to-one name="mostRecentEmployer"

class="Organization"

column="orgId"/>

</join>

Vous pouvez être créatif grace à ces possibilités, mais il est généralementplus pratique d'utiliser des requêtes HQL ou criteria dans ce genre desituation.

Page 164: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

150 Hibernate 3.3.1

Page 165: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 151

Chapitre 8. Mapping de composantsLa notion de composants est réutilisé dans différents contextes, avecdifférents objectifs, à travers Hibernate.

8.1. Objects dépendants

Le composant est un objet inclu dans un autre qui est sauvegardé commeune valeur, et non pas comme une entité. Le composant fait référence à lanotion (au sens objet) de composition (et non pas de composant au sensd'architecture de composants). Par exemple on pourrait modélisé l'objetpersonne de cette façon:

public class Person {

private java.util.Date birthday;

private Name name;

private String key;

public String getKey() {

return key;

}

private void setKey(String key) {

this.key=key;

}

public java.util.Date getBirthday() {

return birthday;

}

public void setBirthday(java.util.Date birthday) {

this.birthday = birthday;

}

public Name getName() {

return name;

}

public void setName(Name name) {

this.name = name;

}

......

......

}

public class Name {

char initial;

String first;

String last;

public String getFirst() {

return first;

}

void setFirst(String first) {

this.first = first;

}

public String getLast() {

Page 166: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 8. Mapping de composants

152 Hibernate 3.3.1

return last;

}

void setLast(String last) {

this.last = last;

}

public char getInitial() {

return initial;

}

void setInitial(char initial) {

this.initial = initial;

}

}

Maintenant Name peut-être sauvegardé comme un composant de Person.Remarquer que Name définit des methodes d'accès et de modification pourses propriétés persistantes, mais il n'a pas besoin des interfaces ou despropriétés d'identification ( par exemple getId() ) qui sont propres aux entités.

Nous serions alors amené à mapper ce composant de cette façon:

<class name="eg.Person" table="person">

<id name="Key" column="pid" type="string">

<generator class="uuid"/>

</id>

<property name="birthday" type="date"/>

<component name="Name" class="eg.Name"> <!-- class attribute

optional -->

<property name="initial"/>

<property name="first"/>

<property name="last"/>

</component>

</class>

La table person aurai les colonnes pid, birthday, initial, first and last.

Comme tous les types valeurs, les composants ne supportent pas lesréférences partagés. En d'autres mots, deux instances de person peuventavoir un même nom, mais ces noms sont indépendants, ils peuvent êtreidentiques si on les compare par valeur mais ils représentent deux objetsdistincts en mémoire. La notion de nullité pour un composant est ad hoc.Quand il recharge l'objet qui contient le composant, Hibernate supposeraque si tous les champs du composants sont nuls alors le composant serapositionné à la valeur null. Ce choix programmatif devrait être satisfaisantdans la plupart des cas.

Les propriétés d'un composant peuvent être de tous les types qu'Hibernatesupporte habituellement (collections, many-to-one associations, autrescomposants, etc). Les composants inclus ne doivent pas être vus commequelque chose d'exotique. Hibernate a été conçu pour supporter un modèleobjet très granulaire.

Page 167: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Collection d'objets dépendants

Hibernate 3.3.1 153

Le <component> peut inclure dans la liste de ses propriétés une référence au<parent> conteneur.

<class name="eg.Person" table="person">

<id name="Key" column="pid" type="string">

<generator class="uuid"/>

</id>

<property name="birthday" type="date"/>

<component name="Name" class="eg.Name" unique="true">

<parent name="namedPerson"/> <!-- reference back to the

Person -->

<property name="initial"/>

<property name="first"/>

<property name="last"/>

</component>

</class>

8.2. Collection d'objets dépendants

Les collections d'objets dépendants sont supportés (exemple: un tableaude type Name). Déclarer la collection de composants en remplaçant le tag<element> par le tag <composite-element>.

<set name="someNames" table="some_names" lazy="true">

<key column="id"/>

<composite-element class="eg.Name"> <!-- class attribute

required -->

<property name="initial"/>

<property name="first"/>

<property name="last"/>

</composite-element>

</set>

Remarque: Si vous définissez un Set d'élément composite, il est trèsimportant d'implémenter la méthode equals() et hashCode() correctement.

Les élements composite peuvent aussi contenir des composants maispas des collections. Si votre élément composite contient aussi descomposants, utilisez l'élément <nested-composite-element> . Une collectionsde composants qui ccontiennent eux-mêmes des composants est un cas trèsexotique. A ce stade demandez-vous si une association un-à-plusieurs neserait pas plus approprié. Essayez de re remodeler votre élément compositecomme une entité ( Dans ce cas même si le modèle Java est le même lalogique de persitence et de relation sont tout de même différentes)

Remarque, le mapping d'éléments composites ne supporte pas la nullité despropriétés lorsqu'on utilise un <set>. Hibernate lorsqu'il supprime un objetutilise chaque colonne pour identifier un objet (on ne peut pas utiliser des

Page 168: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 8. Mapping de composants

154 Hibernate 3.3.1

clés primaires distinctes dans une table d'éléments composites), ce qui n'estpas possible avec des valeurs nulles. Vous devez donc choisir d'interdirela nullité des propriétés d'un élément composite ou choisir un autre type decollection comme : <list>, <map>, <bag> ou <idbag>.

Un cas particulier d'élément composite est un élément composite quiinclut un élément <many-to-one>. Un mapping comme celui-ci vous permetd'associer les colonnes d'une table d'association plusieurs à plusieurs(many-to-many) à la classse de l'élément composite. L'exemple suivant estune association plusieurs à plusieurs de Order à Item à purchaseDate, priceet quantity sont des propriétés de l'association.

<class name="eg.Order" .... >

....

<set name="purchasedItems" table="purchase_items" lazy="true">

<key column="order_id">

<composite-element class="eg.Purchase">

<property name="purchaseDate"/>

<property name="price"/>

<property name="quantity"/>

<many-to-one name="item" class="eg.Item"/> <!-- class

attribute is optional -->

</composite-element>

</set>

</class>

Bien sûr, il ne peut pas y avoir de référence à l'achat (purchase) depuisl'article (item), pour pouvoir naviguer de façon bidirectionnelle dansl'association. N'oubliez pas que les composants sont de type valeurs etn'autorise pas les références partagées.

Même les associations ternaires ou quaternaires sont possibles:

<class name="eg.Order" .... >

....

<set name="purchasedItems" table="purchase_items" lazy="true">

<key column="order_id">

<composite-element class="eg.OrderLine">

<many-to-one name="purchaseDetails class="eg.Purchase"/>

<many-to-one name="item" class="eg.Item"/>

</composite-element>

</set>

</class>

Les éléments composites peuvent apparaître dans les requêtes en utilisant lamême syntaxe que associations

Page 169: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Utiliser les composants comme index demap

Hibernate 3.3.1 155

8.3. Utiliser les composants comme index de map

l'élément <composite-map-key> vous permet d'utiliser une classe decomposant comme indice de Map. Assurez-vous d'avoir surdéfini hashCode()et equals() dans la classe du composant.

8.4. Utiliser un composant comme identifiant

Vous pouvez utiliser un composant comme identifiant d'une entité. Mais pourcela la classe du composant doit respecter certaines règles.

• Elle doit implémenter java.io.Serializable.• Elle doit redéfinir equals() et hashCode(), de façon cohérente avec le fait

qu'elle définit une clé composite dans la base de données.

Remarque: avec hibernate3, la seconde règle n'est plus absolumentnecessaire mais faîtes le quand même.

Vous ne pouvez pas utiliser de IdentifierGenerator pour générer une clécomposite, l'application devra définir elle même ses propres identifiants.

Utiliser l'élément <composite-id> (en incluant l'élément <key-property>) àla place de l'habituel déclaration <id>. Par exemple la classe OrderLine quidépend de la clé primaire (composite) de Order.

<class name="OrderLine">

<composite-id name="id" class="OrderLineId">

<key-property name="lineId"/>

<key-property name="orderId"/>

<key-property name="customerId"/>

</composite-id>

<property name="name"/>

<many-to-one name="order" class="Order"

insert="false" update="false">

<column name="orderId"/>

<column name="customerId"/>

</many-to-one>

....

</class>

Maintenant toutes clés étrangères référençant la table OrderLine devraaussi être composite. Vous devez en tenir compte lorsque vous écrivez vosmapping d'association pour les autres classes. Une association à OrderLinedevrait être mappé de la façon suivante :

Page 170: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 8. Mapping de composants

156 Hibernate 3.3.1

<many-to-one name="orderLine" class="OrderLine">

<!-- the "class" attribute is optional, as usual -->

<column name="lineId"/>

<column name="orderId"/>

<column name="customerId"/>

</many-to-one>

(Remarque: l'élément <column> est une alternative à l'attribut column que l'onutilise partout.)

Une association plusieurs-à-plusieurs (many-to-many) à OrderLine utiliseraaussi une clé étrangère composite:

<set name="undeliveredOrderLines">

<key column name="warehouseId"/>

<many-to-many class="OrderLine">

<column name="lineId"/>

<column name="orderId"/>

<column name="customerId"/>

</many-to-many>

</set>

La collection des OrderLines dans Order utilisera:

<set name="orderLines" inverse="true">

<key>

<column name="orderId"/>

<column name="customerId"/>

</key>

<one-to-many class="OrderLine"/>

</set>

(L'élément <one-to-many>, comme d'habitude, ne déclare pas de colonne.)

Si OrderLine lui-même possède une collection, celle-ci aura aussi une clécomposite étrangère.

<class name="OrderLine">

....

....

<list name="deliveryAttempts">

<key> <!-- a collection inherits the composite key type

-->

<column name="lineId"/>

<column name="orderId"/>

<column name="customerId"/>

</key>

<list-index column="attemptId" base="1"/>

<composite-element class="DeliveryAttempt">

...

</composite-element>

Page 171: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Composant Dynamique

Hibernate 3.3.1 157

</set>

</class>

8.5. Composant Dynamique

Vous pouvez même mapper une propriété de type Map:

<dynamic-component name="userAttributes">

<property name="foo" column="FOO" type="string"/>

<property name="bar" column="BAR" type="integer"/>

<many-to-one name="baz" class="Baz" column="BAZ_ID"/>

</dynamic-component>

La sémantique de l'association à un <dynamic-component> est identiqueà celle que l'on utilise pour les composants. L'avantage de ce type demapping est qu'il pemet de déterminer les véritables propriétés du bean aumoment su déploiement en éditant simplement le document de mapping. Lamanipulation du document de mapping pendant l'execution de l'applicationest aussi possible en utilisant un parser DOM. Il ya même mieux, vouspouvez accéder (et changer) le metamodel de configuration d'hibernate enutilisant l'objet Configuration

Page 172: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

158 Hibernate 3.3.1

Page 173: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 159

Chapitre 9. Mapping d'héritage declasse

9.1. Les trois stratégies

Hibernate supporte les trois stratégies d'héritage de base :

• une table par hiérarchie de classe (table per class hierarchy)

• table per subclass

• une table par classe concrète (table per concrete class)

Hibernate supporte en plus une quatrièmestratégie, légèrement différente,qui supporte le polymorphisme :

• le polymorphisme implicite

Il est possible d'utiliser différentes stratégies de mapping pour différentesbranches d'une même hiérarchie d'héritage, et alors d'employer lepolymorphisme implicite pour réaliser le polymorphisme à travers toute lahiérarchie. Pourtant, Hibernate ne supporte pas de mélanger des mappings<subclass> et <joined-subclass> et <union-subclass> pour le même élément<class> racine. Il est possible de mélanger ensemble les stratégies d'unetable par hiérarchie et d'une table par sous-classe, pour le même élément<class>, en combinant les éléments <subclass> et <join> (voir dessous).

Il est possible de définir des mappings de subclass, union-subclass, etjoined-subclass dans des documents de mapping séparés, directementsous hibernate-mapping. Ceci vous permet d'étendre une hiérarchie declasse juste en ajoutant un nouveau fichier de mapping. Vous devezspécifier un attribut extends dans le mapping de la sous-classe, en nommantune super-classe précédemment mappée. Note : précédemment cettefoncionnalité rendait l'ordre des documents de mapping important. DepuisHibernate3, l'ordre des fichier de mapping n'importe plus lors de l'utilisationdu mot-clef "extends". L'ordre à l'intérieur d'un simple fichier de mappingimpose encore de définir les classes mères avant les classes filles.

<hibernate-mapping>

<subclass name="DomesticCat" extends="Cat"

discriminator-value="D">

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

</subclass>

Page 174: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 9. Mapping d'héritage declasse

160 Hibernate 3.3.1

</hibernate-mapping>

9.1.1. Une table par hiérarchie de classe

Supposons que nous ayons une interface Payment, implémentée parCreditCardPayment, CashPayment, ChequePayment. La stratégie une table parhiérarchie serait :

<class name="Payment" table="PAYMENT">

<id name="id" type="long" column="PAYMENT_ID">

<generator class="native"/>

</id>

<discriminator column="PAYMENT_TYPE" type="string"/>

<property name="amount" column="AMOUNT"/>

...

<subclass name="CreditCardPayment" discriminator-value="CREDIT">

<property name="creditCardType" column="CCTYPE"/>

...

</subclass>

<subclass name="CashPayment" discriminator-value="CASH">

...

</subclass>

<subclass name="ChequePayment" discriminator-value="CHEQUE">

...

</subclass>

</class>

Une seule table est requise. Une grande limitation de cette stratégie est queles colonnes déclarées par les classes filles, telles que CCTYPE, ne peuventavoir de contrainte NOT NULL.

9.1.2. Une table par classe fille

La stratégie une table par classe fille serait :

<class name="Payment" table="PAYMENT">

<id name="id" type="long" column="PAYMENT_ID">

<generator class="native"/>

</id>

<property name="amount" column="AMOUNT"/>

...

<joined-subclass name="CreditCardPayment"

table="CREDIT_PAYMENT">

<key column="PAYMENT_ID"/>

<property name="creditCardType" column="CCTYPE"/>

...

</joined-subclass>

<joined-subclass name="CashPayment" table="CASH_PAYMENT">

<key column="PAYMENT_ID"/>

...

</joined-subclass>

Page 175: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Une table par classe fille, en utilisant undiscriminant

Hibernate 3.3.1 161

<joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">

<key column="PAYMENT_ID"/>

...

</joined-subclass>

</class>

Quatre tables sont requises. Les trois tables des classes filles ont une cléprimaire associée à la table classe mère (le modèle relationnel est uneassociation un-vers-un).

9.1.3. Une table par classe fille, en utilisant undiscriminant

Notez que l'implémentation Hibernate de la stratégie un table par classefille ne nécessite pas de colonne discriminante dans la table classe mère.D'autres implémentations de mappers Objet/Relationnel utilisent une autreimplémentation de la stratégie une table par classe fille qui nécessite unecolonne de type discriminant dans la table de la classe mère. L'approcheprise par Hibernate est plus difficile à implémenter mais plus correcte d'unepoint de vue relationnel. Si vous aimeriez utiliser une colonne discriminanteavec la stratégie d'une table par classe fille, vous pourriez combinerl'utilisation de <subclass> et <join>, comme suit :

<class name="Payment" table="PAYMENT">

<id name="id" type="long" column="PAYMENT_ID">

<generator class="native"/>

</id>

<discriminator column="PAYMENT_TYPE" type="string"/>

<property name="amount" column="AMOUNT"/>

...

<subclass name="CreditCardPayment" discriminator-value="CREDIT">

<join table="CREDIT_PAYMENT">

<key column="PAYMENT_ID"/>

<property name="creditCardType" column="CCTYPE"/>

...

</join>

</subclass>

<subclass name="CashPayment" discriminator-value="CASH">

<join table="CASH_PAYMENT">

<key column="PAYMENT_ID"/>

...

</join>

</subclass>

<subclass name="ChequePayment" discriminator-value="CHEQUE">

<join table="CHEQUE_PAYMENT" fetch="select">

<key column="PAYMENT_ID"/>

...

</join>

</subclass>

</class>

Page 176: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 9. Mapping d'héritage declasse

162 Hibernate 3.3.1

La déclaration optionnelle fetch="select" indique à Hibernate de ne pasrécupérer les données de la classe fille ChequePayment par une jointureexterne lors des requêtes sur la classe mère.

9.1.4. Mélange d'une table par hiérarchie de classe avecune table par classe fille

Vous pouvez même mélanger les stratégies d'une table par hiérarchie declasse et d'une table par classe fille en utilisant cette approche :

<class name="Payment" table="PAYMENT">

<id name="id" type="long" column="PAYMENT_ID">

<generator class="native"/>

</id>

<discriminator column="PAYMENT_TYPE" type="string"/>

<property name="amount" column="AMOUNT"/>

...

<subclass name="CreditCardPayment" discriminator-value="CREDIT">

<join table="CREDIT_PAYMENT">

<property name="creditCardType" column="CCTYPE"/>

...

</join>

</subclass>

<subclass name="CashPayment" discriminator-value="CASH">

...

</subclass>

<subclass name="ChequePayment" discriminator-value="CHEQUE">

...

</subclass>

</class>

Pour importe laquelle de ces stratégies, une association polymorphique versla classe racine Payment est mappée en utilisant <many-to-one>.

<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>

9.1.5. Une table par classe concrète

Il y a deux manières d'utiliser la stratégie d'une table par classe concrète. Lapremière est d'employer <union-subclass>.

<class name="Payment">

<id name="id" type="long" column="PAYMENT_ID">

<generator class="sequence"/>

</id>

<property name="amount" column="AMOUNT"/>

...

<union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">

<property name="creditCardType" column="CCTYPE"/>

Page 177: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Une table par classe concrète, en utilisantle polymorphisme implicite

Hibernate 3.3.1 163

...

</union-subclass>

<union-subclass name="CashPayment" table="CASH_PAYMENT">

...

</union-subclass>

<union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">

...

</union-subclass>

</class>

Trois tables sont nécessaires pour les classes filles. Chaque table définitdes colonnes pour toutes les propriétés de la classe, incluant les propriétéshéritéés.

La limitation de cette approche est que si une propriété est mappée sur laclasse mère, le nom de la colonne doit être le même pour toutes les classesfilles. (Nous pourrions être plus souple dans une future version d'Hibernate).La stratégie du générateur d'identifiant n'est pas permise dans l'héritage declasses filles par union, en effet la valeur (NdT : seed) de la clef primaire doitêtre partagée par toutes les classes filles "union" d'une hiérarchie.

Si votre classe mère est abstraite, mappez la avec abstract="true". Biensûr, si elle n'est pas abstraite, une table supplémentaire (par défaut, PAYMENTdans l'exemple ci-dessus) est requise pour contenir des instances de laclasse mère.

9.1.6. Une table par classe concrète, en utilisant lepolymorphisme implicite

Une approche alternative est l'emploi du polymorphisme implicite :

<class name="CreditCardPayment" table="CREDIT_PAYMENT">

<id name="id" type="long" column="CREDIT_PAYMENT_ID">

<generator class="native"/>

</id>

<property name="amount" column="CREDIT_AMOUNT"/>

...

</class>

<class name="CashPayment" table="CASH_PAYMENT">

<id name="id" type="long" column="CASH_PAYMENT_ID">

<generator class="native"/>

</id>

<property name="amount" column="CASH_AMOUNT"/>

...

</class>

<class name="ChequePayment" table="CHEQUE_PAYMENT">

<id name="id" type="long" column="CHEQUE_PAYMENT_ID">

Page 178: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 9. Mapping d'héritage declasse

164 Hibernate 3.3.1

<generator class="native"/>

</id>

<property name="amount" column="CHEQUE_AMOUNT"/>

...

</class>

Notez que nulle part nous ne mentionnons l'interface Payment explicitement.Notez aussi que des propriétés de Payment sont mappées dans chaqueclasse fille. Si vous voulez éviter des duplications, considérez l'utilisation desentités XML (cf. [ <!ENTITY allproperties SYSTEM "allproperties.xml"> ]dans la déclaration du DOCTYPE et &allproperties; dans le mapping).

L'inconvénient de cette approche est qu'Hibernate ne génère pas d'UNIONsSQL lors de l'exécution des requêtes polymorphiques.

Pour cette stratégie de mapping, une association polymorphique pourPayment est habituellement mappée en utilisant <any>.

<any name="payment" meta-type="string" id-type="long">

<meta-value value="CREDIT" class="CreditCardPayment"/>

<meta-value value="CASH" class="CashPayment"/>

<meta-value value="CHEQUE" class="ChequePayment"/>

<column name="PAYMENT_CLASS"/>

<column name="PAYMENT_ID"/>

</any>

9.1.7. Mélange du polymorphisme implicite avec d'autresmappings d'héritage

Il y a une chose supplémentaire à noter à propos de ce mapping. Puisqueles classes filles sont chacune mappées avec leur propre élément <class>(et puisque Payment est juste une interface), chaque classe fille pourraitfacilement faire partie d'une autre hiérarchie d'héritage ! (Et vous pouvezencore faire des requêtes polymorphiques pour l'interface Payment).

<class name="CreditCardPayment" table="CREDIT_PAYMENT">

<id name="id" type="long" column="CREDIT_PAYMENT_ID">

<generator class="native"/>

</id>

<discriminator column="CREDIT_CARD" type="string"/>

<property name="amount" column="CREDIT_AMOUNT"/>

...

<subclass name="MasterCardPayment" discriminator-value="MDC"/>

<subclass name="VisaPayment" discriminator-value="VISA"/>

</class>

<class name="NonelectronicTransaction" table="NONELECTRONIC_TXN">

<id name="id" type="long" column="TXN_ID">

<generator class="native"/>

Page 179: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Limitations

Hibernate 3.3.1 165

</id>

...

<joined-subclass name="CashPayment" table="CASH_PAYMENT">

<key column="PAYMENT_ID"/>

<property name="amount" column="CASH_AMOUNT"/>

...

</joined-subclass>

<joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT">

<key column="PAYMENT_ID"/>

<property name="amount" column="CHEQUE_AMOUNT"/>

...

</joined-subclass>

</class>

Encore une fois, nous ne mentionnons pas explicitement Payment. Si nousexécutons une requête sur l'interface Payment - par exemple, from Payment -Hibernate retournera automatiquement les instances de CreditCardPayment(et ses classes filles puisqu'elles implémentent aussi Payment), CashPayment etChequePayment mais pas les instances de NonelectronicTransaction.

9.2. Limitations

Il y a certaines limitations à l'approche du "polymorphisme implicite" pour lastratégie de mapping d'une table par classe concrète. Il y a plutôt moins delimitations restrictives aux mappings <union-subclass>.

La table suivante montre les limitations des mappings d'une table par classeconcrète, et du polymorphisme implicite, dans Hibernate.

Page 180: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 9. Mapping d'héritage declasse

166 Hibernate 3.3.1

Tableau 9.1. Caractéristiques du mapping d'héritage

Stratégied'héritage

many-to-one polymorphique

one-to-one polymorphique

one-to-many polymorphique

many-to-many polymorphique

Polymorphicload()/

get()

Requêtespolymorphiques

Jointurespolymorphiques

Récupérationpar jointureexterne

unetablepar hiérarchiedeclasse

<many-

to-one>

<one-

to-one>

<one-

to-

many>

<many-

to-

many>

s.get(Payment.class,

id)

from

Payment

p

from

Order

o join o.payment

p

supportée

tableper subclass

<many-

to-one>

<one-

to-one>

<one-

to-

many>

<many-

to-

many>

s.get(Payment.class,

id)

from

Payment

p

from

Order

o join o.payment

p

supportée

unetableparclasse concrète(union-subclass)

<many-

to-one>

<one-

to-one>

<one-

to-

many>

(for inverse="true"only)

<many-

to-

many>

s.get(Payment.class,

id)

from

Payment

p

from

Order

o join o.payment

p

supportée

unetableparclasse concrète(polymorphismeimplicite)

<any> not supportednot supported<many-

to-any>

s.createCriteria(Payment.class).add(

Restrictions.idEq(id)

).uniqueResult()

from

Payment

p

not supportednot supported

Page 181: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 167

Chapitre 10. Travailler avec desobjets

Hibernate est une solution de mapping objet/relationnel complète qui nemasque pas seulement au développpeur les détails du système de gestionde base de données sous-jacent, mais offre aussi la gestion d'état desobjets. C'est, contrairement à la gestion de statements SQL dans les couchesde persistance habituelles JDBC/SQL, une vue orientée objet très naturellede la persistance dans les applications Java.

En d'autres mots, les développeurs d'applications Hibernate devrait toujoursréfléchir à l'état de leurs objets, et pas nécessairement à l'exécution desexpressions SQL. Cette part est prise en charge pas Hibernate et seulementimportante pour les développeurs d'applications lors du réglage de laperformance de leur système.

10.1. États des objets Hibernate

Hibernate définit et comprend les états suivants :

• Éphémère (NdT : transient) - un objet est éphémère s'il a juste étéinstancié en utilisant l'opérateur new. Il n'a aucune représentationpersistante dans la base de données et aucune valeur d'identifiantn'a été assignée. Les instances éphémères seront détruites par leramasse-miettes si l'application n'en conserve aucune référence. Utilisezla Session d'Hibernate pour rendre un objet persistant (et laisser Hibernates'occuper des expressions SQL qui ont besoin d'être exécutées pour cettetransistion).

• Persistant - une instance persistante a une représentation dans labase de données et une valeur d'identifiant. Elle pourrait avoir juste étésauvegardée ou chargée, pourtant, elle est par définition dans la portéed'une Session. Hibernate détectera n'importe quels changements effectuéssur un objet dans l'état persistant et synchronisera l'état avec la base dedonnées lors de la fin l'unité de travail. Les développeurs n'exécutent pasd'expressions UPDATE ou DELETE manuelles lorsqu'un objet devrait êtrerendu éphémère.

• Détaché - une instance détachée est un objet qui a été persistant, maisdont sa Session a été fermée. La référence à l'objet est encore valide,bien sûr, et l'instance détachée pourrait même être modifiée dans cetétat. Une instance détachée peut être réattachée à une nouvelle Sessionplus tard dans le temps, la rendant (et toutes les modifications avec)

Page 182: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 10. Travailler avec des objets

168 Hibernate 3.3.1

de nouveau persistante. Cette fonctionnalité rend possible un modèlede programmation pour de longues unités de travail qui requièrent untemps de réflexion de l'utilisateur. Nous les appelons des conversations,c'est-à-dire une unité de travail du point de vue de l'utilisateur.

Nous alons maintenant dicuster des états et des transitions d'état (et desméthodes d'Hibernate qui déclenchent une transition) plus en détails.

10.2. Rendre des objets persistants

Les instances nouvellement instanciées d'une classe persistante sontconsidérées éphémères par Hibernate. Nous pouvons rendre une instanceéphémère persistante en l'associant avec une session :

DomesticCat fritz = new DomesticCat();

fritz.setColor(Color.GINGER);

fritz.setSex('M');

fritz.setName("Fritz");

Long generatedId = (Long) sess.save(fritz);

Si Cat a un identifiant généré, l'identifiant est généré et assigné au catlorsque save() est appelée. Si Cat a un identifiant assigned, ou une clefcomposée, l'identifiant devrait être assigné à l'instance de cat avantd'appeler save(). Vous pouvez aussi utiliser persist() à la place desave(),avec la sémantique définie plus tôt dans le brouillon d'EJB3.

• persist() makes a transient instance persistent. However, it doesn'tguarantee that the identifier value will be assigned to the persistentinstance immediately, the assignment might happen at flush time.persist() also guarantees that it will not execute an INSERT statement if itis called outside of transaction boundaries. This is useful in long-runningconversations with an extended Session/persistence context.

• save() does guarantee to return an identifier. If an INSERT has to beexecuted to get the identifier ( e.g. "identity" generator, not "sequence"),this INSERT happens immediately, no matter if you are inside or outsideof a transaction. This is problematic in a long-running conversation with anextended Session/persistence context.

Alternativement, vous pouvez assigner l'identifiant en utilisant une versionsurchargée de save().

DomesticCat pk = new DomesticCat();

pk.setColor(Color.TABBY);

pk.setSex('F');

pk.setName("PK");

pk.setKittens( new HashSet() );

pk.addKitten(fritz);

Page 183: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chargement d'un objet

Hibernate 3.3.1 169

sess.save( pk, new Long(1234) );

Si l'objet que vous rendez persistant a des objets associés (par exemple, lacollection kittens dans l'exemple précédent), ces objets peuvent être renduspersistants dans n'importe quel ordre que vous souhaitez à moins que vousayez une contrainte NOT NULL sur la colonne de la clef étrangère. Il n'y ajamais de risque de violer une contrainte de clef étrangère. Cependant, vouspourriez violer une contrainte NOT NULL si vous appeliez save() sur les objetsdans le mauvais ordre.

Habituellement, vous ne vous préoccupez pas de ce détail, puisque vousutiliserez très probablement la fonctionnalité de persistance transitived'Hibernate pour sauvegarder les objets associés automatiquement. Alors,même les violations de contrainte NOT NULL n'ont plus lieu - Hibernate prendrasoin de tout. La persistance transitive est traitée plus loin dans ce chapitre.

10.3. Chargement d'un objet

Les méthodes load() de Session vous donnent un moyen de récupérer uneinstance persistante si vous connaissez déjà son identifiant. load() prend unobjet de classe et chargera l'état dans une instance nouvellement instanciéede cette classe, dans un état persistant.

Cat fritz = (Cat) sess.load(Cat.class, generatedId);

// you need to wrap primitive identifiers

long id = 1234;

DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new

Long(id) );

Alternativement, vous pouvez charger un état dans une instance donnée :

Cat cat = new DomesticCat();

// load pk's state into cat

sess.load( cat, new Long(pkId) );

Set kittens = cat.getKittens();

Notez que load() lèvera une exception irrécupérable s'il n'y a pas de lignecorrespondante dans la base de données. Si la classe est mappée avec unproxy, load() retourne juste un proxy non initialisé et n'accède en fait pas àla base de données jusqu'à ce que vous invoquiez une méthode du proxy.Ce comportement est très utile si vous souhaitez créer une associationvers un objet sans réellement le charger à partir de la base de données.Cela permet aussi à de multiples instances d'être chargées comme un lot sibatch-size est défini pour le mapping de la classe.

Page 184: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 10. Travailler avec des objets

170 Hibernate 3.3.1

Si vous n'êtes pas certain qu'une ligne correspondante existe, vousdevriez utiliser la méthode get(), laquelle accède à la base de donnéesimmédiatement et retourne null s'il n'y a pas de ligne correspondante.

Cat cat = (Cat) sess.get(Cat.class, id);

if (cat==null) {

cat = new Cat();

sess.save(cat, id);

}

return cat;

Vous pouvez même charger un objet en employant un SELECT ... FORUPDATE SQL, en utilisant un LockMode. Voir la documentation de l'API pourplus d'informations.

Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);

Notez que n'importe quelles instances associées ou collections contenuesne sont pas sélectionnées par FOR UPDATE, à moins que vous ne décidiez despécifier lock ou all en tant que style de cascade pour l'association.

Il est possible de re-charger un objet et toutes ses collections à n'importequel moment, en utilisant la méthode refresh(). C'est utile lorsque des"triggers" de base de données sont utilisés pour initiliser certains propriétésde l'objet.

sess.save(cat);

sess.flush(); //force the SQL INSERT

sess.refresh(cat); //re-read the state (after the trigger executes)

Une question importante apparaît généralement à ce point : combien (NdT: de données) Hibernate charge-t-il de la base de données et combient deSELECTs utilisera-t-il ? Cela dépent de la stratégie de récupération et cela estexpliqué dans Section 19.1, « Stratégies de chargement ».

10.4. Requêtage

Si vous ne connaissez par les identifiants des objets que vous recherchez,vous avez besoin d'une requête. Hibernate supporte un langage derequêtes orientées objet facile à utiliser mais puissant. Pour la création derequêtes par programmation, Hibernate supporte une fonction de requêtagesophistiqué Criteria et Example (QBC et QBE). Vous pouvez aussi exprimezvotre requête dans le SQL natif de votre base de données, avec un supportoptionnel d'Hibernate pour la conversion des ensembles de résultats enobjets.

Page 185: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Exécution de requêtes

Hibernate 3.3.1 171

10.4.1. Exécution de requêtes

Les requêtes HQL et SQL natives sont représentées avec une instancede org.hibernate.Query. L'interface offre des méthodes pour la liaison desparamètres, la gestion des ensembles de resultats, et pour l'exécution dela requête réelle. Vous obtenez toujours une Query en utilisant la Sessioncourante :

List cats = session.createQuery(

"from Cat as cat where cat.birthdate < ?")

.setDate(0, date)

.list();

List mothers = session.createQuery(

"select mother from Cat as cat join cat.mother as mother where

cat.name = ?")

.setString(0, name)

.list();

List kittens = session.createQuery(

"from Cat as cat where cat.mother = ?")

.setEntity(0, pk)

.list();

Cat mother = (Cat) session.createQuery(

"select cat.mother from Cat as cat where cat = ?")

.setEntity(0, izi)

.uniqueResult();]]

Query mothersWithKittens = (Cat) session.createQuery(

"select mother from Cat as mother left join fetch

mother.kittens");

Set uniqueMothers = new HashSet(mothersWithKittens.list());

Une requête est généralement exécutée en invoquant list(), le résultat dela requête sera chargée complètement dans une collection en mémoire. Lesintances d'entités recupérées par une requête sont dans un état persistant.La méthode uniqueResult() offre un raccourci si vous savez que votrerequête retournera seulement un seul objet.

10.4.1.1. Itération de résultats

Occasionnellement, vous pourriez être capable d'obtenir de meilleuresperformances en exécutant la requête avec la méthode iterate(). Ce seragénéralement seulement le cas si vous espérez que les intances réellesd'entité retournées par la requête soient déjà chargées dans la session ou lecache de second niveau. Si elles ne sont pas cachées, iterate() sera pluslent que list() et pourrait nécessiter plusieurs accès à la base de donnéespour une simple requête, généralement 1 pour le select initial qui retourne

Page 186: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 10. Travailler avec des objets

172 Hibernate 3.3.1

seulement les identifiants, et n selects supplémentaires pour initialiser lesinstances réelles.

// fetch ids

Iterator iter = sess.createQuery("from eg.Qux q order by

q.likeliness").iterate();

while ( iter.hasNext() ) {

Qux qux = (Qux) iter.next(); // fetch the object

// something we couldnt express in the query

if ( qux.calculateComplicatedAlgorithm() ) {

// delete the current instance

iter.remove();

// dont need to process the rest

break;

}

}

10.4.1.2. Requêtes qui retournent des tuples

Les requêtes d'Hibernate retournent parfois des tuples d'objets, auquel caschaque tuple est retourné comme un tableau :

Iterator kittensAndMothers = sess.createQuery(

"select kitten, mother from Cat kitten join

kitten.mother mother")

.list()

.iterator();

while ( kittensAndMothers.hasNext() ) {

Object[] tuple = (Object[]) kittensAndMothers.next();

Cat kitten = (Cat) tuple[0];

Cat mother = (Cat) tuple[1];

....

}

10.4.1.3. Résultats scalaires

Des requêtes peuvent spécifier une propriété d'une classe dans la clauseselect. Elles peuvent même appeler des fonctions d'aggrégat SQL. Lespropriétés ou les aggrégats sont considérés comme des résultats "scalaires"(et pas des entités dans un état persistant).

Iterator results = sess.createQuery(

"select cat.color, min(cat.birthdate), count(cat) from Cat

cat " +

"group by cat.color")

.list()

.iterator();

while ( results.hasNext() ) {

Object[] row = (Object[]) results.next();

Page 187: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Exécution de requêtes

Hibernate 3.3.1 173

Color type = (Color) row[0];

Date oldest = (Date) row[1];

Integer count = (Integer) row[2];

.....

}

10.4.1.4. Lier des paramètres

Des méthodes de Query sont fournies pour lier des valeurs à des paramètresnommés ou à des paramètres de style JDBC ?. Contrairement à JDBC, lesnuméros des paramètres d'Hibernate commencent à zéro. Les paramètresnommés sont des identifiants de la forme :nom dans la chaîne de caractèresde la requête. Les avantages des paramètres nommés sont :

• les paramètres nommés sont insensibles à l'ordre de leur place dans lachaîne de la requête

• ils peuvent apparaître plusieurs fois dans la même requête• ils sont auto-documentés

//named parameter (preferred)

Query q = sess.createQuery("from DomesticCat cat where cat.name =

:name");

q.setString("name", "Fritz");

Iterator cats = q.iterate();

//positional parameter

Query q = sess.createQuery("from DomesticCat cat where cat.name =

?");

q.setString(0, "Izi");

Iterator cats = q.iterate();

//named parameter list

List names = new ArrayList();

names.add("Izi");

names.add("Fritz");

Query q = sess.createQuery("from DomesticCat cat where cat.name in

(:namesList)");

q.setParameterList("namesList", names);

List cats = q.list();

10.4.1.5. Pagination

Si vous avez besoin de spécifier des liens sur votre ensemble de résultats(le nombre maximum de lignes que vous voulez récupérez et/ou la premièreligne que vous voulez récupérer) vous devriez utiliser des méthodes del'interface Query :

Query q = sess.createQuery("from DomesticCat cat");

q.setFirstResult(20);

Page 188: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 10. Travailler avec des objets

174 Hibernate 3.3.1

q.setMaxResults(10);

List cats = q.list();

Hibernate sait comment traduite cette requête de limite en SQL natif pourvotre SGBD.

10.4.1.6. Itération "scrollable"

Si votre connecteur JDBC supporte les ResultSets "scrollables", l'interfaceQuery peut être utilisée pour obtenir un objet ScrollableResults, lequelpermet une navigation flexible dans les résultats de la requête.

Query q = sess.createQuery("select cat.name, cat from DomesticCat

cat " +

"order by cat.name");

ScrollableResults cats = q.scroll();

if ( cats.first() ) {

// find the first name on each page of an alphabetical list of

cats by name

firstNamesOfPages = new ArrayList();

do {

String name = cats.getString(0);

firstNamesOfPages.add(name);

}

while ( cats.scroll(PAGE_SIZE) );

// Now get the first page of cats

pageOfCats = new ArrayList();

cats.beforeFirst();

int i=0;

while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add(

cats.get(1) );

}

cats.close()

Notez qu'une connexion ouverte (et un curseur) est requise pour cettefonctionnalité, utilisez setMaxResult()/setFirstResult() si vous avez besoind'une fonctionnalité de pagination hors ligne.

10.4.1.7. Externaliser des requêtes nommées

Vous pouvez aussi définir des requêtes nommées dans le document demapping. (Souvenez-vous d'utiliser une section CDATA si votre requêtecontient des caractères qui pourraient être interprétés comme des élémentsXML.)

<query name="ByNameAndMaximumWeight"><![CDATA[

from eg.DomesticCat as cat

Page 189: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Filtrer des collections

Hibernate 3.3.1 175

where cat.name = ?

and cat.weight > ?

] ]></query>

La liaison de paramètres et l'exécution sont fait par programmation :

Query q = sess.getNamedQuery("ByNameAndMaximumWeight");

q.setString(0, name);

q.setInt(1, minWeight);

List cats = q.list();

Notez que le code réel du programme est indépendant du langage derequête qui est utilisé, vous pouvez aussi définir des requêtes SQL nativezdans les méta-données, ou migrer des requêtes existantes vers Hibernate enles plaçant dans les fichiers de mapping.

UNTRANSLATED! Also note that a query declaration inside a<hibernate-mapping> element requires a global unique name for thequery, while a query declaration inside a <class> element is made uniqueautomatically by prepending the fully qualified name of the class, for exampleeg.Cat.ByNameAndMaximumWeight.

10.4.2. Filtrer des collections

Un filtre de collection est un type spécial de requête qui peut être appliquéà une collection persistante ou à un tableau. La chaîne de requête peut seréférer à this, correspondant à l'élément de la collection courant.

Collection blackKittens = session.createFilter(

pk.getKittens(),

"where this.color = ?")

.setParameter( Color.BLACK,

Hibernate.custom(ColorUserType.class) )

.list()

);

La collection retournée est considérée comme un bag, et c'est une copie dela collection donnée. La collection originale n'est pas modifiée (c'est contraireà l'implication du nom "filtre"; mais cohérent avec le comportement attendu).

Observez que les filtres ne nécessitent pas une clause from (bien qu'ilspuissent en avoir une si besoin est). Les filtres ne sont pas limités à retournerdes éléments de la collection eux-mêmes.

Collection blackKittenMates = session.createFilter(

pk.getKittens(),

"select this.mate where this.color = eg.Color.BLACK.intValue")

.list();

Page 190: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 10. Travailler avec des objets

176 Hibernate 3.3.1

Même une requête de filtre vide est utile, par exemple pour charger unsous-ensemble d'éléments dans une énorme collection :

Collection tenKittens = session.createFilter(

mother.getKittens(), "")

.setFirstResult(0).setMaxResults(10)

.list();

10.4.3. Requêtes Criteria

HQL est extrêmement puissant mais certains développeurs préfèrentconstruire des requêtes dynamiquement, en utilisant l'API orientée objet,plutôt que construire des chaînes de requêtes. Hibernate fournit une APIintuitive de requête Criteria pour ces cas :

Criteria crit = session.createCriteria(Cat.class);

crit.add( Restrictions.eq( "color", eg.Color.BLACK ) );

crit.setMaxResults(10);

List cats = crit.list();

Les APIs Criteria et Example associé sont traitées plus en détail dansChapitre 15, Requêtes par critères.

10.4.4. Requêtes en SQL natif

Vous pouvez exprimer une requête en SQL, en utilisant createSQLQuery() etlaisser Hibernate s'occuper du mapping des résultats vers des objets. Notezque vous pouvez n'importe quand appeler session.connection() et utiliserdirectement la Connection JDBC. Si vous choisissez d'utiliser l'API Hibernate,vous devez mettre les alias SQL entre accolades :

List cats = session.createSQLQuery("SELECT {cat.*} FROM CAT {cat}

WHERE ROWNUM<10")

.addEntity("cat", Cat.class)

.list();

List cats = session.createSQLQuery(

"SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +

"{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class},

... " +

"FROM CAT {cat} WHERE ROWNUM<10")

.addEntity("cat", Cat.class)

.list()

Les requêtes SQL peuvent contenir des paramètres nommés et positionnels,comme des requêtes Hibernate. Plus d'informations à propos des requêtesSQL natives dans Hibernate peuvent être trouvées dans Chapitre 16, SQLnatif.

Page 191: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Modifier des objets persistants

Hibernate 3.3.1 177

10.5. Modifier des objets persistants

Les instances persistantes transactionnelles (c'est-à-dire des objets chargés,sauvegardés, créés ou requêtés par la Session) peuvent être manipulées parl'application et n'importe quel changement vers l'état persistant sera persistélorsque la Session est "flushée" (traité plus tard dans ce chapitre). Il n'y a pasbesoin d'appeler une méthode particulière (comme update(), qui a un butdifférent) pour rendre vos modifications persistantes. Donc la manière la plusdirecte de mettre à jour l'état d'un objet est de le charger avec load(), et puisle manipuler directement, tant que la Session est ouverte :

DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69)

);

cat.setName("PK");

sess.flush(); // changes to cat are automatically detected and

persisted

Parfois ce modèle de programmation est inefficace puisqu'il nécessiterait unSELECT SQL (pour charger l'objet) et un UPDATE SQL (pour persister son étatmis à jour) dans la même session. Aussi Hibernate offre une autre approche,en utilisant des instances détachées.

Note that Hibernate does not offer its own API for direct execution of UPDATEor DELETE statements. Hibernate is a state management service, you don'thave to think in statements to use it. JDBC is a perfect API for executingSQL statements, you can get a JDBC Connection at any time by callingsession.connection(). Furthermore, the notion of mass operations conflictswith object/relational mapping for online transaction processing-orientedapplications. Future versions of Hibernate may however provide specialmass operation functions. See Chapitre 13, Traitement par paquet for somepossible batch operation tricks.

10.6. Modifier des objets détachés

Beaucoup d'applications ont besoin de récupérer un objet dans unetransaction, l'envoyer à la couche interfacée avec l'utilisateur pour lesmanipulations, puis sauvegarder les changements dans une nouvelletransaction. Les applications qui utilisent cette approche dans unenvironnement à haute concurrence utilisent généralement des donnéesversionnées pour assurer l'isolation pour les "longues" unités de travail.

Hibernate supporte ce modèle en permettant pour le réattachementd'instances détachées l'utilisation des méthodes Session.update() ouSession.merge() :

// in the first session

Page 192: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 10. Travailler avec des objets

178 Hibernate 3.3.1

Cat cat = (Cat) firstSession.load(Cat.class, catId);

Cat potentialMate = new Cat();

firstSession.save(potentialMate);

// in a higher layer of the application

cat.setMate(potentialMate);

// later, in a new session

secondSession.update(cat); // update cat

secondSession.update(mate); // update mate

Si le Cat avec l'identifiant catId avait déjà été chargé par secondSessionlorsque l'application a essayé de le réattacher, une exception aurait étélevée.

Utilisez update() si vous êtes sure que la session ne contient pas déjà uneinstance persistante avec le même identifiant, et merge() si vous voulezfusionner vos modifications n'importe quand sans considérer l'état de lasession. En d'autres mots, update() est généralement la première méthodeque vous devriez appeler dans une session fraîche, pour s'assurer que leréattachement de vos instances détachées est la première opération qui estexécutée.

L'application devrait individuellement update() (NdT : mettre à jour) lesinstances détachées accessibles depuis l'instance détachée donnée si etseulement si elle veut que leur état soit aussi mis à jour. Ceci peut êtreautomatisé bien sûr, en utilisant la persistance transitive, voir Section 10.11,« Persistance transitive ».

La méthode lock() permet aussi à une application de réassocier un objetavec une nouvelle session. Pourtant, l'instance détachée doit être nonmodifiée !

//just reassociate:

sess.lock(fritz, LockMode.NONE);

//do a version check, then reassociate:

sess.lock(izi, LockMode.READ);

//do a version check, using SELECT ... FOR UPDATE, then reassociate:

sess.lock(pk, LockMode.UPGRADE);

Notez que lock() peut être utilisé avec différents LockModes, voir ladocumentation de l'API documentation et le chapitre sur la gestion destransactions pour plus d'informations. Le réattachement n'est pas le seul casd'utilisation pour lock().

D'autres modèles pour de longues unités de travail sont traités dansSection 11.3, « Contrôle de consurrence optimiste ».

Page 193: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Détection automatique d'un état

Hibernate 3.3.1 179

10.7. Détection automatique d'un état

Les utilisateurs d'Hibernate ont demandé une méthode dont l'intentiongénérale serait soit de sauvegarder une instance éphémère en générantun nouvel identifiant, soit mettre à jour/réattacher les instances détachéesassociées à l'identifiant courant. La méthode saveOrUpdate() implémentecette fonctionnalité.

// in the first session

Cat cat = (Cat) firstSession.load(Cat.class, catID);

// in a higher tier of the application

Cat mate = new Cat();

cat.setMate(mate);

// later, in a new session

secondSession.saveOrUpdate(cat); // update existing state (cat has

a non-null id)

secondSession.saveOrUpdate(mate); // save the new instance (mate

has a null id)

L'usage et la sémantique de saveOrUpdate() semble être confuse pour lesnouveaux utilisateurs. Premièrement, aussi longtemps que vous n'essayezpas d'utiliser des instances d'une session dans une autre, vous ne devriezpas avoir besoin d'utiliser update(), saveOrUpdate(), ou merge(). Certainesapplications n'utiliseront jamais ces méthodes.

Généralement update() ou saveOrUpdate() sont utilisées dans le scénariosuivant :

• l'application charge un objet dans la première session

• l'objet est passé à la couche utilisateur

• certaines modifications sont effectuées sur l'objet

• l'objet est retourné à la couche logique métier

• l'application persiste ces modifications en appelant update() dans uneseconde sessin

saveOrUpdate() s'utilise dans le cas suivant :

• si l'objet est déjà persistant dans cette session, ne rien faire

• si un autre objet associé à la session a le même identifiant, lever uneexception

• si l'objet n'a pas de propriété d'identifiant, appeler save()

• si l'identifiant de l'objet a une valeur assignée à un objet nouvellementinstancié, appeler save()

Page 194: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 10. Travailler avec des objets

180 Hibernate 3.3.1

• si l'objet est versionné (par <version> ou <timestamp>), et la valeur de lapropriété de version est la même valeur que celle assignée à un objetnouvellement instancié, appeler save()

• sinon mettre à jour l'objet avec update()

et merge() est très différent :

• s'il y a une instance persistante avec le même identifiant courammentassociée à la session, copier l'état de l'objet donné dans l'instancepersistante

• s'il n'y a pas d'instance persistante associée à cette session, essayer dele charger à partir de la base de données, ou créer une nouvelle instancepersistante

• l'instance persistante est retournée• l'instance donnée ne devient pas associée à la session, elle reste

détachée

10.8. Suppression d'objets persistants

Session.delete() supprimera l'état d'un objet de la base de données. Biensûr, votre application pourrait encore conserver une référence vers un objeteffacé. Il est mieux de penser à delete() comme rendant une instancepersistante éphémère.

sess.delete(cat);

Vous pouvez effacer des objets dans l'ordre que vous voulez, sans risque deviolations de contrainte de clef étrangère. Il est encore possible de violer unecontrainte NOT NULL sur une colonne de clef étrangère en effaçant des objetsdans le mauvais ordre, par exemple si vous effacer le parent, mais oubliezd'effacer les enfants.

10.9. Réplication d'objets entre deux entrepôts dedonnées

Il est occasionnellement utile de pouvoir prendre un graphe d'instancespersistantes et de les rendre persistantes dans un entrepôt différent, sansregénérer les valeurs des identifiants.

//retrieve a cat from one database

Session session1 = factory1.openSession();

Transaction tx1 = session1.beginTransaction();

Cat cat = session1.get(Cat.class, catId);

tx1.commit();

session1.close();

Page 195: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Flush de la session

Hibernate 3.3.1 181

//reconcile with a second database

Session session2 = factory2.openSession();

Transaction tx2 = session2.beginTransaction();

session2.replicate(cat, ReplicationMode.LATEST_VERSION);

tx2.commit();

session2.close();

Le ReplicationMode détermine comment replicate() traitera les conflits avecles lignes existantes dans la base de données.

• ReplicationMode.IGNORE - ignore l'objet s'il y a une ligne existante dans labase de données avec le même identifiant

• ReplicationMode.OVERWRITE - écrase n'importe quelle ligne existante dansla base de données avec le même identifiant

• ReplicationMode.EXCEPTION - lève une exception s'il y une ligne dans labase de données avec le même identifiant

• ReplicationMode.LATEST_VERSION - écrase la ligne si son numéro de versionest plus petit que le numéro de version de l'objet, ou ignore l'objet sinon

Les cas d'utilisation de cette fonctionnalité incluent la réconciliation dedonnées entrées dans différentes base de données, l'extension desinformations de configuration du système durant une mise à jour du produit,retour en arrière sur les changements effectués durant des transactionsnon-ACID, et plus.

10.10. Flush de la session

De temps en temps la Session exécutera les expressions SQL requises poursyncrhoniser l'état de la connexion JDBC avec l'état des objets retenus enmémoire. Ce processus, flush, arrive par défaut aux points suivants :

• lors de certaines exécutions de requête• lors d'un appel à org.hibernate.Transaction.commit()• lors d'un appel à Session.flush()

Les expressions SQL sont effectuées dans l'ordre suivant :

1. insertion des entités, dans le même ordre que celui des objetscorrespondants sauvegardés par l'appel à Session.save()

2. mise à jours des entités3. suppression des collections4. suppression, mise à jour et insertion des éléments des collections5. insertion des collections6. suppression des entités, dans le même ordre que celui des objets

correspondants qui ont été supprimés par l'appel à Session.delete()

Page 196: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 10. Travailler avec des objets

182 Hibernate 3.3.1

(Une exception est que des objets utilisant la génération native d'identifiantssont insérés lorsqu'ils sont sauvegardés.)

Excepté lorsque vous appelez flush() explicitement, il n'y absolumentaucune garantie à propos de quand la Session exécute les appels JDBC,seulement sur l'ordre dans lequel ils sont exécutés. Cependant, Hibernategarantit que Query.list(..) ne retournera jamais de données périmées, nides données fausses.

Il est possible de changer le comportement par défaut, donc que le flushse produise moins fréquemment. La classe FlushMode définit trois modesdifférents : flush seulement lors du commit (et seulement quand l'APITransaction d'Hibernate est utilisée), flush automatiquement en utilisant laprocédure expliquée, ou jamais de flush à moins que flush() soit appeléeexplicitement. Le dernier mode est utile pour l'exécution de longues unitésde travail, où une Session est gardée ouverte et déconnectée pour un longmoment (voir Section 11.3.2, « Les sessions longues et le versionnageautomatique. »).

sess = sf.openSession();

Transaction tx = sess.beginTransaction();

sess.setFlushMode(FlushMode.COMMIT); // allow queries to return

stale state

Cat izi = (Cat) sess.load(Cat.class, id);

izi.setName(iznizi);

// might return stale data

sess.find("from Cat as cat left outer join cat.kittens kitten");

// change to izi is not flushed!

...

tx.commit(); // flush occurs

sess.close();

Durant le flush, une exception peut se produire (par exemple, si uneopération de la DML viole une contrainte). Puisque les exceptions de gestionimpliquent une certaine compréhension du comportement transactionneld'Hibernate, nous le traitons dans Chapitre 11, Transactions et accèsconcurrents.

10.11. Persistance transitive

Il est assez pénible de sauvegarder, supprimer, ou réattacher des objets unpar un, surtout si vous traitez un graphe d'objets associés. Un cas habituelest une relation parent/enfant. Considérez l'exemple suivant :

Page 197: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Persistance transitive

Hibernate 3.3.1 183

Si les enfants de la relation parent/enfant étaient des types de valeur (parexemple, une collection d'adresses ou de chaînes de caractères), leurcycle de vie dépendraient du parent et aucune action ne serait requise pour"cascader" facilement les changements d'état. Si le parent est sauvegardé,les objets enfants de type de valeur sont sauvegardés également, si leparent est supprimé, les enfants sont supprimés, etc. Ceci fonctionne mêmepour des opérations telles que la suppression d'un enfant de la collection ;Hibernate détectera cela et, puisque les objets de type de valeur ne peuventpas avoir des références partagées, supprimera l'enfant de la base dedonnées.

Maintenant considérez le même scénario avec un parent et dont les objetsenfants sont des entités, et non des types de valeur (par exemple, descatégories et des objets, ou un parent et des chatons). Les entités ont leurpropre cycle de vie, supportent les références partagées (donc supprimerune entité de la collection ne signifie pas qu'elle peut être supprimée), et il n'ya par défaut pas de cascade d'état d'une entité vers n'importe quelle entitéassociée. Hibernate n'implémente pas la persistance par accessibilité pardéfaut.

Pour chaque opération basique de la session d'Hibernate - incluantpersist(), merge(), saveOrUpdate(), delete(), lock(), refresh(),

evict(), replicate() - il y a un style de cascade correspondant.Respectivement, les styles de cascade s'appellent persist, merge,save-update, delete, lock, refresh, evict, replicate. Si vous voulezqu'une opération soit cascadée le long d'une association, vous devezl'indiquer dans le document de mapping. Par exemple :

<one-to-one name="person" cascade="persist"/>

Les styles de cascade peuvent être combinés :

<one-to-one name="person" cascade="persist,delete,lock"/>

Vous pouvez même utiliser cascade="all" pour spécifier que toutes lesopérations devraient être cascadées le long de l'association. La valeur pardéfaut cascade="none" spécifie qu'aucune opération ne sera cascadée.

Une style de cascade spécial, delete-orphan, s'applique seulement auxassociations un-vers-plusieurs, et indique que l'opération delete() devraitêtre appliquée à n'importe quel enfant qui est supprimé de l'association.

Recommandations :

Page 198: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 10. Travailler avec des objets

184 Hibernate 3.3.1

• Cela n'a généralement aucun sens d'activer la cascade sur uneassociation <many-to-one> ou <many-to-many>. Les cascades sont souventutiles pour des associations <one-to-one> et <one-to-many>.

• Si la durée de vie de l'objet enfant est liée à la durée de vie del'objet parent, faites en un objet du cycle de vie en spécifiantcascade="all,delete-orphan".

• Sinon, vous pourriez ne pas avoir besoin de cascade du tout.Mais si vous pensez que vous travaillerez souvent avec le parentet les enfants ensemble dans la même transaction, et que vousvoulez vous éviter quelques frappes, considérez l'utilisation decascade="persist,merge,save-update".

Mapper une association (soit une simple association valuée, soit unecollection) avec cascade="all" marque l'association comme une relationde style parent/enfant où la sauvegarde/mise à jour/suppression du parententraîne la sauvegarde/mise à jour/suppression de l'enfant ou des enfants.

En outre, une simple référence à un enfant d'un parent persistant aura pourconséquence la sauvegarde/mise à jour de l'enfant. Cette métaphore estcependant incomplète. Un enfant qui devient non référencé par son parentn'est pas automatiquement supprimée, excepté dans le cas d'une association<one-to-many> mappée avec cascade="delete-orphan". La sémantiqueprécise des opérations de cascade pour une relation parent/enfant est lasuivante :

• Si un parent est passé à persist(), tous les enfant sont passés àpersist()

• Si un parent est passé à merge(), tous les enfants sont passés à merge()

• Si un parent est passé à save(), update() ou saveOrUpdate(), tous lesenfants sont passés à saveOrUpdate()

• Si un enfant détaché ou éphémère devient référencé par un parentpersistant, il est passé à saveOrUpdate()

• Si un parent est supprimé, tous les enfants sont passés à delete()

• Si un enfant est déréférencé par un parent persistant, rien de spécialn'arrive - l'application devrait explicitement supprimer l'enfant si nécessaire- à moins que cascade="delete-orphan" soit paramétré, au quel cas l'enfant"orphelin" est supprimé.

Enfin, la cascade des opérations peut être effectuée sur un graphe donnélors de l'appel de l'opération or lors du flush suivant. Toutes les opérations,lorsque cascadées, le sont sur toutes les entités associées atteignableslorsque l'opétation est exécutée. Cependant save-upate et delete-orphansont cascadées à toutes les entités associées atteignables lors du flush de laSession.

Page 199: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Utilisation des méta-données

Hibernate 3.3.1 185

10.12. Utilisation des méta-données

Hibernate requiert un modèle de méta-niveau très riche de toutes les entitéset types valués. De temps en temps, ce modèle est très utile à l'applicationelle même. Par exemple, l'application pourrait utiliser les méta-donnéesd'Hibernate pour implémenter un algorithme de copie en profondeur"intelligent" qui comprendrait quels objets devraient copiés (par exemple lestypes de valeur mutables) et lesquels ne devraient pas l'être (par exemple lestypes de valeurs immutables et, possiblement, les entités associées).

Hibernate expose les méta-données via les interfaces ClassMetadata etCollectionMetadata et la hiérarchie Type. Les instances des interfaces deméta-données peuvent être obtenues à partir de la SessionFactory.

Cat fritz = ......;

ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);

Object[] propertyValues = catMeta.getPropertyValues(fritz);

String[] propertyNames = catMeta.getPropertyNames();

Type[] propertyTypes = catMeta.getPropertyTypes();

// get a Map of all properties which are not collections or

associations

Map namedValues = new HashMap();

for ( int i=0; i<propertyNames.length; i++ ) {

if ( !propertyTypes[i].isEntityType() &&

!propertyTypes[i].isCollectionType() ) {

namedValues.put( propertyNames[i], propertyValues[i] );

}

}

Page 200: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

186 Hibernate 3.3.1

Page 201: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 187

Chapitre 11. Transactions et accèsconcurrents

L'un des principaux avantages du mécanisme de contrôle des accèsconcurrents d'Hibernate est qu'il est très facile à comprendre. Hibernateutilise directement les connexions JDBC ainsi que les ressources JTA sansy ajouter davantage de mécanisme de blocage. Nous vous recommandonsde vous familiariser avec les spécifications JDBC, ANSI et d'isolement detransaction de la base de données que vous utilisez.

Hibernate ne vérouille pas vos objets en mémoire. Votre application peutsuivre le comportement défini par le niveau d'isolation de vos transactionsde base de données. Notez que grâce à la Session, qui est aussi un cachede scope transaction, Hibernate fournit des lectures répétées pour lesrécupération par identifiants et les requêtes d'entités (pas celle de valeursscalaires).

En addition au versionning pour le controle automatique de concurrence,Hibernate fournit une API (mineure) pour le verrouillage perssimiste desenregistrements, en générant une syntaxe SELECT FOR UPDATE. Le controle deconcurrence optimiste et cette API seront détaillés plus tard dans ce chapitre.

Nous aborderons la gestion des accès concurrents en discutant de lagranularité des objets Configuration, SessionFactory, et Session, ainsique de certains concepts relatifs à la base de données et aux longuestransactions applicatives.

11.1. Gestion de session et délimitation detransactions

Il est important de savoir qu'un objet SessionFactory est un objet complexeet optimisé pour fonctionner avec les threads(thread- safe). Il est coûteux àcréer et est ainsi prévu pour n'être instancié qu?une seule fois via un objetConfiguration au démarrage de l'application, et être partagé par tous lesthreads d'une application.

Un objet Session est relativement simple et n'est threadsafe. Il est égalementpeu coûteux à créer. Il devrait n'être utilisé qu'une seule fois, pour unprocessus d'affaire ou une unité de travail ou une conversation et ensuiteêtre relâché. Un objet Session ne tentera pas d'obtenir de connexion (Connection ) JDBC (ou de Datasource ) si ce n'est pas nécessaire.

Page 202: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 11. Transactions et accès c...

188 Hibernate 3.3.1

Afin de compléter ce tableau, vous devez également penser aux transactionsde base de données. Une transaction de base de données se doit d'êtrela plus courte possible afin de réduire les risques de collision sur desenregistrements verrouillés. De longues transactions à la base de donnéesnuiront à l'extensibilité de vos applications lorsque confrontées à de hautsniveaux de charge. Par conséquent, il n'est jamais bon de maintenir unetransaction ouverte pendant la durée de reflexion de l'utilisateur, jusqu'a ceque l'unité de travail soit achevée.

Maintenant, comment délimiter une unité de travail? Est-ce qu'une instancede Session peut avoir une durée de vie dépassant plusieurs transactions à labase de données, ou bien est-ce que celles-ci doivent être liées une à une?Quand faut-il ouvrir et fermer une Session ? Comment définir la démarcationde vos transactions à la base de données?

11.1.1. Unité de travail

Il est important de mentionner que d'utiliser un paradigmesession-par-operation est un anti-pattern. Autrement dit: n'ouvrez et nefermez pas la Session à chacun de vos accès simples à la base de donnéesdans un même thread! Bien sûr, le même raisonnement s'applique sur lagestion des transactions à la base de données. Les appels à la base dedonnées devraient être faits en ordre et selon une séquence définie. Ilsdevraient également être regroupés en des unités de travail atomiques.(Notez que l?utilisation d?une connexion auto-commit constitue le mêmeanti-pattern. Ce mode de fonctionnement existe pour les applicationsémettant des commandes SQL à partir d?une console. Hibernate désengagele mode auto-commit et s'attend à ce qu'un serveur d'applications le fasseégalement.) Les transactions avec la base de données ne sont jamaisoptionnelles, toute communication avec une base de données doit sedérouler dans une transaction, peu importe si vous lisez ou écrivez desdonnées. Comme évoqué, le comportement auto-commit pour lire lesdonnées devrait être évité, puisque plusieurs petites transactions ne serontjamais aussi efficaces qu'une seule plus grosse clairement définie commeunité de travail. Ce dernier choix et en plus beaucoup plus facile a mainteniret à faire évoluer.

The most common pattern in a multi-user client/server application issession-per-request. In this model, a request from the client is sent to theserver (where the Hibernate persistence layer runs), a new Hibernate Sessionis opened, and all database operations are executed in this unit of work.Once the work has been completed (and the response for the client has beenprepared), the session is flushed and closed. You would also use a singledatabase transaction to serve the clients request, starting and committing it

Page 203: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Longue conversation

Hibernate 3.3.1 189

when you open and close the Session. The relationship between the two isone-to-one and this model is a perfect fit for many applications.

The challenge lies in the implementation. Hibernate provides built-inmanagement of the "current session" to simplify this pattern. All you haveto do is start a transaction when a server request has to be processed, andend the transaction before the response is sent to the client. You can do thisin any way you like, common solutions are ServletFilter, AOP interceptorwith a pointcut on the service methods, or a proxy/interception container.An EJB container is a standardized way to implement cross-cutting aspectssuch as transaction demarcation on EJB session beans, declaratively withCMT. If you decide to use programmatic transaction demarcation, prefer theHibernate Transaction API shown later in this chapter, for ease of use andcode portability.

Votre application peut accéder la "session courante" pour exécuter unerequête en invoquant simplement sessionFactory.getCurrentSession()n'importe où et autant de fois que souhaité. Vous obtiendrez toujoursune Session dont le scope est la transaction courante avec la base dedonnées. Ceci doit être configuré soit dans les ressources local ou dansl'environnement JTA, voir Section 2.5, « Sessions Contextuelles ».

Il est parfois utile d'étendre le scope d'une Session et d'une transactionà la base de données jusqu'à ce que "la vue soit rendue". Ceci estparticulièrement utile dans des applications à base de servlet qui utilisent unephase de rendue séparée une fois que la réponse a été préparée. Etendre latransaction avec la base de données jusqu'à la fin du rendering de la vue estaisé si vous implémentez votre propre intercepteur. Cependant, ce n'est pasfacile si vous vous appuyez sur les EJBs avec CMT, puisqu'une transactionsera achevée au retour de la méthode EJB, avant le rendu de la vue. Rendezvous sur le site Hibernate et sur le forum pour des astuces et des exemplessur le pattern Open Session in View pattern..

11.1.2. Longue conversation

Le paradigme session-per-request n'est pas le seul élément à utiliser dansle design de vos unités de travail. Plusieurs processus d'affaire requièrenttoute une série d'interactions avec l'utilisateur, entrelacées d'accès à la basede donnée. Dans une application Web ou une application d'entreprise, ilserait inacceptable que la durée de vie d'une transaction s'étale sur plusieursinteractions avec l'usager. Considérez l'exemple suivant:

• Un écran s'affiche. Les données vues par l'usager ont été chargées dansl'instance d'un objet Session , dans le cadre d'une transaction de base dedonnées. L'usager est libre de modifier ces objets.

Page 204: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 11. Transactions et accès c...

190 Hibernate 3.3.1

• L'usager clique "Sauvegarder" après 5 minutes et souhaite persister lesmodifications qu'il a apportées. Il s'attend à être la seule personne a avoirmodifié ces données et qu'aucune modification conflictuelle ne se soitproduite durant ce laps de temps.

Ceci s'appelle une unité de travail. Du point de vue de l'utilisateur: uneconversation (ou transaction d'application). Il y a plusieurs façon de mettrececi en place dans votre application.

Une première implémentation naïve pourrait consister à garder la Session etla transaction à la base de données ouvertes durant le temps de travail del'usager, à maintenir les enregistrements verrouillés dans la base de donnéesafin d'éviter des modifications concurrentes et de maintenir l'isolation etl'atomicité de la transaction de l'usager. Ceci est un anti-pattern à éviter,puisque le verrouillage des enregistrements dans la base de donnéesne permettrait pas à l'application de gérer un grand nombre d'usagersconcurrents.

Clearly, we have to use several database transactions to implement theconversation. In this case, maintaining isolation of business processesbecomes the partial responsibility of the application tier. A singleconversation usually spans several database transactions. It will be atomicif only one of these database transactions (the last one) stores the updateddata, all others simply read data (e.g. in a wizard-style dialog spanningseveral request/response cycles). This is easier to implement than it mightsound, especially if you use Hibernate's features:

• Automatic Versioning - Hibernate can do automatic optimistic concurrencycontrol for you, it can automatically detect if a concurrent modificationoccurred during user think time. Usually we only check at the end of theconversation.

• Objets Détachés - Si vous décidez d'utiliser le paradigmesession-par-requête discuté plus haut, toutes les entités chargées enmémoire deviendront des objets détachés durant le temps de réflexionde l'usager. Hibernate vous permet de rattacher ces objets et depersister les modifications y ayant été apportées. Ce pattern est appelé:session-per- request-with-detached-objects (littéralement: session-par-requête-avec-objets-détachés). Le versionnage automatique est utiliséafin d'isoler les modifications concurrentes.

• Extended (or Long) Session - The Hibernate Session may be disconnectedfrom the underlying JDBC connection after the database transaction hasbeen committed, and reconnected when a new client request occurs.This pattern is known as session-per-conversation and makes even

Page 205: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

L'identité des objets

Hibernate 3.3.1 191

reattachment unnecessary. Automatic versioning is used to isolateconcurrent modifications and the Session is usually not allowed to beflushed automatically, but explicitly.

Les deux patterns session-per-request-with- detached- objects(session-par-requête-avec-objets- détachés) et session-per-conversation(session-par-conversation) ont chacun leurs avantages et désavantages quiseront exposés dans ce même chapitre, dans la section au sujet du contrôleoptimiste de concurrence.

11.1.3. L'identité des objets

Une application peut accéder à la même entité persistante de manièreconcurrente dans deux Session s différentes. Toutefois, une instance d'uneclasse persistante n'est jamais partagée par deux instances distinctes de laclasse Session . Il existe donc deux notions de l'identité d'un objet:

Identité BDfoo.getId().equals( bar.getId() )

Identité JVMfoo==bar

Then for objects attached to a particular Session (i.e. in the scope of aSession) the two notions are equivalent, and JVM identity for databaseidentity is guaranteed by Hibernate. However, while the application mightconcurrently access the "same" (persistent identity) business object in twodifferent sessions, the two instances will actually be "different" (JVM identity).Conflicts are resolved using (automatic versioning) at flush/commit time,using an optimistic approach.

Cette approche permet de reléguer à Hibernate et à la base de donnéessous-jacente le soin de gérer les problèmes d'accès concurrents.Cette manière de faire assure également une meilleure extensibilité del'application puisque assurer l'identité JVM dans un thread ne nécessitepas de mécanismes de verrouillage coûteux ou d'autres dispositifs desynchronisation. Une application n'aura jamais le besoin de synchroniserdes objets d'affaire tant qu'elle peut garantir qu'un seul thread aura accèsà une instance de Session . Dans le cadre d'exécution d'un objet Session ,l'application peut utiliser en toute sécurité == pour comparer des objets.

Une application qui utiliserait == à l'extérieur du cadre d'exécution d'uneSession pourrait obtenir des résultats inattendus et causer certains effetsde bords. Par exemple, si vous mettez 2 objets dans le même Set ,ceux-ci pourraient avoir la même identité BD (i.e. ils représentent le mêmeenregistrement), mais leur identité JVM pourrait être différente (elle ne peut,

Page 206: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 11. Transactions et accès c...

192 Hibernate 3.3.1

par définition, pas être garantie sur deux objets détachés). Le développeurdoit donc redéfinir l'implémentation des méthodes equals() et hashcode()dans les classes persistantes et y adjoindre sa propre notion d'identité.Il existe toutefois une restriction: Il ne faut jamais utiliser uniquementl'identifiant de la base de données dans l'implémentation de l'égalité; Il faututiliser une clé d'affaire, généralement une combinaison de plusieurs attributsuniques, si possible immuables. Les identifiants de base de données vontchanger si un objet transitoire (transient) devient persistant. Si une instancetransitoire est contenue dans un Set , changer le hashcode brisera le contratdu Set . Les attributs pour les clés d'affaire n'ont pas à être aussi stablesque des clés primaires de bases de données. Il suffit simplement qu'ellessoient stables tant et aussi longtemps que les objets sont dans le même Set. Veuillez consulter le site web Hibernate pour des discussions plus pointuesà ce sujet. Notez que ce concept n'est pas propre à Hibernate mais biengénéral à l'implémentation de l'identité et de l'égalité en Java.

11.1.4. Problèmes communs

Bien qu'il puisse y avoir quelques rares exceptions à cette règle, il estrecommandé de ne jamais utiliser les anti-patterns session-per- user-sessionet session-per-application . Vous trouverez ici- bas quelques problèmesque vous risquez de rencontrer si vous en faite l?utilisation. (Ces problèmespourraient quand même survenir avec des patterns recommandés)Assurez-vous de bien comprendre les implications de chacun des patternsavant de prendre votre décision.

• L'objet Session n?est pas conçu pour être utilisé par de multiples threads.En conséquence, les objets potentiellement multi-thread comme lesrequêtes HTTP, les EJB Session et Swing Worker, risquent de provoquerdes conditions de course dans la Session si celle-ci est partagée. Dansun environnement web classique, il serait préférable de synchroniser lesaccès à la session http afin d?éviter qu?un usager ne recharge une pageassez rapidement pour que deux requêtes s?exécutant dans des threadsconcurrents n?utilisent la même Session .

• Lorsque Hibernate lance une exception, le roll back de la transaction encours doit être effectué et la Session doit être immédiatement fermée.(Ceci sera exploré plus tard dans le chapitre.) Si la Session est directementassociée à une application, il faut arrêter l?application. Le roll back dela transaction ne remettra pas les objets dans leur état du début de latransaction. Ainsi, ceux-ci pourraient être désynchronisés d?avec lesenregistrements. (Généralement, cela ne cause pas de réels problèmespuisque la plupart des exceptions sont non traitables et requièrent lareprise du processus d?affaire ayant échoué.)

Page 207: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Démarcation des transactions

Hibernate 3.3.1 193

• La Session met en mémoire cache tous les objets persistants (les objetssurveillés et dont l'état est géré par Hibernate.) Si la Session est ouverteindéfiniment ou si une trop grande quantité d'objets y est chargée,l?utilisation de la mémoire peut potentiellement croître jusqu?à atteindrele maximum allouable à l?application (java.lang.OutOfMemoryError.)Une solution à ce problème est d?appeler les méthodes Session.clear()et Session.evict() pour gérer la mémoire cache de la Session . Vouspouvez également utiliser des stored procedures si vous devez lancer destraitements sur de grandes quantités d?informations. Certaines solutionssont décrites ici : Chapitre 13, Traitement par paquet . Garder une Sessionouverte pour toute la durée d?une session usager augmente égalementconsidérablement le risque de travailler avec de l?information périmée.

11.2. Démarcation des transactions

Database (or system) transaction boundaries are always necessary.No communication with the database can occur outside of a databasetransaction (this seems to confuse many developers who are used to theauto-commit mode). Always use clear transaction boundaries, even forread-only operations. Depending on your isolation level and databasecapabilities this might not be required but there is no downside if you alwaysdemarcate transactions explicitly. Certainly, a single database transaction isgoing to perform better than many small transactions, even for reading data.

Une application utilisant Hibernate peut s'exécuter dans un environnementléger n?offrant pas la gestion automatique des transactions (applicationautonome, application web simple ou applications Swing) ou dans unenvironnement J2EE offrant des services de gestion automatique destransactions JTA. Dans un environnement simple, Hibernate a généralementla responsabilité de la gestion de son propre pool de connexions à labase de données. Le développeur de l'application doit manuellementdélimiter les transactions. En d'autres mots, il appartient au développeurde gérer les appels à Transaction.begin() , Transaction.commit() etTransaction.rollback() . Un environnement transactionnel J2EE (serveurd'application J2EE) doit offrir la gestion des transactions au niveau ducontainer J2EE. Les bornes de transaction peuvent normalement êtredéfinies de manière déclarative dans les descripteurs de déploiement d'EJBSession, par exemple. La gestion programmatique des transactions n'yest donc pas nécessaire. Même les appels à Session.flush() sont faitsautomatiquement.

However, it is often desirable to keep your persistence layer portablebetween non-managed resource-local environments, and systems thatcan rely on JTA but use BMT instead of CMT. In both cases you'd use

Page 208: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 11. Transactions et accès c...

194 Hibernate 3.3.1

programmatic transaction demarcation. Hibernate offers a wrapper APIcalled Transaction that translates into the native transaction system of yourdeployment environment. This API is actually optional, but we stronglyencourage its use unless you are in a CMT session bean.

Il existe quatre étapes disctinctes lors de la fermeture d'une Session

• flush de la session

• commit de la transaction

• Fermeture de la session (Close)

• Gestion des exceptions

La synchronisation de bdd depuis la session (flush) a déjà été expliqué,nous nous attarderons maintenant à la démarcation des transactionset à la gestion des exceptions dans les environnements légers et lesenvironnements J2EE.

11.2.1. Environnement non managé

Si la couche de persistance Hibernate s'exécute dans un environnement nonmanagé, les connexions à la base de données seront généralement prisesen charge par le mécanisme de pool d'Hibernate. La gestion de la session etde la transaction se fera donc de la manière suivante:

// Non-managed environment idiom

Session sess = factory.openSession();

Transaction tx = null;

try {

tx = sess.beginTransaction();

// do some work

...

tx.commit();

}

catch (RuntimeException e) {

if (tx != null) tx.rollback();

throw e; // or display error message

}

finally {

sess.close();

}

Vous n'avez pas à invoquer flush() explicitement sur la Session - l'appelde commit() déclenchera automatiquement la synchronisation (selon leSection 10.10, « Flush de la session » de la session. Un appel à close()marque la fin de la session. La conséquence directe est que la connexion àla base de données sera relachée par la session. Ce code est portable est

Page 209: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Utilisation de JTA

Hibernate 3.3.1 195

fonctionne dans les environnements non managé ET les environnementsJTA.

Une solution plus flexible est la gestion par contexte fourni par Hibernate quenous avons déjà rencontré:

// Non-managed environment idiom with getCurrentSession()

try {

factory.getCurrentSession().beginTransaction();

// do some work

...

factory.getCurrentSession().getTransaction().commit();

}

catch (RuntimeException e) {

factory.getCurrentSession().getTransaction().rollback();

throw e; // or display error message

}

Vous ne verrez probablement jamais ces exemples de code dans lesapplications; les exceptions fatales (exceptions du système) ne devraientêtre traitées que dans la couche la plus "haute". En d'autres termes, lecode qui exécute les appels à Hibernate (à la couche de persistance) etle code qui gère les RuntimeException (qui ne peut généralement effectuerqu'un nettoyage et une sortie) sont dans des couches différentes. Lagestion du contexte courant par Hibernate peut simplifier notablement cedesign, puisque vous devez accéder à la gestion des exceptions de laSessionFactory, ce qui est décrit plus tard dans ce chapitre.

Notez que vous devriez sélectionnerorg.hibernate.transaction.JDBCTransactionFactory (le défaut), pour lesecond exemple "thread" comme hibernate.current_session_context_class.

11.2.2. Utilisation de JTA

Si votre couche de persistance s'exécute dans un serveur d'application (parexemple, derrière un EJB Session Bean), toutes les datasource utiliséespar Hibernate feront automatiquement partie de transactions JTA globales.Hibernate propose deux stratégies pour réussir cette intégration.

Si vous utilisez des transactions gérées par un EJB (bean managedtransactions - BMT), Hibernate informera le serveur d'application du débutet de la fin des transactions si vous utilisez l'API Transaction . Ainsi, lecode de gestion des transactions sera identique dans les deux typesd'environnements.

// BMT idiom

Page 210: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 11. Transactions et accès c...

196 Hibernate 3.3.1

Session sess = factory.openSession();

Transaction tx = null;

try {

tx = sess.beginTransaction();

// do some work

...

tx.commit();

}

catch (RuntimeException e) {

if (tx != null) tx.rollback();

throw e; // or display error message

}

finally {

sess.close();

}

Ou encore, avec la gestion automatique de contexte:

// BMT idiom with getCurrentSession()

try {

UserTransaction tx = (UserTransaction)new InitialContext()

.lookup("java:comp/UserTransaction");

tx.begin();

// Do some work on Session bound to transaction

factory.getCurrentSession().load(...);

factory.getCurrentSession().persist(...);

tx.commit();

}

catch (RuntimeException e) {

tx.rollback();

throw e; // or display error message

}

With CMT, transaction demarcation is done in session bean deploymentdescriptors, not programmatically, hence, the code is reduced to:

// CMT idiom

Session sess = factory.getCurrentSession();

// do some work

...

Dans un EJB CMT même le rollback intervient automatiquement, puisqu'uneRuntimeException non traitée et soulevée par une méthode d'un bean sessionindique au conteneur d'annuler la transaction globale. Ceci veut donc direque vous n'avez pas à utiliser l'API Transaction d'Hibernate dans CMT.

Page 211: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Gestion des exceptions

Hibernate 3.3.1 197

Note that you should chooseorg.hibernate.transaction.JTATransactionFactory if you use JTA directly(BMT), and org.hibernate.transaction.CMTTransactionFactory in aCMT session bean, when you configure Hibernate's transaction factory.Remember to also set hibernate.transaction.manager_lookup_class.Furthermore, make sure that your hibernate.current_session_context_classis either unset (backwards compatibility), or set to "jta".

The getCurrentSession() operation has one downside in a JTA environment.There is one caveat to the use of after_statement connection releasemode, which is then used by default. Due to a silly limitation of theJTA spec, it is not possible for Hibernate to automatically clean up anyunclosed ScrollableResults or Iterator instances returned by scroll()or iterate(). You must release the underlying database cursor by callingScrollableResults.close() or Hibernate.close(Iterator) explicitly from afinally block. (Of course, most applications can easily avoid using scroll()or iterate() at all from the JTA or CMT code.)

11.2.3. Gestion des exceptions

Si une Session lance une exception (incluant les exceptions du typeSQLException ou d'un sous-type), vous devez immédiatement faire le rollbackde la transaction, appeler Session.close() et relâcher les références surl'objet Session . La Session contient des méthodes pouvant la mettre dansun état inutilisable. Vous devez considérer qu'aucune exception lancée parHibernate n'est traitable. Assurez-vous de fermer la session en faisant l'appelà close() dans un bloc finally .

L'exception HibernateException , qui englobe la plupart des exceptionspouvant survenir dans la couche de persistance Hibernate, est une exceptionnon vérifiée (Ceci n'était pas le cas dans certaines versions antérieuresde Hibernate.) Il est de notre avis que nous ne devrions pas forcer undéveloppeur à gérer une exception qu'il ne peut de toute façon pas traiterdans une couche technique. Dans la plupart des applications, les exceptionsnon vérifiées et les exceptions fatales sont gérées en amont du processus(dans les couches hautes) et un message d'erreur est alors affiché àl'usager (ou un traitement alternatif est invoqué.) Veuillez noter qu'Hibernatepeut également lancer des exceptions non vérifiées d'un autre type queHibernateException . Celles-ci sont également non traitables et vous devezles traiter comme telles.

Hibernate wraps SQLExceptions thrown while interacting with the database ina JDBCException. In fact, Hibernate will attempt to convert the exception intoa more meaningful subclass of JDBCException. The underlying SQLExceptionis always available via JDBCException.getCause(). Hibernate converts

Page 212: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 11. Transactions et accès c...

198 Hibernate 3.3.1

the SQLException into an appropriate JDBCException subclass using theSQLExceptionConverter attached to the SessionFactory. By default, theSQLExceptionConverter is defined by the configured dialect; however, it isalso possible to plug in a custom implementation (see the javadocs for theSQLExceptionConverterFactory class for details). The standard JDBCExceptionsubtypes are:

• JDBCConnectionException - Indique une erreur de communication avec lacouche JDBC sous-jacente.

• SQLGrammarException - Indique un problème de grammaire ou de syntaxeavec la requête SQL envoyée.

• ConstraintViolationException - Indique une violation de contrainted'intégrité.

• LockAcquisitionException - Indique une erreur de verrouillage lors del'éxécution de la requête.

• GenericJDBCException - Indique une erreur générique JDBC d'une autrecatégorie.

11.2.4. Timeout de transaction

One extremely important feature provided by a managed environment likeEJB that is never provided for non-managed code is transaction timeout.Transaction timeouts ensure that no misbehaving transaction can indefinitelytie up resources while returning no response to the user. Outside a managed(JTA) environment, Hibernate cannot fully provide this functionality. However,Hibernate can at least control data access operations, ensuring thatdatabase level deadlocks and queries with huge result sets are limited bya defined timeout. In a managed environment, Hibernate can delegatetransaction timeout to JTA. This functionality is abstracted by the HibernateTransaction object.

Session sess = factory.openSession();

try {

//set transaction timeout to 3 seconds

sess.getTransaction().setTimeout(3);

sess.getTransaction().begin();

// do some work

...

sess.getTransaction().commit()

}

catch (RuntimeException e) {

sess.getTransaction().rollback();

throw e; // or display error message

}

Page 213: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Contrôle de consurrence optimiste

Hibernate 3.3.1 199

finally {

sess.close();

}

Notez que setTimeout() ne peut pas être appelé d'un EJB CMT, puisque letimeout des transaction doit être spécifié de manière déclarative.

11.3. Contrôle de consurrence optimiste

La gestion optimiste des accès concurrents avec versionnage est la seuleapproche pouvant garantir l'extensibilité des applications à haut niveaude charge. Le système de versionnage utilise des numéros de version oul'horodatage pour détecter les mises à jour causant des conflits avec d'autresactualisations antérieures. Hibernate propose trois approches pour l'écriturede code applicatif utilisant la gestion optimiste d'accès concurrents. Le casd'utilisation décrit plus bas fait mention de conversation, mais le versionnagepeut également améliorer la qualité d'une application en prévenant la pertede mises à jour.

11.3.1. Gestion du versionnage au niveau applicatif

Dans cet exemple d'implémentation utilisant peu les fonctionnalitésd'Hibernate, chaque interaction avec la base de données se fait enutilisant une nouvelle Session et le développeur doit recharger les donnéespersistantes à partir de la BD avant de les manipuler. Cette implémentationforce l'application à vérifier la version des objets afin de maintenir l'isolationtransactionnelle. Cette approche, semblable à celle retrouvée pour les EJB,est la moins efficace de celles présentées dans ce chapitre.

// foo is an instance loaded by a previous Session

session = factory.openSession();

Transaction t = session.beginTransaction();

int oldVersion = foo.getVersion();

session.load( foo, foo.getKey() ); // load the current state

if ( oldVersion != foo.getVersion() ) throw new

StaleObjectStateException();

foo.setProperty("bar");

t.commit();

session.close();

Le mapping de la propriété version est fait via <version> et Hibernatel'incrémentera automatiquement à chaque flush() si l'entité doit être mise àjour.

Bien sûr, si votre application ne fait pas face à beaucoup d'accès concurrentset ne nécessite pas l'utilisation du versionnage, cette approche peut

Page 214: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 11. Transactions et accès c...

200 Hibernate 3.3.1

également être utilisée, il n'y a qu'à ignorer le code relié au versionnage.Dans ce cas, la stratégie du last commit wins (littéralement: le derniercommit l'emporte) sera utilisée pour les conversations (longues transactionsapplicatives). Gardez à l'esprit que cette approche pourrait rendre perplexeles utilisateurs de l'application car ils pourraient perdre des données misesà jour sans qu'aucun message d'erreur ne leur soit présenté et sans avoir lapossibilité de fusionner les données.

Clearly, manual version checking is only feasible in very trivial circumstancesand not practical for most applications. Often not only single instances, butcomplete graphs of modified objects have to be checked. Hibernate offersautomatic version checking with either an extended Session or detachedinstances as the design paradigm.

11.3.2. Les sessions longues et le versionnageautomatique.

Dans ce scénario, une seule instance de Session et des objets persistantsest utilisée pour toute l'application. Hibernate vérifie la version des objetspersistants avant d'effectuer le flush() et lance une exception si unemodification concurrente est détectée. Il appartient alors au développeur degérer l'exception. Les traitements alternatifs généralement proposés sontalors de permettre à l'usager de faire la fusion des données ou de lui offrir derecommencer son travail à partie des données les plus récentes dans la BD.

Il est à noter que lorsqu'une application est en attente d'une action de la partde l?usager, La Session n'est pas connectée à la couche JDBC sous-jacente.C'est la manière la plus efficace de gérer les accès à la base de données.L'application ne devrait pas se préoccuper du versionnage des objets, de laréassociation des objets détachés, ni du rechargement de tous les objets àchaque transaction.

// foo is an instance loaded earlier by the old session

Transaction t = session.beginTransaction(); // Obtain a new JDBC

connection, start transaction

foo.setProperty("bar");

session.flush(); // Only for last transaction in conversation

t.commit(); // Also return JDBC connection

session.close(); // Only for last transaction in conversation

L'objet foo sait quel objet Session l'a chargé. Session.reconnect() obtientune nouvelle connexion (celle-ci peut être également fournie) et permetà la session de continuer son travail. La méthode Session.disconnect()déconnecte la session de la connexion JDBC et retourne celle-ci au pool de

Page 215: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Les objets détachés et le versionnageautomatique

Hibernate 3.3.1 201

connexion (à moins que vous ne lui ayez fourni vous même la connexion.)Après la reconnexion, afin de forcer la vérification du versionnage decertaines entités que vous ne cherchez pas à actualiser, vous pouvez faireun appel à Session.lock() en mode LockMode.READ pour tout objet ayant puêtre modifié par une autre transaction. Il n'est pas nécessaire de verrouillerles données que vous désirez mettre à jour.

Si des appels implicites aux méthodes disconnect() etreconnect() sont trop coûteux, vous pouvez les éviter en utilisanthibernate.connection.release_mode .

Ce pattern peut présenter des problèmes si la Session est trop volumineusepour être stockée entre les actions de l'usager. Plus spécifiquement, unesession HttpSession se doit d'être la plus petite possible. Puisque la Sessionjoue obligatoirement le rôle de mémoire cache de premier niveau et contientà ce titre tous les objets chargés, il est préférable de n'utiliser cette stratégieque pour quelques cycles de requêtes car les objets risquent d'y êtrerapidement périmés.

Notez que la Session déconnectée devrait être conservée près de la couchede persistance. Autrement dit, utilisez un EJB stateful pour conserverla Session et évitez de la sérialiser et de la transférer à la couche deprésentation (i.e. Il est préférable de ne pas la conserver dans la sessionHttpSession .)

The extended session pattern, or session-per-conversation, is more difficultto implement with automatic current session context management. You needto supply your own implementation of the CurrentSessionContext for this, seethe Hibernate Wiki for examples.

11.3.3. Les objets détachés et le versionnage automatique

Chaque interaction avec le système de persistance se fait via une nouvelleSession . Toutefois, les mêmes instances d'objets persistants sont réutiliséespour chacune de ces interactions. L'application doit pouvoir manipuler l'étatdes instances détachées ayant été chargées antérieurement via une autresession. Pour ce faire, ces objets persistants doivent être rattachés à laSession courante en utilisant Session.update() , Session.saveOrUpdate() , ouSession.merge() .

// foo is an instance loaded by a previous Session

foo.setProperty("bar");

session = factory.openSession();

Transaction t = session.beginTransaction();

session.saveOrUpdate(foo); // Use merge() if "foo" might have been

loaded already

Page 216: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 11. Transactions et accès c...

202 Hibernate 3.3.1

t.commit();

session.close();

Again, Hibernate will check instance versions during flush, throwing anexception if conflicting updates occurred.

Vous pouvez également utiliser lock() au lieu de update() et utiliser le modeLockMode.READ (qui lancera une vérification de version, en ignorant tousles niveaux de mémoire cache) si vous êtes certain que l'objet n'a pas étémodifié.

11.3.4. Personnaliser le versionnage automatique

Vous pouvez désactiver l'incrémentation automatique du numéro de versionde certains attributs et collections en mettant la valeur du paramètre demapping optimistic-lock à false. Hibernate cessera ainsi d'incrémenter leurnuméro de version s'ils sont mis à jour.

Legacy database schemas are often static and can't be modified. Or, otherapplications might also access the same database and don't know howto handle version numbers or even timestamps. In both cases, versioningcan't rely on a particular column in a table. To force a version checkwithout a version or timestamp property mapping, with a comparisonof the state of all fields in a row, turn on optimistic-lock="all" in the<class> mapping. Note that this conceptually only works if Hibernate cancompare the old and new state, i.e. if you use a single long Session and notsession-per-request-with-detached-objects.

Il peut être souhaitable de permettre les modifications concurrenteslorsque des champs distincts sont modifiés. En mettant la propriétéoptimistic-lock="dirty" dans l'élément <class> , Hibernate ne fera lacomparaison que des champs devant être actualisés lors du flush().

In both cases, with dedicated version/timestamp columns or with full/dirtyfield comparison, Hibernate uses a single UPDATE statement (with anappropriate WHERE clause) per entity to execute the version check and updatethe information. If you use transitive persistence to cascade reattachmentto associated entities, Hibernate might execute unnecessary updates. Thisis usually not a problem, but on update triggers in the database might beexecuted even when no changes have been made to detached instances.You can customize this behavior by setting select-before-update="true" inthe <class> mapping, forcing Hibernate to SELECT the instance to ensure thatchanges did actually occur, before updating the row.

Page 217: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Verouillage pessimiste

Hibernate 3.3.1 203

11.4. Verouillage pessimiste

It is not intended that users spend much time worrying about lockingstrategies. It's usually enough to specify an isolation level for the JDBCconnections and then simply let the database do all the work. However,advanced users may sometimes wish to obtain exclusive pessimistic locks, orre-obtain locks at the start of a new transaction.

Hibernate utilisera toujours le mécanisme de verrouillage de la base dedonnées et ne verrouillera jamais les objets en mémoire!

La classe LockMode définit les différents niveaux de verrouillage pouvantêtre obtenus par Hibernate. Le verrouillage est obtenu par les mécanismessuivants:

• LockMode.WRITE est obtenu automatiquement quand Hibernate actualise ouinsert un enregistrement.

• LockMode.UPGRADE peut être obtenu de manière explicite via la requête enutilisant SELECT ... FOR UPDATE sur une base de données supportant cettesyntaxe.

• LockMode.UPGRADE_NOWAIT peut être obtenu de manière explicite en utilisantSELECT ... FOR UPDATE NOWAIT sur Oracle.

• LockMode.READ est obtenu automatiquement quand Hibernate lit desdonnées dans un contexte d'isolation Repeatable Read ou Serializable .Peut être réobtenu explicitement via une requête.

• LockMode.NONE représente l'absence de verouillage. Tous les objets migrentvers ce mode a la fin d'une Transaction . Les objets associés à unesession via un appel à saveOrUpdate() commencent également leur cyclede vie dans cet état.

Les niveaux de verrouillage peuvent être explicitement obtenus de l'une desmanières suivantes:

• Un appel à Session.load() , en spécifiant un niveau verrouillage LockMode .• Un appel à Session.lock() .• Une appel à Query.setLockMode() .

Si Session.load() est appelé avec le paramètre de niveau de verouillageUPGRADE ou UPGRADE_NOWAIT et que l'objet demandé n'est pas présent dans lasession, celui-ci sera chargé à l'aide d'une requête SELECT ... FOR UPDATE. Si la méthode load() est appelée pour un objet déjà en session avec unverrouillage moindre que celui demandé, Hibernate appellera la méthodelock() pour cet objet.

Session.lock() effectue une vérification de version si le niveau deverrouillage est READ , UPGRADE ou UPGRADE_NOWAIT . (Dans le cas des niveaux

Page 218: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 11. Transactions et accès c...

204 Hibernate 3.3.1

UPGRADE ou UPGRADE_NOWAIT , une requête SELECT ... FOR UPDATE serautilisée.)

Si une base de données ne supporte pas le niveau de verrouillage demandé,Hibernate utilisera un niveau alternatif convenable au lieux de lancer uneexception. Ceci assurera la portabilité de votre application.

11.5. Mode de libération de Connection

Le comportement original (2.x) d'Hibernate pour la gestion des connexionsJDBC était que la Session obtenait une connexion dès qu'elle en avait besoinet la libérait une fois la session fermée. Hibernate 3 a introduit les modesde libération de connexion pour indiquer à la session comment gérer lestransactions JDBC. Notez que la discussion suivante n'est pertinente quepour des connexions fournies par un ConnectionProvider, celles géréespar l'utilisateur sont en dehors du scope de cette discussion. Les différentsmodes sont définies par org.hibernate.ConnectionReleaseMode:

• ON_CLOSE - is essentially the legacy behavior described above. TheHibernate session obtains a connection when it first needs to performsome JDBC access and holds unto that connection until the session isclosed.

• AFTER_TRANSACTION - indique de relacher la connexion après qu'uneorg.hibernate.Transaction se soit achevée.

• AFTER_STATEMENT (aussi appelé libération brutale) - indique de relacherles connexions après chaque exécution d'un statement. Ce relachementaggressif est annulé si ce statement laisse des ressources associéesà une session donnée ouvertes, actuellement ceci n'arrive que lors del'utilisation de org.hibernate.ScrollableResults.

Le paramètre de configuration hibernate.connection.release_mode est utilisépour spécifier quel mode de libération doit être utiliser. Les valeurs possiblessont:

• auto (valeur par défaut) - ce choix délèguele choix de libération à la méthodeorg.hibernate.transaction.TransactionFactory.getDefaultReleaseMode()

Pour la JTATransactionFactory, elle retourneConnectionReleaseMode.AFTER_STATEMENT;pour JDBCTransactionFactory, elle retourneConnectionReleaseMode.AFTER_TRANSACTION. C'est rarement unebonne idée de changer ce comportement par défaut puisque les erreurssoulevées par ce paramétrage tend à prouver une erreur dans le code del'utilisateur.

Page 219: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Mode de libération de Connection

Hibernate 3.3.1 205

• on_close - indique d'utiliser ConnectionReleaseMode.ON_CLOSE.Ce paramétrage existe pour garantir la compatibilité avec les versionsprécédentes, mais ne devrait plus être utilisé.

• after_transaction - indique d'utiliserConnectionReleaseMode.AFTER_TRANSACTION. Ne devraitpas être utilisé dans les environnements JTA. Notez aussi qu'avecConnectionReleaseMode.AFTER_TRANSACTION, si une session estconsidérée comme étant en mode auto-commit les connexions serontrelachées comme si le mode était AFTER_STATEMENT.

• after_statement - indique d'utiliserConnectionReleaseMode.AFTER_STATEMENT. Additonnellement,le ConnectionProvider utilisé est consulté pour savoir s'ilsupporte ce paramétrage (supportsAggressiveRelease()). Sice n'est pas le cas, le mode de libération est ré initialisé àConnectionReleaseMode.AFTER_TRANSACTION. Ce paramétragen'est sûr que dans les environnements où il est possible d'obtenir ànouveau la même connexion JDBC à chaque fois que l'on fait un appelde ConnectionProvider.getConnection() ou dans les envrionnementsauto-commit où il n'est pas important d'obtenir plusieurs fois la mêmeconnexion.

Page 220: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

206 Hibernate 3.3.1

Page 221: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 207

Chapitre 12. Les intercepteurs et lesévénements

Il est souvent utile pour l'application de réagir à certains événements quisurviennent dans Hibernate. Cela autorise l'implémentation de certainessortes de fonctionnalités génériques, et d'extensions de fonctionnalitésd'Hibernate.

12.1. Intercepteurs

L'interface Interceptor fournit des "callbacks" de la session vers l'applicationet permettent à l'application de consulter et/ou de manipuler des propriétésd'un objet persistant avant qu'il soit sauvegardé, mis à jour, supprimé ouchargé. Une utilisation possible de cette fonctionnalité est de tracer l'accès àl'information. Par exemple, l'Interceptor suivant positionne createTimestampquand un Auditable est créé et met à jour la propriété lastUpdateTimestampquand un Auditable est mis à jour.

Vous pouvez soit implémenter Interceptor directement ou (mieux) étendreEmptyInterceptor.

package org.hibernate.test;

import java.io.Serializable;

import java.util.Date;

import java.util.Iterator;

import org.hibernate.EmptyInterceptor;

import org.hibernate.Transaction;

import org.hibernate.type.Type;

public class AuditInterceptor extends EmptyInterceptor {

private int updates;

private int creates;

private int loads;

public void onDelete(Object entity,

Serializable id,

Object[] state,

String[] propertyNames,

Type[] types) {

// do nothing

}

public boolean onFlushDirty(Object entity,

Serializable id,

Page 222: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 12. Les intercepteurs et le...

208 Hibernate 3.3.1

Object[] currentState,

Object[] previousState,

String[] propertyNames,

Type[] types) {

if ( entity instanceof Auditable ) {

updates++;

for ( int i=0; i < propertyNames.length; i++ ) {

if ( "lastUpdateTimestamp".equals( propertyNames[i]

) ) {

currentState[i] = new Date();

return true;

}

}

}

return false;

}

public boolean onLoad(Object entity,

Serializable id,

Object[] state,

String[] propertyNames,

Type[] types) {

if ( entity instanceof Auditable ) {

loads++;

}

return false;

}

public boolean onSave(Object entity,

Serializable id,

Object[] state,

String[] propertyNames,

Type[] types) {

if ( entity instanceof Auditable ) {

creates++;

for ( int i=0; i<propertyNames.length; i++ ) {

if ( "createTimestamp".equals( propertyNames[i] ) )

{

state[i] = new Date();

return true;

}

}

}

return false;

}

public void afterTransactionCompletion(Transaction tx) {

if ( tx.wasCommitted() ) {

System.out.println("Creations: " + creates + ", Updates:

" + updates, "Loads: " + loads);

}

updates=0;

Page 223: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Système d'événements

Hibernate 3.3.1 209

creates=0;

loads=0;

}

}

Interceptors come in two flavors: Session-scoped andSessionFactory-scoped.

A Session-scoped interceptor is specified when a session is opened usingone of the overloaded SessionFactory.openSession() methods accepting anInterceptor.

Session session = sf.openSession( new AuditInterceptor() );

A SessionFactory-scoped interceptor is registered with the Configurationobject prior to building the SessionFactory. In this case, the suppliedinterceptor will be applied to all sessions opened from that SessionFactory;this is true unless a session is opened explicitly specifying the interceptorto use. SessionFactory-scoped interceptors must be thread safe, takingcare to not store session-specific state since multiple sessions will use thisinterceptor (potentially) concurrently.

new Configuration().setInterceptor( new AuditInterceptor() );

12.2. Système d'événements

Si vous devez réagir à des événements particuliers dans votre couchede persistance, vous pouvez aussi utiliser l'architecture d'événementsd'Hibernate3. Le système d'événements peut être utilisé en supplément ouen remplacement des interceptors.

Essentiellement toutes les méthodes de l'interface Session sont corréléesà un événement. Vous avez un LoadEvent, un FlushEvent, etc (consultez laDTD du fichier de configuration XML ou le paquet org.hibernate.event pouravoir la liste complète des types d'événement définis). Quand une requêteest faite à partir d'une de ces méthodes, la Session Hibernate génère unévénement approprié et le passe au listener configuré pour ce type. Pardéfaut, ces listeners implémentent le même traitement dans lequel cesméthodes aboutissent toujours. Cependant, vous êtes libre d'implémenterune version personnalisée d'une de ces interfaces de listener (c'est-à-dire,le LoadEvent est traité par l'implémentation de l'interface LoadEventListenerdéclarée), dans quel cas leur implémentation devrait être responsable dutraitement des requêtes load() faites par la Session.

Page 224: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 12. Les intercepteurs et le...

210 Hibernate 3.3.1

Les listeners devraient effectivement être considérés comme des singletons; dans le sens où ils sont partagés entre des requêtes, et donc ne devraientpas sauvegarder des états de variables d'instance.

Un listener personnalisé devrait implémenter l'interface appropriée pourl'événement qu'il veut traiter et/ou étendre une des classes de base (oumême l'événement prêt à l'emploi utilisé par Hibernate comme ceux déclarésnon-finaux à cette intention). Les listeners personnalisés peuvent être soitinscrits par programmation à travers l'objet Configuration, ou spécifiés laconfiguration XML d'Hibernate (la configuration déclarative à travers le fichierde propriétés n'est pas supportée). Voici un exemple de listener personnalisépour l'événement de chargement :

public class MyLoadListener implements LoadEventListener {

// this is the single method defined by the LoadEventListener

interface

public void onLoad(LoadEvent event, LoadEventListener.LoadType

loadType)

throws HibernateException {

if ( !MySecurity.isAuthorized( event.getEntityClassName(),

event.getEntityId() ) ) {

throw MySecurityException("Unauthorized access");

}

}

}

Vous avez aussi besoin d'une entrée de configuration disant à Hibernated'utiliser ce listener en plus du listener par défaut :

<hibernate-configuration>

<session-factory>

...

<event type="load">

<listener class="com.eg.MyLoadListener"/>

<listener

class="org.hibernate.event.def.DefaultLoadEventListener"/>

</event>

</session-factory>

</hibernate-configuration>

Vous pouvez aussi l'inscrire par programmation :

Configuration cfg = new Configuration();

LoadEventListener[] stack = { new MyLoadListener(), new

DefaultLoadEventListener() };

cfg.EventListeners().setLoadEventListeners(stack);

Les listeners inscrits déclarativement ne peuvent pas partager d'instances.Si le même nom de classe est utilisée dans plusieurs éléments <listener/>,chaque référence sera une instance distincte de cette classe. Si vous

Page 225: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Sécurité déclarative d'Hibernate

Hibernate 3.3.1 211

avez besoin de la faculté de partager des instances de listener entreplusieurs types de listener, vous devez utiliser l'approche d'inscription parprogrammation.

Pourquoi implémenter une interface et définir le type spécifique durantla configuration ? Une implémentation de listener pourrait implémenterplusieurs interfaces de listener d'événements. Avoir en plus le type définidurant l'inscription rend plus facile l'activation ou la désactivation pendant laconfiguration.

12.3. Sécurité déclarative d'Hibernate

Généralement, la sécurité déclarative dans les applications Hibernateest gérée dans la couche de session. Maintenant, Hibernate3 permet àcertaines actions d'être approuvées via JACC, et autorisées via JAAS.Cette fonctionnalité optionnelle est construite au dessus de l'architectured'événements.

D'abord, vous devez configurer les listeners d'événements appropriés pourpermettre l'utilisation d'autorisations JAAS.

<listener type="pre-delete"

class="org.hibernate.secure.JACCPreDeleteEventListener"/>

<listener type="pre-update"

class="org.hibernate.secure.JACCPreUpdateEventListener"/>

<listener type="pre-insert"

class="org.hibernate.secure.JACCPreInsertEventListener"/>

<listener type="pre-load"

class="org.hibernate.secure.JACCPreLoadEventListener"/>

Notez que <listener type="..." class="..."/> est juste un raccourcipour <event type="..."><listener class="..."/></event> quand il y aexactement un listener pour un type d'événement particulier.

Ensuite, toujours dans hibernate.cfg.xml, lier les permissions aux rôles :

<grant role="admin" entity-name="User"

actions="insert,update,read"/>

<grant role="su" entity-name="User" actions="*"/>

Les noms de rôle sont les rôles compris par votre fournisseur JAAC.

Page 226: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

212 Hibernate 3.3.1

Page 227: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 213

Chapitre 13. Traitement par paquetUne approche naïve pour insérer 100 000 lignes dans la base dedonnées en utilisant Hibernate pourrait ressembler à ça :

Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();

for ( int i=0; i<100000; i++ ) {

Customer customer = new Customer(.....);

session.save(customer);

}

tx.commit();

session.close();

Ceci devrait s'écrouler avec une OutOfMemoryException quelque part auxalentours de la 50 000ème ligne. C'est parce qu'Hibernate cache toutes lesinstances de Customer nouvellement insérées dans le cache de secondniveau.

Dans ce chapitre nous montrerons comment éviter ce problème. D'abord,cependant, si vous faites des traitements par batch, il est absolument critiqueque vous activiez l'utilisation ds paquet JDBC (NdT : JDBC batching), si vousavez l'intention d'obtenir des performances raisonnables. Configurez la tailledu paquet JDBC avec un nombre raisonnable (disons, 10-50) :

hibernate.jdbc.batch_size 20

Vous pourriez aussi vouloir faire cette sorte de travail dans un traitementoù l'interaction avec le cache de second niveau est complètementdésactivé :

Insertions en paquet

hibernate.cache.use_second_level_cache false

Lorsque vous rendez des nouveaux objets persistants, vous devezrégulièrement appeler flush() et puis clear() sur la session, pourcontrôler la taille du cache de premier niveau.

13.1. Paquet de mises à jour

Pour récupérer et mettre à jour des données les mêmes idéess'appliquent. En plus, vous avez besoin d'utiliser scroll() pour tirer partiedes curseurs côté serveur pour les requêtes qui retournent beaucoup delignes de données.

Page 228: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 13. Traitement par paquet

214 Hibernate 3.3.1

Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();

for ( int i=0; i<100000; i++ ) {

Customer customer = new Customer(.....);

session.save(customer);

if ( i % 20 == 0 ) { //20, same as the JDBC batch size

//flush a batch of inserts and release memory:

session.flush();

session.clear();

}

}

tx.commit();

session.close();

13.2. L'interface StatelessSession

Alternativement, Hibernate fournit une API orientée commande qui peutêtre utilisée avec des flux de données pour et en provenance de la basede données sous la forme d'objets détachés. Une StatelessSessionn'a pas de contexte de persistance associé et ne fournit pas beaucoup desémantique de durée de vie de haut niveau. En particulier, une sessionsans état n'implémente pas de cache de premier niveau et n'interagitpas non plus avec un cache de seconde niveau ou un cache de requêtes.Elle n'implémente pas les transactions ou la vérification sale automatique(NdT : automatic dirty checking). Les opérations réalisées avec unesession sans état ne sont jamais répercutées en cascade sur lesinstances associées. Les collections sont ignorées par une sessionsans état. Les opérations exécutées via une session sans étatoutrepasse le modèle d'événements d'Hibernate et les intercepteurs.Les sessions sans état sont vulnérables aux effets de modificationdes données, ceci est dû au manque de cache de premier niveau. Unesession sans état est une abstraction bas niveau, plus proche de la coucheJDBC sous-jacente.

Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();

ScrollableResults customers = session.getNamedQuery("GetCustomers")

.setCacheMode(CacheMode.IGNORE)

.scroll(ScrollMode.FORWARD_ONLY);

int count=0;

while ( customers.next() ) {

Customer customer = (Customer) customers.get(0);

customer.updateStuff(...);

if ( ++count % 20 == 0 ) {

//flush a batch of updates and release memory:

session.flush();

Page 229: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Notez que dans le code de l'exemple,les intances de Customer retournéespar la requête sont immédiatement

détachées. Elles ne sont jamaisassociées à un contexte de persistance.

Hibernate 3.3.1 215

session.clear();

}

}

tx.commit();

session.close();

13.3. Notez que dans le code de l'exemple, lesintances de Customer retournées par la requêtesont immédiatement détachées. Ellesne sont jamais associées à un contexte depersistance.

Les opérations insert(), update() et delete() définies par l'interfaceStatelessSession sont considérées comme des opérations d'accèsdirect aux lignes de la base de données, ce qui résulte en uneexécution immédiate du SQL INSERT, UPDATE ou DELETE respectif. Delà , elles ont des sémantiques tres différentes des opérations save(),saveOrUpdate() et delete() définies par l'interface Session.

StatelessSession session = sessionFactory.openStatelessSession();

Transaction tx = session.beginTransaction();

ScrollableResults customers = session.getNamedQuery("GetCustomers")

.scroll(ScrollMode.FORWARD_ONLY);

while ( customers.next() ) {

Customer customer = (Customer) customers.get(0);

customer.updateStuff(...);

session.update(customer);

}

tx.commit();

session.close();

Opérations de style DML

Comme déjà discuté avant, le mapping objet/relationnel automatiqueet transparent est intéressé par la gestion de l'état de l'objet. Ceciimplique que l'état de l'objet est disponible en mémoire, d'où manipuler(en utilisant des expressions du langage de manipulation de données -Data Manipulation Language (DML) - SQL) les données directement dansla base n'affectera pas l'état en mémoire. Pourtant, Hibernate fournit desméthodes pour l'exécution d'expression DML de style SQL lesquellessont réalisées à travers le langage de requête d'Hibernate (Chapitre 14,HQL: Langage de requêtage d'Hibernate).

Page 230: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 13. Traitement par paquet

216 Hibernate 3.3.1

13.4. La pseudo-syntaxe pour les expressionsUPDATE et DELETE est : ( UPDATE | DELETE ) FROM?EntityName (WHERE where_conditions)?. Certainspoints sont à noter :

Dans la clause from, le mot-clef FROM est optionnel

Il ne peut y avoir qu'une seule entité nommée dans la clause from ;elle peut optionnellement avoir un alias. Si le nom de l'entité a un alias,alors n'importe quelle référence de propriété doit être qualifiée enayant un alias ; si le nom de l'entité n'a pas d'alias, alors il est illégal pourn'importe quelle référence de propriété d'être qualifiée.

• Aucune jointure (implicite ou explicite) ne peut être spécifiée dans unerequête HQL. Les sous-requêtes peuvent être utilisées dans la clausewhere ; les sous-requêtes, elles-mêmes, peuvent contenir des jointures.

• La clause where est aussi optionnelle.• Par exemple, pour exécuter un UPDATE HQL, utilisez la méthode

Query.executeUpdate() (la méthode est données pour ceux qui sontfamiliers avec PreparedStatement.executeUpdate() de JDBC) :

• Pour exécuter un DELETE HQL, utilisez la même méthodeQuery.executeUpdate() :

La valeur du int retourné par la méthode Query.executeUpdate() indiquele nombre d'entités affectées par l'opération. Considérez que celapeut ou pas corréler le nombre de lignes affectés dans la base dedonnées. Une opération HQL pourrait entraîner l'exécution demultiples expressions SQL réelles, pour des classes filles mappées parjointure (NdT: join-subclass), par exemple. Le nombre retourné indiquele nombre d'entités réelles affectées par l'expression. Retour à l'exemple de la classe fille mappée par jointure, un effacement d'une desclasses filles peut réellement entraîner des suppressions pas seulementdans la table qui mappe la classe fille, mais aussi dans la table "racine" etpotentillement dans les tables des classes filles plus bas dans la hiérarchied'héritage.

Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();

String hqlUpdate = "update Customer c set c.name = :newName where

c.name = :oldName";

// or String hqlUpdate = "update Customer set name = :newName where

name = :oldName";

int updatedEntities = s.createQuery( hqlUpdate )

Page 231: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

La pseudo-syntaxe pour les expressionsUPDATE et DELETE est : ( UPDATE |

DELETE ) FROM? EntityName (WHERE

where_conditions)?. Certains points sont à noter :

Hibernate 3.3.1 217

.setString( "newName", newName )

.setString( "oldName", oldName )

.executeUpdate();

tx.commit();

session.close();

La pseudo-syntaxe pour l'expression INSERT est : INSERT INTO EntityNameproperties_list select_statement. Quelques points sont à noter :

Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();

String hqlVersionedUpdate = "update versioned Customer set name =

:newName where name = :oldName";

int updatedEntities = s.createQuery( hqlUpdate )

.setString( "newName", newName )

.setString( "oldName", oldName )

.executeUpdate();

tx.commit();

session.close();

Seule la forme INSERT INTO ... SELECT ... est supportée ; pas la formeINSERT INTO ... VALUES ... .

La properties_list est analogue à la spécification de la colonne Theproperties_list is analogous to the column speficiation dans l'expressionSQL INSERT. Pour les entités impliquées dans un héritage mappé,seules les propriétés directement définies à ce niveau de classedonné peuvent être utilisées dans properties_list. Les propriétés dela classe mère ne sont pas permises ; et les propriétés des classes fillesn'ont pas de sens. En d'autres mots, les expressions INSERT par nature nonpolymorphiques.

Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();

String hqlDelete = "delete Customer c where c.name = :oldName";

// or String hqlDelete = "delete Customer where name = :oldName";

int deletedEntities = s.createQuery( hqlDelete )

.setString( "oldName", oldName )

.executeUpdate();

tx.commit();

session.close();

select_statement peut être n'importe quelle requête de sélection HQlvalide, avec l'avertissement que les types de retour doivent correspondreaux types attendus par l'insertion. Actuellement, c'est vérifié durant lacompilation de la requête plutôt que la vérification soit reléguéeà la base de données. Notez cependant que cela pourrait poser desproblèmes entre les Types d'Hibernate qui sont équivalents opposé Ã

Page 232: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 13. Traitement par paquet

218 Hibernate 3.3.1

égaux. Cela pourrait poser des problèmes avec des disparités entreune propriété définie comme un org.hibernate.type.DateType et unepropriété définie comme un org.hibernate.type.TimestampType, mêmesi la base de données ne ferait pas de distinction ou ne serait pas capablede gérer la conversion.

Pour la propriéte id, l'expression d'insertion vous donne deux options.Vous pouvez soit spécifier explicitement la propriété id dansproperties_list (auquel cas sa valeur est extraite de l'expression desélection correspondante), soit l'omettre de properties_list (auquelcas une valeur générée est utilisée). Cette dernière option estseulement disponible en utilisant le générateur d'identifiant qui opèredans la base de données ; tenter d'utiliser cette option avec n'importequel type de générateur "en mémoire" causera une exception durantl'analyse. Notez que pour les buts de cette discussion, les générateurs"en base" sont considérés être org.hibernate.id.SequenceGenerator(et ses classes filles) et n'importe quelles implémentations deorg.hibernate.id.PostInsertIdentifierGenerator. L'exception la plusnotable ici est org.hibernate.id.TableHiLoGenerator, qu ne peut pas êtreutilisée parce qu'il ne propose pas un moyen de d'exposer ses valeurs parun select.

• Pour des propriétés mappées comme version ou timestamp,l'expression d'insertion vous donne deux options. Vous pouvez soitspécifier la propriété dans properties_list (auquel cas sa valeurest extraite des expressions select correspondantes), soit l'omettre deproperties_list (auquel cas la valeur de graine (NdT : seed value) définiepar le org.hibernate.type.VersionType est utilisée).

Un exemple d'exécution d'une expression INSERT HQL :

• translator-credits

• For the id property, the insert statement gives you two options. You caneither explicitly specify the id property in the properties_list (in which caseits value is taken from the corresponding select expression) or omit itfrom the properties_list (in which case a generated value is used). Thislater option is only available when using id generators that operate inthe database; attempting to use this option with any "in memory" typegenerators will cause an exception during parsing. Note that for thepurposes of this discussion, in-database generators are considered tobe org.hibernate.id.SequenceGenerator (and its subclasses) and anyimplementors of org.hibernate.id.PostInsertIdentifierGenerator. Themost notable exception here is org.hibernate.id.TableHiLoGenerator,which cannot be used because it does not expose a selectable way to getits values.

Page 233: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

La pseudo-syntaxe pour les expressionsUPDATE et DELETE est : ( UPDATE |

DELETE ) FROM? EntityName (WHERE

where_conditions)?. Certains points sont à noter :

Hibernate 3.3.1 219

• For properties mapped as either version or timestamp, the insertstatement gives you two options. You can either specify the property inthe properties_list (in which case its value is taken from the correspondingselect expressions) or omit it from the properties_list (in which case theseed value defined by the org.hibernate.type.VersionType is used).

An example HQL INSERT statement execution:

Session session = sessionFactory.openSession();

Transaction tx = session.beginTransaction();

String hqlInsert = "insert into DelinquentAccount (id, name) select

c.id, c.name from Customer c where ...";

int createdEntities = s.createQuery( hqlInsert )

.executeUpdate();

tx.commit();

session.close();

Page 234: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

220 Hibernate 3.3.1

Page 235: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 221

Chapitre 14. HQL: Langage derequêtage d'Hibernate

Hibernate fourni un langage d'interrogation extrêmement puissant quiressemble (et c'est voulu) au SQL. Mais ne soyez pas distraits par la syntaxe; HQL est totalement orienté objet, comprenant des notions d'héritage, depolymorphisme et d'association.

14.1. Sensibilité à la casse

Les requêtes sont insensibles à la casse, à l'exception des noms des classesJava et des propriétés. Ainsi, SeLeCT est identique à sELEct et à SELECT maisnet.sf.hibernate.eg.FOO n'est pas identique net.sf.hibernate.eg.Foo etfoo.barSet n'est pas identique à foo.BARSET.

Ce guide utilise les mots clés HQL en minuscule. Certains utilisateurstrouvent les requêtes écrites avec les mots clés en majuscule plus lisibles,mais nous trouvons cette convention pénible lorsqu'elle est lue dans du codeJava.

14.2. La clause from

La requête Hibernate la plus simple est de la forme :

from eg.Cat

qui retourne simplement toutes les instances de la classe eg.Cat. Nousn'avons pas besoin d'habitude de qualifier le nom de la classe, puisqueauto-import est la valeur par défaut. Donc nous écrivons presque toujours :

from Cat

La plupart du temps, vous devrez assigner un alias puisque vous voudrezfaire référence à Cat dans d'autres parties de la requête.

from Cat as cat

Cette requête assigne l'alias cat à l'instance Cat, nous pouvons donc utilisercet alias ailleurs dans la requête. Le mot clé as est optionnel ; nous aurionspu écrire :

from Cat cat

Page 236: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 14. HQL: Langage de requêta...

222 Hibernate 3.3.1

Plusieurs classes peuvent apparaître, ce qui conduira à un produit cartésien(encore appelé jointures croisées).

from Formula, Parameter

from Formula as form, Parameter as param

C'est une bonne pratique que de nommer les alias dans les requêtes enutilisant l'initiale en miniscule, ce qui a le mérite d'être en phase avec lesstandards de nommage Java pour les variables locales (domesticCat).

14.3. Associations et jointures

On peut aussi assigner des alias à des entités associées, ou même auxéléments d'une collection de valeurs, en utilisant un join (jointure).

from Cat as cat

inner join cat.mate as mate

left outer join cat.kittens as kitten

from Cat as cat left join cat.mate.kittens as kittens

from Formula form full join form.parameter param

Les types de jointures supportées sont celles de ANSI SQL

• inner join (jointure fermée)

• left outer join (jointure ouverte par la gauche)

• right outer join (jointure ouverte par la droite)

• full join (jointure ouverte totalement - généralement inutile)

Les constructions des jointures inner join, left outer join et right outerjoin peuvent être abbrégées.

from Cat as cat

join cat.mate as mate

left join cat.kittens as kitten

Nous pouvons soumettre des conditions de jointure supplémentaires enutilisant le mot-clef HQL with.

from Cat as cat

left join cat.kittens as kitten

with kitten.bodyWeight > 10.0

Par ailleurs, une jointure "fetchée" (rapportée) permet d'initialiser lesassociations ou collections de valeurs en même temps que leur objet parent,

Page 237: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Associations et jointures

Hibernate 3.3.1 223

le tout n'utilisant qu'un seul Select. Ceci est particulièrement utile dans le casdes collections. Ce système permet de surcharger les déclarations "lazy" et"outer-join" des fichiers de mapping pour les associations et collections. VoirSection 19.1, « Stratégies de chargement » pour plus d'informations.

from Cat as cat

inner join fetch cat.mate

left join fetch cat.kittens

Une jointure "fetchée" (rapportée) n'a généralement pas besoin de sevoir assigner un alias puisque les objets associés n'ont pas à être utilisésdans les autres clauses. Notez aussi que les objets associés ne sontpas retournés directement dans le résultat de la requête mais l'on peut yaccéder via l'objet parent. La seule raison pour laquelle nous pourrionsavoir besoin d'un alias est si nous récupérions récursivement une collectionsupplémentaire :

from Cat as cat

inner join fetch cat.mate

left join fetch cat.kittens child

left join fetch child.kittens

Notez que la construction de fetch ne peut pas être utilisée dans lesrequêtes appelées par scroll() ou iterate(). fetch ne devrait pas non plusêtre utilisé avec setMaxResults() ou setFirstResult(). fetch ne peut pasnon plus être utilisé avec une condition with ad hoc. Il est possible de créerun produit cartésien par jointure en récupérant plus d'une collection dansune requête, donc faites attention dans ce cas. Récupérer par jointure demultiples collections donne aussi parfois des résultats inattendus pour desmappings de bag, donc soyez prudent lorsque vous formulez vos requêtesdans de tels cas. Finalement, notez que full join fetch et right join fetchne sont pas utiles en général.

Si vous utilisez un chargement retardé pour les propriétés (avec uneinstrumentation par bytecode), il est possible de forcer Hibernate à récupérerles propriétés non encore chargées immédiatement (dans la premièrerequête) en utilisant fetch all properties.

from Document fetch all properties order by name

from Document doc fetch all properties where lower(doc.name) like

'%cats%'

Page 238: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 14. HQL: Langage de requêta...

224 Hibernate 3.3.1

14.4. Formes de syntaxes pour les jointures

HQL supporte deux formes pour joindre les associations: implicite etexplicite.

Les requêtes présentes dans la section précédente utilisent la formeexplicite où le mode clé join est explicitement utilisé dans la clause from.C'est la forme recommandée.

La forme implicite n'utilise pas le mot clé join. A la place, les associationssont "déréférencées" en utilisant le notation '.'. Ces jointures peuventapparaitre dans toutes les clauses. Les jointures implicites résultent en desinner join dans le SQL généré.

from Cat as cat where cat.mate.name like '%s%'

14.5. Refering to identifier property

There are, generally speaking, 2 ways to refer to an entity's identifierproperty:

• The special property (lowercase) id may be used to reference the identifierproperty of an entity provided that entity does not define a non-identifierproperty named id.

• If the entity defines a named identifier property, you may use that propertyname.

References to composite identifier properties follow the same naming rules.If the entity has a non-identifier property named id, the composite identifierproperty can only be referenced by its defined named; otherwise, the specialid property can be used to rerference the identifier property.

Note: this has changed significantly starting in version 3.2.2. In previousversions, id always referred to the identifier property no matter what its actualname. A ramification of that decision was that non-identifier properties namedid could never be referenced in Hibernate queries.

14.6. La clause select

La clause select sélectionne les objets et propriétés qui doivent êtreretournés dans le résultat de la requête. Soit :

select mate

from Cat as cat

inner join cat.mate as mate

Page 239: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

La clause select

Hibernate 3.3.1 225

La requête recherchera les mates liés aux Cats. Vous pouvez explimer larequête d'une manière plus compacte :

select cat.mate from Cat cat

Les requêtes peuvent retourner des propriétés de n'importe quel type, mêmecelles de type composant (component) :

select cat.name from DomesticCat cat

where cat.name like 'fri%'

select cust.name.firstName from Customer as cust

Les requêtes peuvent retourner plusieurs objets et/ou propriétés sous laforme d'un tableau du type Object[],

select mother, offspr, mate.name

from DomesticCat as mother

inner join mother.mate as mate

left outer join mother.kittens as offspr

ou sous la forme d'une List,

select new list(mother, offspr, mate.name)

from DomesticCat as mother

inner join mother.mate as mate

left outer join mother.kittens as offspr

ou sous la forme d'un objet Java typé,

select new Family(mother, mate, offspr)

from DomesticCat as mother

join mother.mate as mate

left join mother.kittens as offspr

à condition que la classe Family possède le constructeur approprié.

Vous pouvez assigner des alias aux expressions sélectionnées en utilisant as:

select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n

from Cat cat

C'est surtout utile lorsque c'est utilisé avec select new map :

select new map( max(bodyWeight) as max, min(bodyWeight) as min,

count(*) as n )

from Cat cat

Page 240: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 14. HQL: Langage de requêta...

226 Hibernate 3.3.1

Cette requête retourne une Map à partir des alias vers les valeurssélectionnées.

14.7. Fonctions d'aggrégation

Les requêtes HQL peuvent aussi retourner le résultat de fonctionsd'aggrégation sur les propriétés :

select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)

from Cat cat

Les fonctions supportées sont

• avg(...), sum(...), min(...), max(...)

• count(*)

• count(...), count(distinct ...), count(all...)

Vous pouvez utiliser des opérateurs arithmétiques, la concaténation, et desfonctions SQL reconnues dans la clause select :

select cat.weight + sum(kitten.weight)

from Cat cat

join cat.kittens kitten

group by cat.id, cat.weight

select firstName||' '||initial||' '||upper(lastName) from Person

Les mots clé distinct et all peuvent être utilisés et ont la même significationqu'en SQL.

select distinct cat.name from Cat cat

select count(distinct cat.name), count(cat) from Cat cat

14.8. Requêtes polymorphiques

Une requête comme:

from Cat as cat

retourne non seuleument les instances de Cat, mais aussi celles des sousclasses comme DomesticCat. Les requêtes Hibernate peuvent nommern'importe quelle classe ou interface Java dans la clause from. La requêteretournera les instances de toutes les classes persistantes qui étendent cetteclasse ou implémente cette interface. La requête suivante retournera tous lesobjets persistants :

Page 241: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

La clause where

Hibernate 3.3.1 227

from java.lang.Object o

L'interface Named peut être implémentée par plusieurs classes persistantes :

from Named n, Named m where n.name = m.name

Notez que ces deux dernières requêtes nécessitent plus d'un SELECT SQL.Ce qui signifie que la clause order by ne trie pas correctement la totalité desrésultats (cela signifie aussi que vous ne pouvez exécuter ces requêtes enappelant Query.scroll()).

14.9. La clause where

La clause where vous permet de réduire la liste des instances retournées. Siaucun alias n'existe, vous pouvez vous référer aux propriétés par leur nom :

from Cat where name='Fritz'

S'il y a un alias, utilisez un nom de propriété qualifié :

from Cat as cat where cat.name='Fritz'

retourne les instances de Cat dont name est égale à 'Fritz'.

select foo

from Foo foo, Bar bar

where foo.startDate = bar.date

retournera les instances de Foo pour lesquelles il existe une instance debar avec la propriété date est égale à la propriété startDate de Foo. Lesexpressions utilisant la navigation rendent la clause where extrêmementpuissante. Soit :

from Cat cat where cat.mate.name is not null

Cette requête se traduit en SQL par une jointure interne à une table. Si voussouhaitez écrire quelque chose comme :

from Foo foo

where foo.bar.baz.customer.address.city is not null

vous finiriez avec une requête qui nécessiterait quatre jointures en SQL.

L'opérateur = peut être utilisé pour comparer aussi bien des propriétés quedes instances :

from Cat cat, Cat rival where cat.mate = rival.mate

Page 242: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 14. HQL: Langage de requêta...

228 Hibernate 3.3.1

select cat, mate

from Cat cat, Cat mate

where cat.mate = mate

La propriété spéciale (en minuscule) id peut être utilisée pour faire référenceà l'identifiant d'un objet (vous pouvez aussi utiliser le nom de cette propriété).

from Cat as cat where cat.id = 123

from Cat as cat where cat.mate.id = 69

La seconde requête est particulièrement efficace. Aucune jointure n'estnécessaire !

Les propriétés d'un identifiant composé peuvent aussi être utilisées.Supposez que Person ait un identifiant composé de country etmedicareNumber.

from bank.Person person

where person.id.country = 'AU'

and person.id.medicareNumber = 123456

from bank.Account account

where account.owner.id.country = 'AU'

and account.owner.id.medicareNumber = 123456

Une fois de plus, la seconde requête ne nécessite pas de jointure.

De même, la propriété spéciale class interroge la valeur discriminanted'une instance dans le cas d'une persistance polymorphique. Le nom d'uneclasse Java incorporée dans la clause where sera traduite par sa valeurdiscriminante.

from Cat cat where cat.class = DomesticCat

Vous pouvez aussi spécifier les propriétés des composants ou typesutilisateurs composés (components, composite user types etc). N'essayezjamais d'utiliser un expression de navigation qui se terminerait parune propriété de type composant (qui est différent d'une propriété d'uncomposant). Par exemple, si store.owner est une entité avec un composantaddress

Un type "any" possède les propriétés spéciales id et class, qui nouspermettent d'exprimer une jointure de la manière suivante (où AuditLog.itemest une propriété mappée avec <any>).

from AuditLog log, Payment payment

where log.item.class = 'Payment' and log.item.id = payment.id

Page 243: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Expressions

Hibernate 3.3.1 229

Dans la requête précédente, notez que log.item.class et payment.classferaient référence à des valeurs de colonnes de la base de donnéescomplètement différentes.

14.10. Expressions

Les expressions permises dans la clause where incluent la plupart deschoses que vous pouvez utiliser en SQL :

• opérateurs mathématiques +, -, *, /

• opérateur de comparaison binaire =, >=, <=, <>, !=, like

• opérateurs logiques and, or, not

• Parenthèses ( ), indiquant un regroupement

• in, not in, between, is null, is not null, is empty, is not empty, memberof and not member of

• "Simple" case, case ... when ... then ... else ... end, and "searched"case, case when ... then ... else ... end

• concatenation de chaîne de caractères ...||... ou concat(...,...)

• current_date(), current_time(), current_timestamp()

• second(...), minute(...), hour(...), day(...), month(...), year(...),

• N'importe quel fonction ou opérateur défini par EJB-QL 3.0 : substring(),trim(), lower(), upper(), length(), locate(), abs(), sqrt(),

bit_length(), mod()

• coalesce() et nullif()

• str() pour convertir des valeurs numériques ou temporelles vers unechaîne de caractères lisible

• cast(... as ...), où le second argument est le nom d'un type Hibernate,et extract(... from ...) si le cast() ANSI et extract() sont supportés parla base de données sous-jacente

• La fonction HQL index(), qui s'applique aux alias d'une collection indexéejointe

• Les fonctions HQL qui s'appliquent expressions représentant descollections : size(), minelement(), maxelement(), minindex(),maxindex(), ainsi que les fonctions spéciales elements() et indices quipeuvent être quantifiées en utilisant some, all, exists, any, in.

• N'importe quelle fonction scalaire supportée par la base de donnéescomme sign(), trunc(), rtrim(), sin()

• Les paramètres positionnels de JDBC ?

• paramètres nommés :name, :start_date, :x1

• littéral SQL 'foo', 69, '1970-01-01 10:00:01.0'

• Constantes Java public static finaleg.Color.TABBY

in et between peuvent être utilisés comme suit :

Page 244: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 14. HQL: Langage de requêta...

230 Hibernate 3.3.1

from DomesticCat cat where cat.name between 'A' and 'B'

from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )

et la forme négative peut être écrite

from DomesticCat cat where cat.name not between 'A' and 'B'

from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )

De même, is null et is not null peuvent être utilisés pour tester les valeursnulle.

Les booléens peuvent être facilement utilisés en déclarant les substitutionsde requêtes dans la configuration Hibernate :

<property name="hibernate.query.substitutions">true 1, false

0</property>

Ce qui remplacera les mots clés true et false par 1 et 0 dans la traductionSQL du HQL suivant :

from Cat cat where cat.alive = true

Vous pouvez tester la taille d'une collection par la propriété spéciale size, oula fonction spéciale size().

from Cat cat where cat.kittens.size > 0

from Cat cat where size(cat.kittens) > 0

Pour les collections indexées, vous pouvez faire référence aux indicesminimum et maximum en utilisant les fonctions minindex and maxindex.De manière similaire, vous pouvez faire référence aux éléments minimumet maximum d'une collection de type basiques en utilisant les fonctionsminelement et maxelement.

from Calendar cal where maxelement(cal.holidays) > current_date

from Order order where maxindex(order.items) > 100

from Order order where minelement(order.items) > 10000

Les fonctions SQL any, some, all, exists, in supportent que leur soientpassées l'élément, l'index d'une collection (fonctions elements et indices) oule résultat d'une sous requête (voir ci dessous).

Page 245: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Expressions

Hibernate 3.3.1 231

select mother from Cat as mother, Cat as kit

where kit in elements(foo.kittens)

select p from NameList list, Person p

where p.name = some elements(list.names)

from Cat cat where exists elements(cat.kittens)

from Player p where 3 > all elements(p.scores)

from Show show where 'fizard' in indices(show.acts)

Notez que l'écriture de - size, elements, indices, minindex, maxindex,minelement, maxelement - peuvent seulement être utilisée dans la clausewhere dans Hibernate3.

Les éléments de collections indexées (arrays, lists, maps) peuvent êtreréférencés via index (dans une clause where seulement) :

from Order order where order.items[0].id = 1234

select person from Person person, Calendar calendar

where calendar.holidays['national day'] = person.birthDay

and person.nationality.calendar = calendar

select item from Item item, Order order

where order.items[ order.deliveredItemIndices[0] ] = item and

order.id = 11

select item from Item item, Order order

where order.items[ maxindex(order.items) ] = item and order.id = 11

L'expression entre [] peut même être une expression arithmétique.

select item from Item item, Order order

where order.items[ size(order.items) - 1 ] = item

HQL propose aussi une fonction index() interne, pour les éléments d'uneassociation one-to-many ou d'une collections de valeurs.

select item, index(item) from Order order

join order.items item

where index(item) < 5

Les fonctions SQL scalaires supportées par la base de données utiliséepeuvent être utilisées

Page 246: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 14. HQL: Langage de requêta...

232 Hibernate 3.3.1

from DomesticCat cat where upper(cat.name) like 'FRI%'

Si vous n'êtes pas encore convaincu par tout cela, imaginez la taille etl'illisibilité qui caractériseraient la transformation SQL de la requête HQLsuivante :

select cust

from Product prod,

Store store

inner join store.customers cust

where prod.name = 'widget'

and store.location.name in ( 'Melbourne', 'Sydney' )

and prod = all elements(cust.currentOrder.lineItems)

Un indice : cela donnerait quelque chose comme

SELECT cust.name, cust.address, cust.phone, cust.id,

cust.current_order

FROM customers cust,

stores store,

locations loc,

store_customers sc,

product prod

WHERE prod.name = 'widget'

AND store.loc_id = loc.id

AND loc.name IN ( 'Melbourne', 'Sydney' )

AND sc.store_id = store.id

AND sc.cust_id = cust.id

AND prod.id = ALL(

SELECT item.prod_id

FROM line_items item, orders o

WHERE item.order_id = o.id

AND cust.current_order = o.id

)

14.11. La clause order by

La liste retounée par la requête peut être triée par n'importe quelle propriétéde la classe ou du composant retourné :

from DomesticCat cat

order by cat.name asc, cat.weight desc, cat.birthdate

Le mot optionnel asc ou desc indique respectivement si le tri doit êtrecroissant ou décroissant.

Page 247: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

La clause group by

Hibernate 3.3.1 233

14.12. La clause group by

Si la requête retourne des valeurs aggrégées, celles ci peuvent êtregroupées par propriété ou composant :

select cat.color, sum(cat.weight), count(cat)

from Cat cat

group by cat.color

select foo.id, avg(name), max(name)

from Foo foo join foo.names name

group by foo.id

Une clause having est aussi permise.

select cat.color, sum(cat.weight), count(cat)

from Cat cat

group by cat.color

having cat.color in (eg.Color.TABBY, eg.Color.BLACK)

Les fonctions SQL et les fonctions d'aggrégations sont permises dans lesclauses having et order by, si elles sont supportées par la base de données(ce que ne fait pas MySQL par exemple).

select cat

from Cat cat

join cat.kittens kitten

group by cat.id, cat.name, cat.other, cat.properties

having avg(kitten.weight) > 100

order by count(kitten) asc, sum(kitten.weight) desc

Notez que ni la clause group by ni la clause order by ne peuvent contenird'expressions arithmétiques.

14.13. Sous-requêtes

Pour les bases de données le supportant, Hibernate supporte les sousrequêtes dans les requêtes. Une sous requête doit être entre parenthèses(souvent pour un appel à une fonction d'agrégation SQL) Même les sousrequêtes corrélées (celles qui font référence à un alias de la requêteprincipale) sont supportées.

from Cat as fatcat

where fatcat.weight > (

select avg(cat.weight) from DomesticCat cat

)

from DomesticCat as cat

Page 248: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 14. HQL: Langage de requêta...

234 Hibernate 3.3.1

where cat.name = some (

select name.nickName from Name as name

)

from Cat as cat

where not exists (

from Cat as mate where mate.mate = cat

)

from DomesticCat as cat

where cat.name not in (

select name.nickName from Name as name

)

select cat.id, (select max(kit.weight) from cat.kitten kit)

from Cat as cat

Notez que les sous-requêtes HQL peuvent arriver seulememnt dans lesclauses select ou where.

Note that subqueries can also utilize row value constructor syntax. SeeSection 14.18, « Row value constructor syntax » for more details.

14.14. Exemples HQL

Les requêtes Hibernate peuvent être relativement puissantes et complexes.En fait, la puissance du langage de requêtage est l'un des avantagesprincipaux d'Hibernate. Voici quelques exemples très similaires aux requêtesque nous avons utilisées lors d'un récent projet. Notez que la plupart desrequêtes que vous écrirez seront plus simples que les exemples suivantes !

La requête suivante retourne l'id de commande (order), le nombred'articles (items) et la valeur totale de la commande (order) pour toutesles commandes non payées d'un client (customer) particulier pour un totalminimum donné, le tout trié par la valeur totale. La requête SQL généréesur les tables ORDER, ORDER_LINE, PRODUCT, CATALOG et PRICE est composée dequatre jointures interne ainsi que d'une sous-requête (non corrélée).

select order.id, sum(price.amount), count(item)

from Order as order

join order.lineItems as item

join item.product as product,

Catalog as catalog

join catalog.prices as price

where order.paid = false

and order.customer = :customer

and price.product = product

and catalog.effectiveDate < sysdate

and catalog.effectiveDate >= all (

Page 249: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Exemples HQL

Hibernate 3.3.1 235

select cat.effectiveDate

from Catalog as cat

where cat.effectiveDate < sysdate

)

group by order

having sum(price.amount) > :minAmount

order by sum(price.amount) desc

Quel monstre ! En principe, nous ne sommes pas très fan dessous-requêtes, la requête ressemblait donc plutôt à cela :

select order.id, sum(price.amount), count(item)

from Order as order

join order.lineItems as item

join item.product as product,

Catalog as catalog

join catalog.prices as price

where order.paid = false

and order.customer = :customer

and price.product = product

and catalog = :currentCatalog

group by order

having sum(price.amount) > :minAmount

order by sum(price.amount) desc

La requête suivante compte le nombre de paiements (payments) pourchaque status, en excluant les paiements dans le status AWAITING_APPROVALoù le changement de status le plus récent à été fait par l'utilisateur courant.En SQL, cette requête effectue deux jointures internes et des sous requêtescorrélées sur les tables PAYMENT, PAYMENT_STATUS et PAYMENT_STATUS_CHANGE.

select count(payment), status.name

from Payment as payment

join payment.currentStatus as status

join payment.statusChanges as statusChange

where payment.status.name <> PaymentStatus.AWAITING_APPROVAL

or (

statusChange.timeStamp = (

select max(change.timeStamp)

from PaymentStatusChange change

where change.payment = payment

)

and statusChange.user <> :currentUser

)

group by status.name, status.sortOrder

order by status.sortOrder

Si nous avions mappé la collection statusChanges comme une liste, au lieud'un ensemble, la requête aurait été plus facile à écrire.

select count(payment), status.name

Page 250: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 14. HQL: Langage de requêta...

236 Hibernate 3.3.1

from Payment as payment

join payment.currentStatus as status

where payment.status.name <> PaymentStatus.AWAITING_APPROVAL

or payment.statusChanges[ maxIndex(payment.statusChanges) ].user

<> :currentUser

group by status.name, status.sortOrder

order by status.sortOrder

La requête qui suit utilise la fonction de MS SQL isNull() pour retourner tousles comptes (accounts) et paiements (payments) impayés pour l'organisationà laquelle l'uilisateur (user) courant appartient. Elle est traduite en SQL partrois jointures internes, une jointure externe ainsi qu'une sous requête surles tables ACCOUNT, PAYMENT, PAYMENT_STATUS, ACCOUNT_TYPE, ORGANIZATION etORG_USER.

select account, payment

from Account as account

left outer join account.payments as payment

where :currentUser in elements(account.holder.users)

and PaymentStatus.UNPAID = isNull(payment.currentStatus.name,

PaymentStatus.UNPAID)

order by account.type.sortOrder, account.accountNumber,

payment.dueDate

Pour d'autres base de données, nous aurions dû faire sans la sous-requête(corrélée).

select account, payment

from Account as account

join account.holder.users as user

left outer join account.payments as payment

where :currentUser = user

and PaymentStatus.UNPAID = isNull(payment.currentStatus.name,

PaymentStatus.UNPAID)

order by account.type.sortOrder, account.accountNumber,

payment.dueDate

14.15. Mise à jour et suppression

HQL supporte maintenant les expressions update, delete et insert ...select .... Voir Section 13.4, « La pseudo-syntaxe pour les expressionsUPDATE et DELETE est : ( UPDATE | DELETE ) FROM? EntityName (WHEREwhere_conditions)?. Certains points sont à noter : » pour les détails.

14.16. Trucs & Astuces

Vous pouvez compter le nombre de résultats d'une requête sans lesretourner :

Page 251: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Trucs & Astuces

Hibernate 3.3.1 237

( (Integer) session.createQuery("select count(*) from

....").iterate().next() ).intValue()

Pour trier les résultats par la taille d'une collection, utilisez la requêtesuivante :

select usr.id, usr.name

from User as usr

left join usr.messages as msg

group by usr.id, usr.name

order by count(msg)

Si votre base de données supporte les sous-requêtes, vous pouvez placerdes conditions sur la taille de la sélection dans la clause where de votrerequête:

from User usr where size(usr.messages) >= 1

Si votre base de données ne supporte pas les sous-requêtes, utilisez larequête suivante :

select usr.id, usr.name

from User usr.name

join usr.messages msg

group by usr.id, usr.name

having count(msg) >= 1

Cette solution ne peut pas retourner un User avec zéro message à cause dela jointure interne, la forme suivante peut donc être utile :

select usr.id, usr.name

from User as usr

left join usr.messages as msg

group by usr.id, usr.name

having count(msg) = 0

Les propriétés d'un JavaBean peuvent être injectées dans les paramètresnommés d'un requête :

Query q = s.createQuery("from foo Foo as foo where foo.name=:name

and foo.size=:size");

q.setProperties(fooBean); // fooBean has getName() and getSize()

List foos = q.list();

Les collections sont paginables via l'utilisation de l'interface Query avec unfiltre :

Query q = s.createFilter( collection, "" ); // the trivial filter

q.setMaxResults(PAGE_SIZE);

Page 252: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 14. HQL: Langage de requêta...

238 Hibernate 3.3.1

q.setFirstResult(PAGE_SIZE * pageNumber);

List page = q.list();

Les éléments d'une collection peuvent être triés ou groupés en utilisant unfiltre de requête :

Collection orderedCollection = s.filter( collection, "order by

this.amount" );

Collection counts = s.filter( collection, "select this.type,

count(this) group by this.type" );

Vous pouvez récupérer la taille d'une collection sans l'initialiser :

( (Integer) session.createQuery("select count(*) from

....").iterate().next() ).intValue();

14.17. translator-credits

Components might be used in just about every way that simple value typescan be used in HQL queries. They can appear in the select clause:

select p.name from Person p

select p.name.first from Person p

where the Person's name property is a component. Components can also beused in the where clause:

from Person p where p.name = :name

from Person p where p.name.first = :firstName

Components can also be used in the order by clause:

from Person p order by p.name

from Person p order by p.name.first

Another common use of components is in row value constructors.

14.18. Row value constructor syntax

HQL supports the use of ANSI SQL row value constructor syntax(sometimes called tuple syntax), even though the underlying databasemay not support that notion. Here we are generally referring to multi-valuedcomparisons, typically associated with components. Consider an entityPerson which defines a name component:

Page 253: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Row value constructor syntax

Hibernate 3.3.1 239

from Person p where p.name.first='John' and

p.name.last='Jingleheimer-Schmidt'

That's valid syntax, although a little verbose. It be nice to make this a bitmore concise and use row value constructor syntax:

from Person p where p.name=('John', 'Jingleheimer-Schmidt')

It can also be useful to specify this in the select clause:

select p.name from Person p

Another time using row value constructor syntax can be beneficial is whenusing subqueries needing to compare against multiple values:

from Cat as cat

where not ( cat.name, cat.color ) in (

select cat.name, cat.color from DomesticCat cat

)

One thing to consider when deciding if you want to use this syntax is that thequery will be dependent upon the ordering of the component sub-propertiesin the metadata.

Page 254: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

240 Hibernate 3.3.1

Page 255: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 241

Chapitre 15. Requêtes par critèresHibernate offre une API d'interrogation par critères intuitive et extensible.

15.1. Créer une instance de Criteria

L'interface net.sf.hibernate.Criteria représente une requête sur une classepersistente donnée. La Session fournit les instances de Criteria.

Criteria crit = sess.createCriteria(Cat.class);

crit.setMaxResults(50);

List cats = crit.list();

15.2. Restriction du résultat

Un criterion (critère de recherche) est une instance del'interface org.hibernate.criterion.Criterion. La classeorg.hibernate.criterion.Restrictions définit des méthodes pour obtenir destypes de Criterion pré-définis.

List cats = sess.createCriteria(Cat.class)

.add( Restrictions.like("name", "Fritz%") )

.add( Restrictions.between("weight", minWeight, maxWeight) )

.list();

Les restrictions peuvent être goupées de manière logique.

List cats = sess.createCriteria(Cat.class)

.add( Restrictions.like("name", "Fritz%") )

.add( Restrictions.or(

Restrictions.eq( "age", new Integer(0) ),

Restrictions.isNull("age")

) )

.list();

List cats = sess.createCriteria(Cat.class)

.add( Restrictions.in( "name", new String[] { "Fritz", "Izi",

"Pk" } ) )

.add( Restrictions.disjunction()

.add( Restrictions.isNull("age") )

.add( Restrictions.eq("age", new Integer(0) ) )

.add( Restrictions.eq("age", new Integer(1) ) )

.add( Restrictions.eq("age", new Integer(2) ) )

) )

.list();

Page 256: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 15. Requêtes par critères

242 Hibernate 3.3.1

Il y a plusieurs types de criterion pré-définis (sous classes de Restriction),mais l'une d'entre elle particulièrement utile vous permet de spécifierdirectement du SQL.

List cats = sess.createCriteria(Cat.class)

.add( Restrictions.sqlRestriction("lower({alias}.name) like

lower(?)", "Fritz%", Hibernate.STRING) )

.list();

La zone {alias} sera remplacée par l'alias de colonne de l'entité que l'onsouhaite intérroger.

Une autre approche pour obtenir un criterion est de le récupérer d'uneinstance de Property. Vous pouvez créer une Property en appelantProperty.forName().

Property age = Property.forName("age");

List cats = sess.createCriteria(Cat.class)

.add( Restrictions.disjunction()

.add( age.isNull() )

.add( age.eq( new Integer(0) ) )

.add( age.eq( new Integer(1) ) )

.add( age.eq( new Integer(2) ) )

) )

.add( Property.forName("name").in( new String[] { "Fritz",

"Izi", "Pk" } ) )

.list();

15.3. Trier les résultats

Vous pouvez trier les résultats en utilisant org.hibernate.criterion.Order.

List cats = sess.createCriteria(Cat.class)

.add( Restrictions.like("name", "F%")

.addOrder( Order.asc("name") )

.addOrder( Order.desc("age") )

.setMaxResults(50)

.list();

List cats = sess.createCriteria(Cat.class)

.add( Property.forName("name").like("F%") )

.addOrder( Property.forName("name").asc() )

.addOrder( Property.forName("age").desc() )

.setMaxResults(50)

.list();

Page 257: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Associations

Hibernate 3.3.1 243

15.4. Associations

Vous pouvez facilement spécifier des contraintes sur des entités liées, pardes associations en utilisant createCriteria().

List cats = sess.createCriteria(Cat.class)

.add( Restrictions.like("name", "F%") )

.createCriteria("kittens")

.add( Restrictions.like("name", "F%") )

.list();

Notez que la seconde createCriteria() retourne une nouvelle instance deCriteria, qui se rapporte aux éléments de la collection kittens.

La forme alternative suivante est utile dans certains cas.

List cats = sess.createCriteria(Cat.class)

.createAlias("kittens", "kt")

.createAlias("mate", "mt")

.add( Restrictions.eqProperty("kt.name", "mt.name") )

.list();

(createAlias() ne crée pas de nouvelle instance de Criteria.)

Notez que les collections kittens contenues dans les instances de Catretournées par les deux précédentes requêtes ne sont pas pré-filtréespar les critères ! Si vous souhaitez récupérer uniquement les kittens quicorrespondent à la criteria, vous devez utiliser ResultTransformer.

List cats = sess.createCriteria(Cat.class)

.createCriteria("kittens", "kt")

.add( Restrictions.eq("name", "F%") )

.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP)

.list();

Iterator iter = cats.iterator();

while ( iter.hasNext() ) {

Map map = (Map) iter.next();

Cat cat = (Cat) map.get(Criteria.ROOT_ALIAS);

Cat kitten = (Cat) map.get("kt");

}

15.5. Peuplement d'associations de manièredynamique

Vous pouvez spéficier au moment de l'exécution le peuplement d'uneassociation en utilisant setFetchMode() (c'est-à-dire le chargement decelle-ci). Cela permet de surcharger les valeurs "lazy" et "outer-join" dumapping.

Page 258: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 15. Requêtes par critères

244 Hibernate 3.3.1

List cats = sess.createCriteria(Cat.class)

.add( Restrictions.like("name", "Fritz%") )

.setFetchMode("mate", FetchMode.EAGER)

.setFetchMode("kittens", FetchMode.EAGER)

.list();

Cette requête recherchera mate et kittens via les jointures externes. VoirSection 19.1, « Stratégies de chargement » pour plus d'informations.

15.6. Requêtes par l'exemple

La classe org.hibernate.criterion.Example vous permet de construire uncritère suivant une instance d'objet donnée.

Cat cat = new Cat();

cat.setSex('F');

cat.setColor(Color.BLACK);

List results = session.createCriteria(Cat.class)

.add( Example.create(cat) )

.list();

Les propriétés de type version, identifiant et association sont ignorées. Pardéfaut, les valeurs null sont exclues.

Vous pouvez ajuster la stratégie d'utilisation de valeurs de l'Exemple.

Example example = Example.create(cat)

.excludeZeroes() //exclude zero valued properties

.excludeProperty("color") //exclude the property named "color"

.ignoreCase() //perform case insensitive string

comparisons

.enableLike(); //use like for string comparisons

List results = session.createCriteria(Cat.class)

.add(example)

.list();

Vous pouvez utiliser les "exemples" pour des critères sur les objets associés.

List results = session.createCriteria(Cat.class)

.add( Example.create(cat) )

.createCriteria("mate")

.add( Example.create( cat.getMate() ) )

.list();

15.7. Projections, agrégation et regroupement

La classe org.hibernate.criterion.Projections est une fabrique d'instancesde Projection. Nous appliquons une projection sur une requête en appelantsetProjection().

Page 259: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Projections, agrégation et regroupement

Hibernate 3.3.1 245

List results = session.createCriteria(Cat.class)

.setProjection( Projections.rowCount() )

.add( Restrictions.eq("color", Color.BLACK) )

.list();

List results = session.createCriteria(Cat.class)

.setProjection( Projections.projectionList()

.add( Projections.rowCount() )

.add( Projections.avg("weight") )

.add( Projections.max("weight") )

.add( Projections.groupProperty("color") )

)

.list();

Il n'y a pas besoin de "group by" explicite dans une requête par critère.Certains types de projection sont définis pour être des projections deregroupement, lesquels apparaissent aussi dans la clause group by SQL.

Un alias peut optionnellement être assigné à une projection, ainsi la valeurprojetée peut être référencée dans des restrictions ou des tris. Voici deuxfaçons différentes de faire ça :

List results = session.createCriteria(Cat.class)

.setProjection( Projections.alias(

Projections.groupProperty("color"), "colr" ) )

.addOrder( Order.asc("colr") )

.list();

List results = session.createCriteria(Cat.class)

.setProjection( Projections.groupProperty("color").as("colr") )

.addOrder( Order.asc("colr") )

.list();

Les méthodes alias() et as() enveloppe simplement une instance deprojection dans une autre instance (aliasée) de Projection. Comme unraccourci, vous pouvez assignez un alias lorsque vous ajoutez la projection àla liste de projections :

List results = session.createCriteria(Cat.class)

.setProjection( Projections.projectionList()

.add( Projections.rowCount(), "catCountByColor" )

.add( Projections.avg("weight"), "avgWeight" )

.add( Projections.max("weight"), "maxWeight" )

.add( Projections.groupProperty("color"), "color" )

)

.addOrder( Order.desc("catCountByColor") )

.addOrder( Order.desc("avgWeight") )

.list();

List results = session.createCriteria(Domestic.class, "cat")

Page 260: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 15. Requêtes par critères

246 Hibernate 3.3.1

.createAlias("kittens", "kit")

.setProjection( Projections.projectionList()

.add( Projections.property("cat.name"), "catName" )

.add( Projections.property("kit.name"), "kitName" )

)

.addOrder( Order.asc("catName") )

.addOrder( Order.asc("kitName") )

.list();

Vous pouvez aussi utiliser Property.forName() pour formuler des projections :

List results = session.createCriteria(Cat.class)

.setProjection( Property.forName("name") )

.add( Property.forName("color").eq(Color.BLACK) )

.list();

List results = session.createCriteria(Cat.class)

.setProjection( Projections.projectionList()

.add( Projections.rowCount().as("catCountByColor") )

.add( Property.forName("weight").avg().as("avgWeight") )

.add( Property.forName("weight").max().as("maxWeight") )

.add( Property.forName("color").group().as("color" )

)

.addOrder( Order.desc("catCountByColor") )

.addOrder( Order.desc("avgWeight") )

.list();

15.8. Requêtes et sous-requêtes détachées

La classe DetachedCriteria vous laisse créer une requête en dehors de laportée de la session, et puis l'exécuter plus tard en utilisant n'importe quelleSession arbitraire.

DetachedCriteria query = DetachedCriteria.forClass(Cat.class)

.add( Property.forName("sex").eq('F') );

Session session = ....;

Transaction txn = session.beginTransaction();

List results =

query.getExecutableCriteria(session).setMaxResults(100).list();

txn.commit();

session.close();

Une DetachedCriteria peut aussi être utilisée pour exprimer unesous-requête. Des instances de criterion impliquant des sous-requêtespeuvent être obtenues via Subqueries ou Property.

DetachedCriteria avgWeight = DetachedCriteria.forClass(Cat.class)

.setProjection( Property.forName("weight").avg() );

session.createCriteria(Cat.class)

.add( Property.forName("weight").gt(avgWeight) )

Page 261: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Requêtes par identifiant naturel

Hibernate 3.3.1 247

.list();

DetachedCriteria weights = DetachedCriteria.forClass(Cat.class)

.setProjection( Property.forName("weight") );

session.createCriteria(Cat.class)

.add( Subqueries.geAll("weight", weights) )

.list();

Même des requêtes corrélées sont possibles :

DetachedCriteria avgWeightForSex =

DetachedCriteria.forClass(Cat.class, "cat2")

.setProjection( Property.forName("weight").avg() )

.add( Property.forName("cat2.sex").eqProperty("cat.sex") );

session.createCriteria(Cat.class, "cat")

.add( Property.forName("weight").gt(avgWeightForSex) )

.list();

15.9. Requêtes par identifiant naturel

Pour la plupart des requêtes, incluant les requêtes par critère, le cachede requêtes n'est pas très efficace, parce que l'invalidation du cache derequêtes arrive trop souvent. Cependant, il y a une sorte spéciale derequête où nous pouvons optimiser l'algorithme d'invalidation du cache : lesrecherches sur une clef naturelle constante. Dans certaines applications,cette sorte de requête se produit fréquemment. L'API de critère fournit uneprovision spéciale pour ce cas d'utilisation.

D'abord vous devriez mapper la clef naturelle de votre entité en utilisant<natural-id>, et activer l'utilisation du cache de second niveau.

<class name="User">

<cache usage="read-write"/>

<id name="id">

<generator class="increment"/>

</id>

<natural-id>

<property name="name"/>

<property name="org"/>

</natural-id>

<property name="password"/>

</class>

Notez que cette fonctionnalité n'est pas prévue pour l'utilisation avec desentités avec des clefs naturelles mutables.

Ensuite, activez le cache de requête d'Hibernate.

Maintenant Restrictions.naturalId() nous permet de rendre l'utilisation del'algorithme de cache plus efficace.

Page 262: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 15. Requêtes par critères

248 Hibernate 3.3.1

session.createCriteria(User.class)

.add( Restrictions.naturalId()

.set("name", "gavin")

.set("org", "hb")

).setCacheable(true)

.uniqueResult();

Page 263: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 249

Chapitre 16. SQL natifVous pouvez aussi écrire vos requêtes dans le dialecte SQL natif de votrebase de données. Ceci est utile si vous souhaitez utiliser les fonctionnalitésspécifiques de votre base de données comme le mot clé CONNECT d'Oracle.Cette fonctionnalité offre par ailleurs un moyen de migration plus propre etdoux d'une application basée sur SQL/JDBC vers une application Hibernate.

Hibernate3 vous permet de spécifier du SQL écrit à la main (incluant lesprocédures stockées) pour toutes les opérations de création, mise à jour,suppression et chargement.

16.1. Utiliser une SQLQuery

L'exécution des requêtes en SQL natif est contrôlée par l'interface SQLQuery,laquelle est obtenue en appelant Session.createSQLQuery(). Dans des casextrêmement simples, nous pouvons utiliser la forme suivante :

16.1.1. Scalar queries

The most basic SQL query is to get a list of scalars (values).

sess.createSQLQuery("SELECT * FROM CATS").list();

sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();

These will both return a List of Object arrays (Object[]) with scalar values foreach column in the CATS table. Hibernate will use ResultSetMetadata todeduce the actual order and types of the returned scalar values.

To avoid the overhead of using ResultSetMetadata or simply to be moreexplicit in what is returned one can use addScalar().

sess.createSQLQuery("SELECT * FROM CATS")

.addScalar("ID", Hibernate.LONG)

.addScalar("NAME", Hibernate.STRING)

.addScalar("BIRTHDATE", Hibernate.DATE)

This query specified:

• the SQL query string

• the columns and types to return

This will still return Object arrays, but now it will not use ResultSetMetadatabut will instead explicitly get the ID, NAME and BIRTHDATE column asrespectively a Long, String and a Short from the underlying resultset. This

Page 264: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 16. SQL natif

250 Hibernate 3.3.1

also means that only these three columns will be returned, even though thequery is using * and could return more than the three listed columns.

It is possible to leave out the type information for all or some of the scalars.

sess.createSQLQuery("SELECT * FROM CATS")

.addScalar("ID", Hibernate.LONG)

.addScalar("NAME")

.addScalar("BIRTHDATE")

This is essentially the same query as before, but now ResultSetMetaData isused to decide the type of NAME and BIRTHDATE where as the type of ID isexplicitly specified.

How the java.sql.Types returned from ResultSetMetaData is mapped toHibernate types is controlled by the Dialect. If a specific type is not mappedor does not result in the expected type it is possible to customize it via callsto registerHibernateType in the Dialect.

16.1.2. Entity queries

The above queries were all about returning scalar values, basically returningthe "raw" values from the resultset. The following shows how to get entityobjects from a native sql query via addEntity().

sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class);

sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM

CATS").addEntity(Cat.class);

This query specified:

• the SQL query string

• the entity returned by the query

Assuming that Cat is mapped as a class with the columns ID, NAME andBIRTHDATE the above queries will both return a List where each element isa Cat entity.

If the entity is mapped with a many-to-one to another entity it is required toalso return this when performing the native query, otherwise a databasespecific "column not found" error will occur. The additional columns willautomatically be returned when using the * notation, but we prefer to beexplicit as in the following example for a many-to-one to a Dog:

sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM

CATS").addEntity(Cat.class);

Page 265: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Handling associations and collections

Hibernate 3.3.1 251

This will allow cat.getDog() to function properly.

16.1.3. Handling associations and collections

It is possible to eagerly join in the Dog to avoid the possible extra roundtrip forinitializing the proxy. This is done via the addJoin() method, which allows youto join in an association or collection.

sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID,

D_NAME FROM CATS c, DOGS d WHERE c.DOG_ID = d.D_ID")

.addEntity("cat", Cat.class)

.addJoin("cat.dog");

In this example the returned Cat's will have their dog property fully initializedwithout any extra roundtrip to the database. Notice that we added a aliasname ("cat") to be able to specify the target property path of the join. It ispossible to do the same eager joining for collections, e.g. if the Cat had aone-to-many to Dog instead.

sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME,

CAT_ID FROM CATS c, DOGS d WHERE c.ID = d.CAT_ID")

.addEntity("cat", Cat.class)

.addJoin("cat.dogs");

At this stage we are reaching the limits of what is possible with native querieswithout starting to enhance the sql queries to make them usable in Hibernate;the problems starts to arise when returning multiple entities of the same typeor when the default alias/column names are not enough.

16.1.4. Returning multiple entities

Until now the result set column names are assumed to be the same as thecolumn names specified in the mapping document. This can be problematicfor SQL queries which join multiple tables, since the same column namesmay appear in more than one table.

Column alias injection is needed in the following query (which most likely willfail):

sess.createSQLQuery("SELECT c.*, m.* FROM CATS c, CATS m WHERE

c.MOTHER_ID = c.ID")

.addEntity("cat", Cat.class)

.addEntity("mother", Cat.class)

The intention for this query is to return two Cat instances per row, a catand its mother. This will fail since there is a conflict of names since they are

Page 266: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 16. SQL natif

252 Hibernate 3.3.1

mapped to the same column names and on some databases the returnedcolumn aliases will most likely be on the form "c.ID", "c.NAME", etc. whichare not equal to the columns specified in the mappings ("ID" and "NAME").

The following form is not vulnerable to column name duplication:

sess.createSQLQuery("SELECT {cat.*}, {mother.*} FROM CATS c, CATS m

WHERE c.MOTHER_ID = c.ID")

.addEntity("cat", Cat.class)

.addEntity("mother", Cat.class)

This query specified:

• the SQL query string, with placeholders for Hibernate to inject columnaliases

• the entities returned by the query

The {cat.*} and {mother.*} notation used above is a shorthand for "allproperties". Alternatively, you may list the columns explicitly, but even in thiscase we let Hibernate inject the SQL column aliases for each property. Theplaceholder for a column alias is just the property name qualified by the tablealias. In the following example, we retrieve Cats and their mothers from adifferent table (cat_log) to the one declared in the mapping metadata. Noticethat we may even use the property aliases in the where clause if we like.

String sql = "SELECT ID as {c.id}, NAME as {c.name}, " +

"BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother},

{mother.*} " +

"FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID";

List loggedCats = sess.createSQLQuery(sql)

.addEntity("cat", Cat.class)

.addEntity("mother", Cat.class).list()

16.1.4.1. Alias and property references

For most cases the above alias injection is needed, but for queriesrelating to more complex mappings like composite properties, inheritancediscriminators, collections etc. there are some specific aliases to use to allowHibernate to inject the proper aliases.

The following table shows the different possibilities of using the aliasinjection. Note: the alias names in the result are examples, each alias willhave a unique and probably different name when used.

Page 267: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Returning non-managed entities

Hibernate 3.3.1 253

Tableau 16.1. Alias injection names

Description Syntax Example

A simpleproperty

{[aliasname].[propertyname]A_NAME as {item.name}

A compositeproperty

{[aliasname].[componentname].[propertyname]}CURRENCY as {item.amount.currency},

VALUE as {item.amount.value}

Discriminator ofan entity

{[aliasname].class}DISC as {item.class}

All properties ofan entity

{[aliasname].*} {item.*}

A collection key {[aliasname].key}ORGID as {coll.key}

The id of ancollection

{[aliasname].id}EMPID as {coll.id}

The element ofan collection

{[aliasname].element}XID as {coll.element}

roperty of theelement in thecollection

{[aliasname].element.[propertyname]}NAME as {coll.element.name}

All properties ofthe element inthe collection

{[aliasname].element.*}{coll.element.*}

All propertiesof the thecollection

{[aliasname].*} {coll.*}

16.1.5. Returning non-managed entities

It is possible to apply a ResultTransformer to native sql queries. Allowing it toe.g. return non-managed entities.

sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")

.setResultTransformer(Transformers.aliasToBean(CatDTO.class))

This query specified:

• the SQL query string

• a result transformer

Page 268: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 16. SQL natif

254 Hibernate 3.3.1

The above query will return a list of CatDTO which has been instantiatedand injected the values of NAME and BIRTHNAME into its correspondingproperties or fields.

16.1.6. Handling inheritance

Native sql queries which query for entities that is mapped as part of aninheritance must include all properties for the baseclass and all it subclasses.

16.1.7. Parameters

Native sql queries support positional as well as named parameters:

Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME

like ?").addEntity(Cat.class);

List pusList = query.setString(0, "Pus%").list();

query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like

:name").addEntity(Cat.class);

List pusList = query.setString("name", "Pus%").list();

16.2. Requêtes SQL nommées

Les requêtes SQL nommées peuvent être définies dans le document demapping et appelées exactement de la même manière qu'un requête HQLnommée. Dans ce cas, nous n'avons pas besoin d'appeler addEntity().

<sql-query name="persons">

<return alias="person" class="eg.Person"/>

SELECT person.NAME AS {person.name},

person.AGE AS {person.age},

person.SEX AS {person.sex}

FROM PERSON person

WHERE person.NAME LIKE :namePattern

</sql-query>

List people = sess.getNamedQuery("persons")

.setString("namePattern", namePattern)

.setMaxResults(50)

.list();

Les éléments <return-join> et <load-collection> sont respectivementutilisés pour lier des associations et définir des requêtes qui initialisent descollections.

<sql-query name="personsWith">

<return alias="person" class="eg.Person"/>

<return-join alias="address" property="person.mailingAddress"/>

Page 269: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Requêtes SQL nommées

Hibernate 3.3.1 255

SELECT person.NAME AS {person.name},

person.AGE AS {person.age},

person.SEX AS {person.sex},

address.STREET AS {address.street},

address.CITY AS {address.city},

address.STATE AS {address.state},

address.ZIP AS {address.zip}

FROM PERSON person

JOIN ADDRESS address

ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'

WHERE person.NAME LIKE :namePattern

</sql-query>

Une requête SQL nommée peut retourner une valeur scalaire. Vousdevez spécifier l'alias de colonne et le type Hibernate utilisant l'élément<return-scalar> :

<sql-query name="mySqlQuery">

<return-scalar column="name" type="string"/>

<return-scalar column="age" type="long"/>

SELECT p.NAME AS name,

p.AGE AS age,

FROM PERSON p WHERE p.NAME LIKE 'Hiber%'

</sql-query>

You can externalize the resultset mapping informations in a <resultset>element to either reuse them across several named queries or through thesetResultSetMapping() API.

<resultset name="personAddress">

<return alias="person" class="eg.Person"/>

<return-join alias="address" property="person.mailingAddress"/>

</resultset>

<sql-query name="personsWith" resultset-ref="personAddress">

SELECT person.NAME AS {person.name},

person.AGE AS {person.age},

person.SEX AS {person.sex},

address.STREET AS {address.street},

address.CITY AS {address.city},

address.STATE AS {address.state},

address.ZIP AS {address.zip}

FROM PERSON person

JOIN ADDRESS address

ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'

WHERE person.NAME LIKE :namePattern

</sql-query>

You can alternatively use the resultset mapping information in your hbm filesdirectly in java code.

List cats = sess.createSQLQuery(

Page 270: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 16. SQL natif

256 Hibernate 3.3.1

"select {cat.*}, {kitten.*} from cats cat, cats kitten where

kitten.mother = cat.id"

)

.setResultSetMapping("catAndKitten")

.list();

16.2.1. Utilisation de return-property pour spécifierexplicitement les noms des colonnes/alias

Avec <return-property> vous pouvez explicitement dire à Hibernate quelsalias de colonne utiliser, plutot que d'employer la syntaxe {} pour laisserHibernate injecter ses propres alias.

<sql-query name="mySqlQuery">

<return alias="person" class="eg.Person">

<return-property name="name" column="myName"/>

<return-property name="age" column="myAge"/>

<return-property name="sex" column="mySex"/>

</return>

SELECT person.NAME AS myName,

person.AGE AS myAge,

person.SEX AS mySex,

FROM PERSON person WHERE person.NAME LIKE :name

</sql-query>

<return-property> fonctionne aussi avec de multiple colonnes. Celarésout une limitation de la syntaxe {} qui ne peut pas permettre une bonnegranularité des propriétés multi-colonnes.

<sql-query name="organizationCurrentEmployments">

<return alias="emp" class="Employment">

<return-property name="salary">

<return-column name="VALUE"/>

<return-column name="CURRENCY"/>

</return-property>

<return-property name="endDate" column="myEndDate"/>

</return>

SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS

{emp.employer},

STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},

REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE,

CURRENCY

FROM EMPLOYMENT

WHERE EMPLOYER = :id AND ENDDATE IS NULL

ORDER BY STARTDATE ASC

</sql-query>

Notez que dans cet exemple nous avons utilisé <return-property> encombinaison avec la syntaxe {} pour l'injection. Cela autorise les utilisateursà choisir comment ils veulent référencer les colonnes et les propriétés.

Page 271: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Utilisation de procédures stockées pour lesrequêtes

Hibernate 3.3.1 257

Si votre mapping a un discriminant vous devez utiliser<return-discriminator> pour spécifier la colonne discriminante.

16.2.2. Utilisation de procédures stockées pour lesrequêtes

Hibernate 3 introduit le support des requêtes via procédures stockées etles fonctions. La documentation suivante est valable pour les deux. Lesprocédures stockées/fonctions doivent retourner l'ensemble de résultats entant que premier paramètre sortant (NdT: "out-parameter") pour être capablede fonctionner avec Hibernate. Un exemple d'une telle procédure stockée enOracle 9 et version supérieure :

CREATE OR REPLACE FUNCTION selectAllEmployments

RETURN SYS_REFCURSOR

AS

st_cursor SYS_REFCURSOR;

BEGIN

OPEN st_cursor FOR

SELECT EMPLOYEE, EMPLOYER,

STARTDATE, ENDDATE,

REGIONCODE, EID, VALUE, CURRENCY

FROM EMPLOYMENT;

RETURN st_cursor;

END;

Pour utiliser cette requête dans Hibernate vous avez besoin de la mapper viaune requête nommée.

<sql-query name="selectAllEmployees_SP" callable="true">

<return alias="emp" class="Employment">

<return-property name="employee" column="EMPLOYEE"/>

<return-property name="employer" column="EMPLOYER"/>

<return-property name="startDate" column="STARTDATE"/>

<return-property name="endDate" column="ENDDATE"/>

<return-property name="regionCode" column="REGIONCODE"/>

<return-property name="id" column="EID"/>

<return-property name="salary">

<return-column name="VALUE"/>

<return-column name="CURRENCY"/>

</return-property>

</return>

{ ? = call selectAllEmployments() }

</sql-query>

Notez que les procédures stockées retournent, pour le moment, seulementdes scalaires et des entités. <return-join> et <load-collection> ne sont passupportés.

Page 272: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 16. SQL natif

258 Hibernate 3.3.1

16.2.2.1. Règles/limitations lors de l'utilisation des procéduresstockées

Pur utiliser des procédures stockées avec Hibernate, les procéduresdoivent suivre certaines règles. Si elles ne suivent pas ces règles, ellesne sont pas utilisables avec Hibernate. Si vous voulez encore utiliser cesprocédures vous devez les exécuter via session.connection(). Les règlessont différentes pour chaque base de données, puisque les vendeursde base de données ont des sémantiques/syntaxes différentes pour lesprocédures stockées.

Les requêtes de procédures stockées ne peuvent pas être paginées avecsetFirstResult()/setMaxResults().

Recommended call form is standard SQL92: { ? = callfunctionName(<parameters>) } or { ? = call procedureName(<parameters>}.Native call syntax is not supported.

Pour Oracle les règles suivantes s'appliquent :

• La procédure doit retourner un ensemble de résultats. Le prmeierparamètre d'une procédure doit être un OUT qui retourne un ensemble derésultats. Ceci est fait en retournant un SYS_REFCURSOR dans Oracle 9 ou 10.Dans Oracle vous avez besoin de définir un type REF CURSOR.

Pour Sybase ou MS SQL server les règles suivantes s'appliquent :

• La procédure doit retourner un ensemble de résultats. Notez que commeces serveurs peuvent retourner de multiples ensembles de résultats etmettre à jour des compteurs, Hibernate itérera les résultats et prendra lepremier résultat qui est un ensemble de résultat comme valeur de retour.Tout le reste sera ignoré.

• Si vous pouvez activer SET NOCOUNT ON dans votre procédure, elle seraprobablement plus efficace, mais ce n'est pas une obligation.

16.3. SQL personnalisé pour créer, mettre à jour eteffacer

Hibernate3 peut utiliser des expression SQL personnalisées pour desopérations de création, de mise à jour, et de suppression. Les objetspersistants les classes et les collections dans Hibernate contiennent déjàun ensemble de chaînes de caractères générées lors de la configuration(insertsql, deletesql, updatesql, etc). Les tages de mapping <sql-insert>,<sql-delete>, et <sql-update> surchargent ces chaînes de caractères :

Page 273: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

SQL personnalisé pour créer, mettre à jouret effacer

Hibernate 3.3.1 259

<class name="Person">

<id name="id">

<generator class="increment"/>

</id>

<property name="name" not-null="true"/>

<sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ?

)</sql-insert>

<sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE

ID=?</sql-update>

<sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>

</class>

Le SQL est directement exécuté dans votre base de données, donc vousêtes libre d'utiliser le dialecte que vous souhaitez. Cela réduira bien sûr laportabilité de votre mapping si vous utilisez du SQL spécifique à votre basede données.

Les procédures stockées sont supportées si l'attribut callable est paramétré:

<class name="Person">

<id name="id">

<generator class="increment"/>

</id>

<property name="name" not-null="true"/>

<sql-insert callable="true">{call createPerson (?,

?)}</sql-insert>

<sql-delete callable="true">{? = call deletePerson

(?)}</sql-delete>

<sql-update callable="true">{? = call updatePerson (?,

?)}</sql-update>

</class>

L'ordre des paramètres positionnels est actuellement vital, car ils doivent êtredans la même séquence qu'Hibernate les attend.

Vous pouvez voir l'ordre attendu en activant les journaux de debug pourle niveau org.hibernate.persister.entity level. Avec ce niveau activé,Hibernate imprimera le SQL statique qui est utilisé pour créer, mettre à jour,supprimer, etc. des entités. (Pour voir la séquence attendue, rappelez-vousde ne pas inclure votre SQL personnalisé dans les fichiers de mapping demanière à surcharger le SQL statique généré par Hibernate.)

Les procédures stockées sont dans la plupart des cas (lire : il vaut mieuxle faire) requises pour retourner le nombre de lignes insérées/mises àjour/supprimées, puisque Hibernate fait quelques vérifications de succèslors de l'exécution de l'expression. Hibernate inscrit toujours la premièreexpression comme un paramètre de sortie numérique pour les opérationsCUD :

Page 274: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 16. SQL natif

260 Hibernate 3.3.1

CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN

VARCHAR2)

RETURN NUMBER IS

BEGIN

update PERSON

set

NAME = uname,

where

ID = uid;

return SQL%ROWCOUNT;

END updatePerson;

16.4. SQL personnalisé pour le chargement

Vous pouvez aussi déclarer vos propres requêtes SQL (ou HQL) pour lechargement d'entité :

<sql-query name="person">

<return alias="pers" class="Person" lock-mode="upgrade"/>

SELECT NAME AS {pers.name}, ID AS {pers.id}

FROM PERSON

WHERE ID=?

FOR UPDATE

</sql-query>

Ceci est juste une déclaration de requête nommée, comme vu plus tôt. Vouspouvez référencer cette requête nommée dans un mapping de classe :

<class name="Person">

<id name="id">

<generator class="increment"/>

</id>

<property name="name" not-null="true"/>

<loader query-ref="person"/>

</class>

Ceci fonctionne même avec des procédures stockées.

Vous pouvez même définir une requête pour le chargement d'une collection :

<set name="employments" inverse="true">

<key/>

<one-to-many class="Employment"/>

<loader query-ref="employments"/>

</set>

<sql-query name="employments">

Page 275: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

SQL personnalisé pour le chargement

Hibernate 3.3.1 261

<load-collection alias="emp" role="Person.employments"/>

SELECT {emp.*}

FROM EMPLOYMENT emp

WHERE EMPLOYER = :id

ORDER BY STARTDATE ASC, EMPLOYEE ASC

</sql-query>

Vous pourriez même définir un chargeur d'entité qui charge une collectionpar jointure :

<sql-query name="person">

<return alias="pers" class="Person"/>

<return-join alias="emp" property="pers.employments"/>

SELECT NAME AS {pers.*}, {emp.*}

FROM PERSON pers

LEFT OUTER JOIN EMPLOYMENT emp

ON pers.ID = emp.PERSON_ID

WHERE ID=?

</sql-query>

Page 276: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

262 Hibernate 3.3.1

Page 277: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 263

Chapitre 17. Filtrer les donnéesHibernate3 fournit une nouvelle approche innovatrice pour gérer desdonnées avec des règles de "visibilité". Un filtre Hibernate est un filtre global,nommé, paramétré qui peut être activé ou désactivé pour une sessionHibernate particulière.

17.1. Filtres Hibernate

Hibernate3 ajoute la capacité de prédéfinir des critères de filtre et d'attacherces filtres à une classe ou à une collection. Un critère de filtre est la facultéde définir une clause de restriction très similaire à l'attribut "where" existantdisponible sur une classe et divers éléments d'une collection. Mis à part queces conditions de filtre peuvent être paramétrées. L'application peut alorsprendre la décision à l'exécution si des filtres donnés devraient être activés etquels devraient être leurs paramètres. Des filtres peuvent être utilisés commedes vues de base de données, mais paramétrées dans l'application.

Afin d'utiliser des filtres, ils doivent d'abord être définis, puis attachés auxéléments de mapping appropriés. Pour définir un filtre, utilisez l'élément<filter-def/> dans un élément <hibernate-mapping/> :

<filter-def name="myFilter">

<filter-param name="myFilterParam" type="string"/>

</filter-def>

Puis, ce filtre peut être attaché à une classe :

<class name="myClass" ...>

...

<filter name="myFilter" condition=":myFilterParam =

MY_FILTERED_COLUMN"/>

</class>

ou à une collection :

<set ...>

<filter name="myFilter" condition=":myFilterParam =

MY_FILTERED_COLUMN"/>

</set>

ou même aux deux (ou à plusieurs de chaque) en même temps.

The methods on Session are: enableFilter(String filterName),getEnabledFilter(String filterName), and disableFilter(String

Page 278: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 17. Filtrer les données

264 Hibernate 3.3.1

filterName). By default, filters are not enabled for a given session; they mustbe explcitly enabled through use of the Session.enableFilter() method,which returns an instance of the Filter interface. Using the simple filterdefined above, this would look like:

session.enableFilter("myFilter").setParameter("myFilterParam",

"some-value");

Notez que des méthodes sur l'interface org.hibernate.Filter autorisent lechaînage de beaucoup de méthodes communes d'Hibernate.

Un exemple complet, utilisant des données temporelles avec une structurede date d'enregistrement effectif :

<filter-def name="effectiveDate">

<filter-param name="asOfDate" type="date"/>

</filter-def>

<class name="Employee" ...>

...

<many-to-one name="department" column="dept_id"

class="Department"/>

<property name="effectiveStartDate" type="date"

column="eff_start_dt"/>

<property name="effectiveEndDate" type="date"

column="eff_end_dt"/>

...

<!--

Note that this assumes non-terminal records have an

eff_end_dt set to

a max db date for simplicity-sake

-->

<filter name="effectiveDate"

condition=":asOfDate BETWEEN eff_start_dt and

eff_end_dt"/>

</class>

<class name="Department" ...>

...

<set name="employees" lazy="true">

<key column="dept_id"/>

<one-to-many class="Employee"/>

<filter name="effectiveDate"

condition=":asOfDate BETWEEN eff_start_dt and

eff_end_dt"/>

</set>

</class>

Puis, afin de s'assurer que vous pouvez toujours récupérer lesenregistrements actuellement effectifs, activez simplement le filtre sur lasession avant de récupérer des données des employés :

Page 279: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Filtres Hibernate

Hibernate 3.3.1 265

Session session = ...;

session.enableFilter("effectiveDate").setParameter("asOfDate", new

Date());

List results = session.createQuery("from Employee as e where

e.salary > :targetSalary")

.setLong("targetSalary", new Long(1000000))

.list();

Dans le HQL ci-dessus, bien que nous ayons seulement mentionné unecontrainte de salaire sur les resultats, à cause du filtre activé, la requêteretournera seulement les employés actuellement actifs qui ont un salairesupérieur à un million de dollars.

A noter : si vous prévoyez d'utiliser des filtres avec des jointures externes(soit à travers HQL, soit par le chargement) faites attention à la direction del'expression de condition. Il est plus sûr de la positionner pour les jointuresexternes à gauche ; en général, placez le paramètre d'abord, suivi du(des)nom(s) de colonne après l'opérateur.

translator-credits

<filter-def name="myFilter" condition="abc > xyz">...</filter-def>

<filter-def name="myOtherFilter">abc=xyz</filter-def>

This default condition will then be used whenever the filter is attached tosomething without specifying a condition. Note that this means you can givea specific condition as part of the attachment of the filter which overrides thedefault condition in that particular case.

Page 280: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

266 Hibernate 3.3.1

Page 281: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 267

Chapitre 18. Mapping XMLNotez que cette fonctionnalité est expérimentale dans Hibernate 3.0 et est endéveloppement extrêmement actif.

18.1. Travailler avec des données XML

Hibernate vous laisse travailler avec des données XML persistantes de lamême manière que vous travaillez avec des POJOs persistants. Un arbreXML peut être vu comme une autre manière de représenter les donnéesrelationnelles au niveau objet, à la place des POJOs.

Hibernate supporte dom4j en tant qu'API pour la manipulation des arbresXML. Vous pouvez écrire des requêtes qui récupèrent des arbres dom4jà partie de la base de données, et avoir toutes les modifications que vousfaites sur l'arbre automatiquement synchronisées dans la base de données.Vous pouvez même prendre un document XML, l'analyser en utilisant dom4j,et l'écrire dans la base de données via les opérations basiques d'Hibernate :persist(), saveOrUpdate(), merge(), delete(), replicate() (merge() n'estpas encore supporté).

Cette fonctionnalité a plusieurs applications dont l'import/export de données,l'externalisation d'entités via JMS ou SOAP et les rapports XSLT.

Un simple mapping peut être utilisé pour simultanément mapper lespropriétés d'une classe et les noeuds d'un document XML vers la base dedonnées, ou, si il n'y a pas de classe à mapper, il peut être utilisé juste pourmapper le XML.

18.1.1. Spécifier le mapping XML et le mapping d'uneclasse ensemble

Voici un exemple de mapping d'un POJO et du XML simultanément :

<class name="Account"

table="ACCOUNTS"

node="account">

<id name="accountId"

column="ACCOUNT_ID"

node="@id"/>

<many-to-one name="customer"

column="CUSTOMER_ID"

node="customer/@id"

embed-xml="false"/>

Page 282: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 18. Mapping XML

268 Hibernate 3.3.1

<property name="balance"

column="BALANCE"

node="balance"/>

...

</class>

18.1.2. Spécifier seulement un mapping XML

Voici un exemple dans lequel il n'y a pas de class POJO :

<class entity-name="Account"

table="ACCOUNTS"

node="account">

<id name="id"

column="ACCOUNT_ID"

node="@id"

type="string"/>

<many-to-one name="customerId"

column="CUSTOMER_ID"

node="customer/@id"

embed-xml="false"

entity-name="Customer"/>

<property name="balance"

column="BALANCE"

node="balance"

type="big_decimal"/>

...

</class>

Ce mapping vous permet d'accéder aux données comme un arbre dom4j, oucomme un graphe de paire nom de propriété/valeur (Maps java). Les nomsdes propriétés sont des constructions purement logiques qui peuvent êtreréférées des dans requêtes HQL.

18.2. Métadonnées du mapping XML

Plusieurs éléments du mapping Hibernate acceptent l'attribut node. Ceci vouspermet de spécifier le nom d'un attribut XML ou d'un élément qui contient lapropriété ou les données de l'entité. Le format de l'attribut node doit être undes suivants :

• "element-name" - mappe vers l'élément XML nommé

Page 283: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Métadonnées du mapping XML

Hibernate 3.3.1 269

• "@attribute-name" - mappe vers l'attribut XML nommé

• "." - mappe vers le parent de l'élément

• "element-name/@attribute-name" - mappe vers l'élément nommé del'attribut nommé

Pour des collections et de simples associations valuées, il y a un attributembed-xml supplémentaire. Si embed-xml="true", qui est la valeur par défaut,l'arbre XML pour l'entité associée (ou la collection des types de valeurs)sera embarquée directement dans l'arbre XML pour l'entité qui possèdel'association. Sinon, si embed-xml="false", alors seule la valeur de l'identifiantréférencé apparaîtra dans le XML pour de simples associations de points, etles collections n'appraîtront simplement pas.

Vous devriez faire attention à ne pas laisser embed-xml="true" pour tropd'associations, puisque XML ne traite pas bien les liens circurlaires.

<class name="Customer"

table="CUSTOMER"

node="customer">

<id name="id"

column="CUST_ID"

node="@id"/>

<map name="accounts"

node="."

embed-xml="true">

<key column="CUSTOMER_ID"

not-null="true"/>

<map-key column="SHORT_DESC"

node="@short-desc"

type="string"/>

<one-to-many entity-name="Account"

embed-xml="false"

node="account"/>

</map>

<component name="name"

node="name">

<property name="firstName"

node="first-name"/>

<property name="initial"

node="initial"/>

<property name="lastName"

node="last-name"/>

</component>

...

</class>

Page 284: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 18. Mapping XML

270 Hibernate 3.3.1

dans ce cas, nous avons décidé d'embarquer la collection d'identifiantsde compte, mais pas les données actuelles du compte. La requête HQLsuivante :

from Customer c left join fetch c.accounts where c.lastName like

:lastName

devrait retourner l'ensemble de données suivant :

<customer id="123456789">

<account short-desc="Savings">987632567</account>

<account short-desc="Credit Card">985612323</account>

<name>

<first-name>Gavin</first-name>

<initial>A</initial>

<last-name>King</last-name>

</name>

...

</customer>

Si vous positionnez embed-xml="true" sur le mapping <one-to-many>, lesdonnées pourraient ressembler plus à ça :

<customer id="123456789">

<account id="987632567" short-desc="Savings">

<customer id="123456789"/>

<balance>100.29</balance>

</account>

<account id="985612323" short-desc="Credit Card">

<customer id="123456789"/>

<balance>-2370.34</balance>

</account>

<name>

<first-name>Gavin</first-name>

<initial>A</initial>

<last-name>King</last-name>

</name>

...

</customer>

18.3. Manipuler des données XML

Relisons et mettons à jour des documents XML dans l'application. Nousfaisons ça en obtenant une session dom4j :

Document doc = ....;

Session session = factory.openSession();

Session dom4jSession = session.getSession(EntityMode.DOM4J);

Transaction tx = session.beginTransaction();

Page 285: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Manipuler des données XML

Hibernate 3.3.1 271

List results = dom4jSession

.createQuery("from Customer c left join fetch c.accounts where

c.lastName like :lastName")

.list();

for ( int i=0; i<results.size(); i++ ) {

//add the customer data to the XML document

Element customer = (Element) results.get(i);

doc.add(customer);

}

tx.commit();

session.close();

Session session = factory.openSession();

Session dom4jSession = session.getSession(EntityMode.DOM4J);

Transaction tx = session.beginTransaction();

Element cust = (Element) dom4jSession.get("Customer", customerId);

for ( int i=0; i<results.size(); i++ ) {

Element customer = (Element) results.get(i);

//change the customer name in the XML and database

Element name = customer.element("name");

name.element("first-name").setText(firstName);

name.element("initial").setText(initial);

name.element("last-name").setText(lastName);

}

tx.commit();

session.close();

Il est extrêmement utile de combiner cette fonctionnalité avec l'opérationreplicate() d'Hibernate pour implémenter des imports/exports de donnéesXML.

Page 286: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

272 Hibernate 3.3.1

Page 287: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 273

Chapitre 19. Améliorer lesperformances

19.1. Stratégies de chargement

Une stratégie de chargement est une stratégie qu'Hibernate va utiliser pourrécupérer des objets associés si l'application à besoin de naviguer à traversune association. Les stratégies de chargement peuvent être déclarées dansles méta-données de l'outil de mapping objet relationnel ou surchargées parune requête de type HQL ou Criteria particulière.

Hibernate3 définit les stratégies de chargement suivantes :

• Chargement par jointure - Hibernate récupère l'instance associée ou lacollection dans un même SELECT, en utilisant un OUTER JOIN.

• Chargement par select - Un second SELECT est utilisé pour récupérerl'instance associée ou la collection. A moins que vous ne désactiviezexplicitement le chargement tardif en spécifiant lazy="false", ce secondselect ne sera exécuté que lorsque vous accéderez réellement àl'association.

• Chargement par sous-select - Un second SELECT est utilisé pour récupérerles associations pour toutes les entités récupérées dans une requête ouun chargement préalable. A moins que vous ne désactiviez explicitementle chargement tardif en spécifiant lazy="false", ce second select ne seraexécuté que lorsque vous accéderez réellement à l'association.

• Chargement par lot - Il s'agit d'une stratégie d'optimisation pour lechargement par select - Hibernate récupère un lot d'instances ou decollections en un seul SELECT en spécifiant une liste de clé primaire ou declé étrangère.

Hibernate fait également la distinction entre :

• Chargement immédiat - Une association, une collection ou un attribut estchargé immédiatement lorsque l'objet auquel appartient cet élément estchargé.

• Chargement tardif d'une collection - Une collection est chargée lorquel'application invoque une méthode sur cette collection (il s'agit du mode dechargement par défaut pour les collections).

• Chargement "super tardif" d'une collection - les éléments de la collectionsont récupérés individuellement depuis la base de données lorsque

Page 288: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 19. Améliorer les performances

274 Hibernate 3.3.1

nécessaire. Hibernate essaie de ne pas charger toute la collection enmémoire sauf si cela est absolument nécessaire (bien adapté aux trèsgrandes collections).

• Chargement par proxy - une association vers un seul objet est chargéelorsqu'une méthode autre que le getter sur l'identifiant est appelée surl'objet associé.

• Chargement "sans proxy" - une association vers un seul objet est chargéelorsque l'on accède à cet objet. Par rapport au chargement par proxy,cette approche est moins tardif (l'association est quand même chargéemême si on n'accède qu'à l'identifiant) mais plus transparente car il n'ya pas de proxy visible dans l'application. Cette approche requiert uneinstrumentation du bytecode à la compilation et est rarement nécessaire.

• Chargement tardif des attributs - Un attribut ou un objet associé seul estchargé lorsque l'on y accède. Cette approche requiert une instrumentationdu bytecode à la compilation et est rarement nécessaire.

Nous avons ici deux notions orthogonales : quand l'association estchargée et comment (quelle requête SQL est utilisée). Il ne faut pasconfondre les deux. Le mode de chargement est utilisé pour améliorer lesperformances. On peut utiliser le mode tardif pour définir un contrat surquelles données sont toujours accessibles sur une instance détachée d'uneclasse particulière.

19.1.1. Travailler avec des associations chargéestardivement

Par défaut, Hibernate3 utilise le chargement tardif par select pour lescollections et le chargement tardif par proxy pour les associations versun seul objet. Ces valeurs par défaut sont valables pour la plupart desassociations dans la plupart des applications.

Note : si vous définissez hibernate.default_batch_fetch_size, Hibernate vautiliser l'optimisation du chargement par lot pour le chargement tardif (cetteoptimisation peut aussi être activée à un niveau de granularité plus fin).

Cependant, le chargement tardif pose un problème qu'il faut connaitre.L'accès à une association définie comme "tardive", hors du contexte d'unesession hibernate ouverte, va conduire à une exception. Par exemple :

s = sessions.openSession();

Transaction tx = s.beginTransaction();

User u = (User) s.createQuery("from User u where u.name=:userName")

.setString("userName", userName).uniqueResult();

Page 289: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Personnalisation des stratégies dechargement

Hibernate 3.3.1 275

Map permissions = u.getPermissions();

tx.commit();

s.close();

Integer accessLevel = (Integer) permissions.get("accounts"); //

Error!

Etant donné que la collection des permissions n'a pas été initialisée avantque la Session soit fermée, la collection n'est pas capable de se charger.Hibernate ne supporte pas le chargement tardif pour des objets détachés.La solution à ce problème est de déplacer le code qui lit la collection avant le"commit" de la transaction.

Une autre alternative est d'utiliser une collection ou une association non"tardive" en spécifiant lazy="false" dans le mapping de l'association.Cependant il est prévu que le chargement tardif soit utilisé pour quasimenttoutes les collections ou associations. Si vous définissez trop d'associtionsnon "tardives" dans votre modèle objet, Hibernate va finir par devoir chargertoute la base de données en mémoire à chaque transaction !

D'un autre côté, on veut souvent choisir un chargement par jointure (quiest par défaut non tardif) à la place du chargement par select dans unetransaction particulière. Nous allons maintenant voir comment adapter lesstratégies de chargement. Dans Hibernate3 les mécanismes pour choisir unestratégie de chargement sont identiques que l'on ait une association vers unobjet simple ou vers une collection.

19.1.2. Personnalisation des stratégies de chargement

Le chargement par select (mode par défaut) est très vulnérable au problèmedu N+1 selects, du coup vous pouvez avoir envie d'activer le chargement parjointure dans les fichiers de mapping :

<set name="permissions"

fetch="join">

<key column="userId"/>

<one-to-many class="Permission"/>

</set

<many-to-one name="mother" class="Cat" fetch="join"/>

La stratégie de chargement définie à l'aide du mot fetch dans les fichiers demapping affecte :

• La récupération via get() ou load()

• La récupération implicite lorsque l'on navigue à travers une association

Page 290: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 19. Améliorer les performances

276 Hibernate 3.3.1

• Les requêtes de type Criteria

• Les requêtes HQL si l'on utilise le chargement par subselect

Quelle que soit la stratégie de chargement que vous utilisez, la partiedu graphe d'objets qui est définie comme non "tardive" sera chargée enmémoire. Cela peut mener à l'exécution de plusieurs selects successifs pourune seule requête HQL.

On n'utilise pas souvent les documents de mapping pour adapter lechargement. Au lieu de cela, on conserve le comportement par défaut eton le surcharge pour une transaction particulière en utilisant left joinfetch dans les requêtes HQL. Cela indique à hibernate à Hibernate decharger l'association de manière agressive lors du premier select en utilisantune jointure externe. Dans l'API Criteria vous pouvez utiliser la méthodesetFetchMode(FetchMode.JOIN)

Si vous ne vous sentez pas prêt à modifier la stratégie de chargement utilisépar get() ou load(), vous pouvez juste utiliser une requête de type Criteriacomme par exemple :

User user = (User) session.createCriteria(User.class)

.setFetchMode("permissions", FetchMode.JOIN)

.add( Restrictions.idEq(userId) )

.uniqueResult();

(Il s'agit de l'équivalent pour Hibernate de ce que d'autres outils de mappingappellent un "fetch plan" ou "plan de chargement")

Une autre manière complètement différente d'éviter le problème des N+1selects est d'utiliser le cache de second niveau.

19.1.3. Proxys pour des associations vers un seul objet

Le chargement tardif des collections est implémenté par Hibernate enutilisant ses propres implémentations pour des collections persistantes. Sil'on veut un chargement tardif pour des associations vers un seul objet métieril faut utiliser un autre mécanisme. L'entité qui est pointée par l'associationdoit être masquée derrière un proxy. Hibernate implémente l'initialisationtardive des proxys sur des objets persistents via une mise à jour à chaud dubytecode (à l'aide de l'excellente librairie CGLIB).

Par défaut, Hibernate génère des proxys (au démarrage) pour toutes lesclasses persistantes et les utilise pour activer le chargement tardif desassociations many-to-one et one-to-one.

Le fichier de mapping peut déclarer une interface qui sera utilisée par leproxy d'interfaçage pour cette classe à l'aide de l'attribut proxy. Par défaut

Page 291: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Proxys pour des associations vers un seulobjet

Hibernate 3.3.1 277

Hibernate utilises une sous classe de la classe persistante. Il faut que lesclasses pour lesquelles on ajoute un proxy implémentent un constructeur pardéfaut de visibilité au moins package. Ce constructeur est recommandé pourtoutes les classes persistantes !

Il y a quelques précautions à prendre lorsque l'on étend cette approche à desclasses polymorphiques, exemple :

<class name="Cat" proxy="Cat">

......

<subclass name="DomesticCat">

.....

</subclass>

</class>

Tout d'abord, les instances de Cat ne pourront jamais être "castées"en DomesticCat, même si l'instance sous jacente est une instance deDomesticCat :

Cat cat = (Cat) session.load(Cat.class, id); // instantiate a proxy

(does not hit the db)

if ( cat.isDomesticCat() ) { // hit the db to

initialize the proxy

DomesticCat dc = (DomesticCat) cat; // Error!

....

}

Deuxièmement, il est possible de casser la notion d'== des proxy.

Cat cat = (Cat) session.load(Cat.class, id); //

instantiate a Cat proxy

DomesticCat dc =

(DomesticCat) session.load(DomesticCat.class, id); //

acquire new DomesticCat proxy!

System.out.println(cat==dc); // false

Cette situation n'est pas si mauvaise qu'il n'y parait. Même si nous avonsdeux références à deux objets proxys différents, l'instance de base seraquand même le même objet :

cat.setWeight(11.0); // hit the db to initialize the proxy

System.out.println( dc.getWeight() ); // 11.0

Troisièmement, vous ne pourrez pas utiliser un proxy CGLIB pour une classefinal ou pour une classe contenant la moindre méthode final.

Enfin, si votre objet persistant obtient une ressource à l'instanciation (parexample dans les initialiseurs ou dans le contructeur par défaut), alors ces

Page 292: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 19. Améliorer les performances

278 Hibernate 3.3.1

ressources seront aussi obtenues par le proxy. La classe proxy est vraimentune sous classe de la classe persistante.

Ces problèmes sont tous dus aux limitations fondamentales du modèled'héritage unique de Java. Si vous souhaitez éviter ces problèmes, vosclasses persistantes doivent chacune implémenter une interface qui déclareses méthodes métier. Vous devriez alors spécifier ces interfaces dans lefichier de mapping :

<class name="CatImpl" proxy="Cat">

......

<subclass name="DomesticCatImpl" proxy="DomesticCat">

.....

</subclass>

</class>

où CatImpl implémente l'interface Cat et DomesticCatImpl implémentel'interface DomesticCat. Ainsi, des proxys pour les instances de Cat etDomesticCat pourraient être retournées par load() ou iterate() (Notez quelist() ne retourne généralement pas de proxy).

Cat cat = (Cat) session.load(CatImpl.class, catid);

Iterator iter = session.createQuery("from CatImpl as cat where

cat.name='fritz'").iterate();

Cat fritz = (Cat) iter.next();

Les relations sont aussi initialisées tardivement. Ceci signifie que vous devezdéclarer chaque propriété comme étant de type Cat, et non CatImpl.

Certaines opérations ne nécessitent pas l'initialisation du proxy

• equals(), si la classe persistante ne surcharge pas equals()

• hashCode(), si la classe persistante ne surcharge pas hashCode()

• Le getter de l'identifiant

Hibernate détectera les classes qui surchargent equals() ou hashCode().

Eh choisissant lazy="no-proxy" au lieu de lazy="proxy" qui est la valeurpar défaut, il est possible d'éviter les problèmes liés au transtypage. Ilfaudra alors une instrumentation du bytecode à la compilation et toutes lesopérations résulterons immédiatement en une initialisation du proxy.

19.1.4. Initialisation des collections et des proxys

Une exception de type LazyInitializationException sera renvoyée parhibernate si une collection ou un proxy non initialisé est accédé en dehors de

Page 293: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Initialisation des collections et des proxys

Hibernate 3.3.1 279

la portée de la Session, e.g. lorsque l'entité à laquelle appartient la collectionou qui a une référence vers le proxy est dans l'état "détachée".

Parfois, nous devons nous assurer qu'un proxy ou une collectionest initialisée avant de fermer la Session. Bien sûr, nous pouvonstoujours forcer l'initialisation en appelant par exemple cat.getSex() oucat.getKittens().size(). Mais ceci n'est pas très lisible pour les personnesparcourant le code et n'est pas très générique.

Les méthodes statiques Hibernate.initialize() etHibernate.isInitialized() fournissent à l'application un moyen de travailleravec des proxys ou des collections initialisés. Hibernate.initialize(cat)forcera l'initialisation d'un proxy de cat, si tant est que sa Session est ouverte.Hibernate.initialize( cat.getKittens() ) a le même effet sur la collectionkittens.

Une autre option est de conserver la Session ouverte jusqu'à ce quetoutes les collections et tous les proxys aient été chargés. Dans certainesarchitectures applicatives, particulièrement celles ou le code d'accès auxdonnées via hiberante et le code qui utilise ces données sont dans descouches applicatives différentes ou des processus physiques différents, ilpeut devenir problématique de garantir que la Session est ouverte lorsqu'unecollection est initialisée. Il y a deux moyens de traiter ce problème :

• Dans une application web, un filtre de servlet peut être utilisé pour fermerla Session uniquement lorsque la requête a été entièrement traitée, lorsquele rendu de la vue est fini (il s'agit du pattern Open Session in View). Biensûr, cela demande plus d'attention à la bonne gestion des exceptions del'application. Il est d'une importance vitale que la Session soit fermée et latransaction terminée avant que l'on rende la main à l'utilisateur même siune exception survient durant le traitement de la vue. Voir le wiki Hibernatepour des exemples sur le pattern "Open Session in View".

• Dans une application avec une couche métier séparée, la couchecontenant la logique métier doit "préparer" toutes les collections quiseront nécessaires à la couche web avant de retourner les données. Celasignifie que la couche métier doit charger toutes les données et retournertoutes les données déjà initialisées à la couche de présentation/web pourun cas d'utilisation donné. En général l'application appelle la méthodeHibernate.initialize() pour chaque collection nécessaire dans la coucheweb (cet appel doit être fait avant la fermeture de la session) ou bienrécupère les collections de manière agressive à l'aide d'une requête HQLavec une clause FETCH ou à l'aide du mode FetchMode.JOIN pour unerequête de type Criteria. Cela est en général plus facile si vous utilisez lepattern Command plutôt que Session Facade.

Page 294: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 19. Améliorer les performances

280 Hibernate 3.3.1

• Vous pouvez également attacher à une Session un objet chargé aupréalable à l'aide des méthodes merge() ou lock() avant d'accéder auxcollections (ou aux proxys) non initialisés. Non, Hibernate ne fait pas, etne doit pas faire, cela automatiquement car cela pourrait introduire unesémantique transactionnelle ad hoc.

Parfois, vous ne voulez pas initialiser une grande collection mais vous avezquand même besoin d'informations sur elle (comme sa taille) ou un sousensemble de ses données

Vous pouvez utiliser un filtre de collection pour récupérer sa taille sansl'initialiser :

( (Integer) s.createFilter( collection, "select count(*)"

).list().get(0) ).intValue()

La méthode createFilter() est également utilisée pour récupérer demanière efficace des sous ensembles d'une collection sans avoir besoin del'initialiser dans son ensemble.

s.createFilter( lazyCollection,

"").setFirstResult(0).setMaxResults(10).list();

19.1.5. Utiliser le chargement par lot

Pour améliorer les performances, Hibernate peut utiliser le chargementpar lot ce qui veut dire qu'Hibernate peut charger plusieurs proxys (oucollections) non initialisés en une seule requête lorsque l'on accède à l'unde ces proxys. Le chargement par lot est une optimisation intimement liée àla stratégie de chargement tardif par select. Il y a deux moyens d'activer lechargement par lot : au niveau de la classe et au niveau de la collection.

Le chargement par lot pour les classes/entités est plus simple à comprendre.Imaginez que vous ayez la situation suivante à l'exécution : vous avez 25instances de Cat chargées dans une Session, chaque Cat a une référenceà son owner, une Person. La classe Person est mappée avec un proxy,lazy="true". Si vous itérez sur tous les cats et appelez getOwner() surchacun d'eux, Hibernate exécutera par défaut 25 SELECT, pour charger lesowners (initialiser le proxy). Vous pouvez paramétrer ce comportement enspécifiant une batch-size (taille du lot) dans le mapping de Person :

<class name="Person" batch-size="10">...</class>

Hibernate exécutera désormais trois requêtes, en chargeant respectivement10, 10, et 5 entités.

Page 295: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Utilisation du chargement par sous select

Hibernate 3.3.1 281

You may also enable batch fetching of collections. For example, if eachPerson has a lazy collection of Cats, and 10 persons are currently loaded inthe Session, iterating through all persons will generate 10 SELECTs, one forevery call to getCats(). If you enable batch fetching for the cats collection inthe mapping of Person, Hibernate can pre-fetch collections:

<class name="Person">

<set name="cats" batch-size="3">

...

</set>

</class>

Avec une taille de lot (batch-size) de 3, Hibernate chargera respectivement3, 3, 3, et 1 collections en quatre SELECTs. Encore une fois, la valeur del'attribut dépend du nombre de collections non initialisées dans une Sessionparticulière.

Le chargement par lot de collections est particulièrement utile si vous avezdes arborescenses récursives d'éléments (typiquement, le schéma facturede matériels). (Bien qu'un sous ensemble ou un chemin matérialisé est sansdoute une meilleure option pour des arbres principalement en lecture.)

19.1.6. Utilisation du chargement par sous select

Si une collection ou un proxy vers un objet doit être chargé, Hibernate vatous les charger en ré-exécutant la requête orignial dans un sous select.Cela fonctionne de la même manière que le chargement par lot sans lapossibilité de fragmenter le chargement.

19.1.7. Utiliser le chargement tardif des propriétés

Hibernate3 supporte le chargement tardif de propriétés individuelles. Latechnique d'optimisation est également connue sous le nom de fetch groups(groupes de chargement). Il faut noter qu'il s'agit principalement d'unefonctionnalité marketing car en pratique l'optimisation de la lecture d'unenregistrement est beaucoup plus importante que l'optimisation de la lectured'une colonne. Cependant, la restriction du chargement à certaines colonnespeut être pratique dans des cas extrèmes, lorsque des tables "legacy"possèdent des centaines de colonnes et que le modèle de données ne peutpas être amélioré.

Pour activer le chargement tardif d'une propriété, il faut mettre l'attribut lazysur une propriété particulière du mapping :

<class name="Document">

<id name="id">

Page 296: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 19. Améliorer les performances

282 Hibernate 3.3.1

<generator class="native"/>

</id>

<property name="name" not-null="true" length="50"/>

<property name="summary" not-null="true" length="200"

lazy="true"/>

<property name="text" not-null="true" length="2000"

lazy="true"/>

</class>

Le chargement tardif des propriétés requiert une instrumentation du bytecodelors de la compilation ! Si les classes persistantes ne sont pas instrumentées,Hibernate ignorera de manière silencieuse le mode tardif et retombera dansle mode de chargement immédiat.

Pour l'instrumentation du bytecode vous pouvez utiliser la tâche Ant suivante:

<target name="instrument" depends="compile">

<taskdef name="instrument"

classname="org.hibernate.tool.instrument.InstrumentTask">

<classpath path="${jar.path}"/>

<classpath path="${classes.dir}"/>

<classpath refid="lib.class.path"/>

</taskdef>

<instrument verbose="true">

<fileset

dir="${testclasses.dir}/org/hibernate/auction/model">

<include name="*.class"/>

</fileset>

</instrument>

</target>

A different (better?) way to avoid unnecessary column reads, at least forread-only transactions is to use the projection features of HQL or Criteriaqueries. This avoids the need for buildtime bytecode processing and iscertainly a preferred solution.

Vous pouvez forcer le mode de chargement agressif des propriétés enutilisant fetch all properties dans les requêts HQL.

19.2. Le cache de second niveau

Une Session Hibernate est un cache de niveau transactionnel des donnéespersistantes. Il est possible de configurer un cache de cluster ou de JVM(de niveau SessionFactory pour être exact) défini classe par classe etcollection par collection. Vous pouvez même utiliser votr choix de cacheen implémentant le pourvoyeur (provider) associé. Faites attention, lescaches ne sont jamais avertis des modifications faites dans la base de

Page 297: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Mapping de Cache

Hibernate 3.3.1 283

données par d'autres applications (ils peuvent cependant être configuréspour régulièrement expirer les données en cache).

Par défaut, Hibernate utilise EHCache comme cache de niveau JVM (lesupport de JCS est désormais déprécié et sera enlevé des futures versionsd'Hibernate). Vous pouvez choisir une autre implémentation en spécifiantle nom de la classe qui implémente org.hibernate.cache.CacheProvider enutilisant la propriété hibernate.cache.provider_class.

Tableau 19.1. Fournisseur de cache

Cache Classe pourvoyeuse Type SupportenCluster

Cache derequêtessupporté

Hashtable(notintendedfor productionuse)

org.hibernate.cache.HashtableCacheProvidermémoire yes

EHCache org.hibernate.cache.EhCacheProvidermémoire,disque

yes

OSCache org.hibernate.cache.OSCacheProvidermémoire,disque

yes

SwarmCacheorg.hibernate.cache.SwarmCacheProvideren cluster(multicastip)

oui (invalidationdecluster)

JBossCache1.x

org.hibernate.cache.TreeCacheProvideren cluster(multicastip), transactionnel

oui (replication)oui(horlogesync. nécessaire)

JBossCache 2

org.hibernate.cache.jbc2.JBossCacheRegionFactoryen cluster(multicastip), transactionnel

yes (replicationor invalidation)

oui(horlogesync. nécessaire)

19.2.1. Mapping de Cache

L'élément <cache> d'une classe ou d'une collection à la forme suivante :

<cache

usage="transactional|read-write|nonstrict-read-write|read-only"

(1)

region="RegionName"

(2)

include="all|non-lazy"

(3)

Page 298: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 19. Améliorer les performances

284 Hibernate 3.3.1

/>

(1) usage (requis) spécifie la stratégie de cache : transactionel,lecture-écriture, lecture-écriture non stricte ou lecture seule

(2) region (optionnel, par défaut il s'agit du nom de la classe ou du nom derole de la collection) spécifie le nom de la région du cache de secondniveau

(3) include (optionnel, par défaut all) non-lazy spécifie que les propriétésdes entités mappées avec lazy="true" ne doivent pas être mises encache lorsque le chargement tardif des attributs est activé.

Alternatively (preferably?), you may specify <class-cache> and<collection-cache> elements in hibernate.cfg.xml.

L'attribut usage spécifie une stratégie de concurrence d'accès au cache.

19.2.2. Strategie : lecture seule

Si votre application a besoin de lire mais ne modifie jamais les instancesd'une classe, un cache read-only peut être utilisé. C'est la stratégie la plussimple et la plus performante. Elle est même parfaitement sûre dans uncluster.

<class name="eg.Immutable" mutable="false">

<cache usage="read-only"/>

....

</class>

19.2.3. Stratégie : lecture/écriture

Si l'application a besoin de mettre à jour des données, un cache read-writepeut être approprié. Cette stratégie ne devrait jamais être utilisée si votreapplication nécessite un niveau d'isolation transactionnelle sérialisable.Si le cache est utilisé dans un environnement JTA, vous devez spécifierhibernate.transaction.manager_lookup_class, fournissant une stratégiepour obtenir le TransactionManager JTA. Dans d'autres environnements,vous devriez vous assurer que la transation est terminée à l'appel deSession.close() ou Session.disconnect(). Si vous souhaitez utiliser cettestratégie dans un cluster, vous devriez vous assurer que l'implémentation decache utilisée supporte le vérrouillage. Ce que ne font pas les pourvoyeurscaches fournis.

<class name="eg.Cat" .... >

<cache usage="read-write"/>

....

<set name="kittens" ... >

Page 299: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Stratégie : lecture/écriture non stricte

Hibernate 3.3.1 285

<cache usage="read-write"/>

....

</set>

</class>

19.2.4. Stratégie : lecture/écriture non stricte

Si l'application besoin de mettre à jour les données de manière occasionnelle(qu'il est très peu probable que deux transactions essaient de mettre àjour le même élément simultanément) et qu'une isolation transactionnellestricte n'est pas nécessaire, un cache nonstrict-read-write peut êtreapproprié. Si le cache est utilisé dans un environnement JTA, vous devezspécifier hibernate.transaction.manager_lookup_class. Dans d'autresenvironnements, vous devriez vous assurer que la transation est terminée àl'appel de Session.close() ou Session.disconnect()

19.2.5. Stratégie : transactionelle

La stratégie de cache transactional supporte un cache complètementtransactionnel comme, par exemple, JBoss TreeCache. Un tel cache nepeut être utilisé que dans un environnement JTA et vous devez spécifierhibernate.transaction.manager_lookup_class.

19.2.6. Cache-provider/concurrency-strategycompatibility

Important

None of the cache providers support all of the cache concurrencystrategies.

The following table shows which providers are compatible with whichconcurrency strategies.

Page 300: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 19. Améliorer les performances

286 Hibernate 3.3.1

Tableau 19.2. Stratégie de concurrence du cache

Cache read-only(lectureseule)

nonstrict-read-write(lecture-écriture nonstricte)

read-write(lecture-ériture)

transactional (transactionnel)

Hashtable(not intendedfor productionuse)

yes yes yes

EHCache yes yes yes

OSCache yes yes yes

SwarmCache yes yes

JBoss Cache1.x

yes yes

JBoss Cache2

yes yes

19.3. Gérer les caches

A chaque fois que vous passez un objet à la méthode save(), update() ousaveOrUpdate() et à chaque fois que vous récupérez un objet avec load(),get(), list(), iterate() or scroll(), cet objet est ajouté au cache interne dela Session.

Lorsqu'il y a un appel à la méthode flush(), l'état de cet objet va êtresynchronisé avec la base de données. Si vous ne voulez pas que cettesynchronisation ait lieu ou si vous traitez un grand nombre d'objets etque vous avez besoin de gérer la mémoire de manière efficace, vouspouvez utiliser la méthode evict() pour supprimer l'objet et ses collectionsdépendantes du cache de la session

ScrollableResult cats = sess.createQuery("from Cat as

cat").scroll(); //a huge result set

while ( cats.next() ) {

Cat cat = (Cat) cats.get(0);

doSomethingWithACat(cat);

sess.evict(cat);

}

La Session dispose aussi de la méthode contains() pour déterminer si uneinstance appartient au cache de la session.

Page 301: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Le cache de requêtes

Hibernate 3.3.1 287

Pour retirer tous les objets du cache session, appelez Session.clear()

Pour le cache de second niveau, il existe des méthodes définies dansSessionFactory pour retirer des instances du cache, la classe entière, uneinstance de collection ou le rôle entier d'une collection.

sessionFactory.evict(Cat.class, catId); //evict a particular Cat

sessionFactory.evict(Cat.class); //evict all Cats

sessionFactory.evictCollection("Cat.kittens", catId); //evict a

particular collection of kittens

sessionFactory.evictCollection("Cat.kittens"); //evict all kitten

collections

Le CacheMode contrôle comme une session particulière interragit avec lecache de second niveau

• CacheMode.NORMAL - lit et écrit les items dans le cache de second niveau

• CacheMode.GET - lit les items dans le cache de second niveau mais ne lesécrit pas sauf dans le cache d'une mise à jour d'une donnée

• CacheMode.PUT - écrit les items dans le cache de second niveau mais ne leslit pas dans le cache de second niveau

• CacheMode.REFRESH - écrit les items dans le cache de second niveaumais ne les lit pas dans le cache de second niveau, outrepasse l'effetdehibernate.cache.use_minimal_puts, en forçant un rafraîchissement ducache de second niveau pour chaque item lu dans la base

Pour parcourir le contenu du cache de second niveau ou la région du cachedédiée au requêtes, vous pouvez utiliser l'API Statistics API:

Map cacheEntries = sessionFactory.getStatistics()

.getSecondLevelCacheStatistics(regionName)

.getEntries();

Vous devez pour cela activer les statistiques et optionnellement forcerHibernate à conserver les entrées dans le cache sous un format pluscompréhensible pour l'utilisateur :

hibernate.generate_statistics true

hibernate.cache.use_structured_entries true

19.4. Le cache de requêtes

Les résultats d'une requête peuvent aussi être placés en cache. Ceci n'estutile que pour les requêtes qui sont exécutées avec les mêmes paramètres.Pour utiliser le cache de requêtes, vous devez d'abord l'activer :

Page 302: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 19. Améliorer les performances

288 Hibernate 3.3.1

hibernate.cache.use_query_cache true

Ce paramètre amène la création de deux nouvelles régions dans lecache, une qui va conserver le résultat des requêtes mises en cache(org.hibernate.cache.StandardQueryCache) et l'autre qui va conserverl'horodatage des mises à jour les plus récentes effectuées sur les tablesrequêtables (org.hibernate.cache.UpdateTimestampsCache). Il faut noterque le cache de requête ne conserve pas l'état des entités, il met en cacheuniquement les valeurs de l'identifiant et les valeurs de types de base (?). Lecache de requête doit toujours être utilisé avec le cache de second niveaupour être efficace.

La plupart des requêtes ne retirent pas de bénéfice pas du cache, doncpar défaut les requêtes ne sont pas mises en cache. Pour activer le cache,appelez Query.setCacheable(true). Cet appel permet de vérifier si lesrésultats sont en cache ou non, voire d'ajouter ces résultats si la requête estexécutée.

Si vous avez besoin de contrôler finement les délais d'expiration du cache,vous pouvez spécifier une région de cache nommée pour une requêteparticulière en appelant Query.setCacheRegion().

List blogs = sess.createQuery("from Blog blog where blog.blogger =

:blogger")

.setEntity("blogger", blogger)

.setMaxResults(15)

.setCacheable(true)

.setCacheRegion("frontpages")

.list();

Si une requête doit forcer le rafraîchissement de sa région de cache,vous devez appeler Query.setCacheMode(CacheMode.REFRESH). C'estparticulièrement utile lorsque les données peuvent avoir été mises à jour parun processus séparé (e.g. elles n'ont pas été modifiées par Hibernate). Celapermet à l'application de rafraîchir de manière sélective les résultats d'unerequête particulière. Il s'agit d'une alternative plus efficace à l'éviction d'unerégion du cache à l'aide de la méthode SessionFactory.evictQueries().

19.5. Comprendre les performances desCollections

Nous avons déjà passé du temps à discuter des collections. Dans cettesection, nous allons traiter du comportement des collections à l'exécution.

Page 303: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Classification

Hibernate 3.3.1 289

19.5.1. Classification

Hibernate définit trois types de collections :

• les collections de valeurs

• les associations un-vers-plusieurs

• les associations plusieurs-vers-plusieurs

Cette classification distingue les différentes relations entre les tables et lesclés étrangères mais ne nous apprend rien de ce que nous devons savoir surle modèle relationnel. Pour comprendre parfaitement la structure relationnelleet les caractéristiques des performances, nous devons considérer lastructure de la clé primaire qui est utilisée par Hibernate pour mettre àjour ou supprimer les éléments des collections. Celà nous amène auxclassifications suivantes :

• collections indexées

• sets

• bags

Toutes les collections indexées (maps, lists, arrays) ont une clé primaireconstituée des colonnes clé (<key>) et <index>. Avec ce type de clé primaire,la mise à jour de collection est en général très performante - la clé primairepeut être indexées efficacement et un élément particulier peut être localiséefficacement lorsqu'Hibernate essaie de le mettre à jour ou de le supprimer.

Sets have a primary key consisting of <key> and element columns. This maybe less efficient for some types of collection element, particularly compositeelements or large text or binary fields; the database may not be able to indexa complex primary key as efficiently. On the other hand, for one to many ormany to many associations, particularly in the case of synthetic identifiers, itis likely to be just as efficient. (Side-note: if you want SchemaExport to actuallycreate the primary key of a <set> for you, you must declare all columns asnot-null="true".)

Le mapping à l'aide d'<idbag> définit une clé de substitution ce qui leurpermet d'être très efficaces lors de la mise à jour. En fait il s'agit du meilleurcas de mise à jour d'une collection

Le pire cas intervient pour les Bags. Dans la mesure où un bag permetla duplications des éléments et n'a pas de colonne d'index, aucune cléprimaire ne peut être définie. Hibernate n'a aucun moyen de distinguer des

Page 304: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 19. Améliorer les performances

290 Hibernate 3.3.1

enregistrements dupliqués. Hibernate résout ce problème en supprimantcomplètement les enregistrements (via un simple DELETE), puis en recréant lacollection chaque fois qu'elle change. Ce qui peut être très inefficace.

Notez que pour une relation un-vers-plusieurs, la "clé primaire" peut ne pasêtre la clé primaire de la table en base de données - mais même dans cecas, la classification ci-dessus reste utile (Elle explique comment Hibernate"localise" chaque enregistrement de la collection).

19.5.2. Les lists, les maps, les idbags et les sets sont lescollections les plus efficaces pour la mise à jour

La discussion précédente montre clairement que les collections indexées et(la plupart du temps) les sets, permettent de réaliser le plus efficacement lesopérations d'ajout, de suppression ou de modification d'éléments.

Il existe un autre avantage qu'ont les collections indexées sur les Setsdans le cadre d'une association plusieurs vers plusieurs ou d'unecollection de valeurs. A cause de la structure inhérente d'un Set, Hibernaten'effectue jamais d'UPDATE quand un enregistrement est modifié. Lesmodifications apportées à un Set se font via un INSERT et DELETE (de chaqueenregistrement). Une fois de plus, ce cas ne s'applique pas aux associationsun vers plusieurs.

Après s'être rappelé que les tableaux ne peuvent pas être chargéstardivement, nous pouvons conclure que les lists, les maps et les idbags sontles types de collections (non inversées) les plus performants, avec les setspas loin derrières. Les sets son le type de collection le plus courant dans lesapplications Hibernate. Cela est du au fait que la sémantique des "set" est laplus naturelle dans le modèle relationnel.

Cependant, dans des modèles objet bien conçus avec Hibernate, onvoit souvent que la plupart des collections sont en fait des associations"un-vers-plusieurs" avec inverse="true". Pour ces associations, les misesà jour sont gérées au niveau de l'association "plusieurs-vers-un" et lesconsidérations de performance de mise à jour des collections ne s'appliquenttout simplement pas dans ces cas là.

19.5.3. Les Bags et les lists sont les plus efficaces pourles collections inverse

Avant que vous n'oubliez les bags pour toujours, il y a un cas précis où lesbags (et les lists) sont bien plus performants que les sets. Pour une collectionmarquée comme inverse="true" (le choix le plus courant pour un relation un

Page 305: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Suppression en un coup

Hibernate 3.3.1 291

vers plusieurs bidirectionnelle), nous pouvons ajouter des éléments à un bagou une list sans avoir besoin de l'initialiser (fetch) les éléments du sac! Ceciparce que Collection.add() ou Collection.addAll() doit toujours retournervrai pour un bag ou une List (contrairement au Set). Cela peut rendre lecode suivant beaucoup plus rapide.

Parent p = (Parent) sess.load(Parent.class, id);

Child c = new Child();

c.setParent(p);

p.getChildren().add(c); //no need to fetch the collection!

sess.flush();

19.5.4. Suppression en un coup

Parfois, effacer les éléments d'une collection un par un peut êtreextrêmement inefficace. Hibernate n'est pas totalement stupide, il sait qu'ilne faut pas le faire dans le cas d'une collection complètement vidée (lorsquevous appellez list.clear(), par exemple). Dans ce cas, Hibernate fera unsimple DELETE et le travail est fait !

Supposons que nous ajoutions un élément dans une collection de taillevingt et que nous enlevions ensuite deux éléments. Hibernate effectuera unINSERT puis deux DELETE (à moins que la collection ne soit un bag). Ce qui estsouhaitable.

Cependant, supposons que nous enlevions dix huit éléments, laissant ainsideux éléments, puis que nous ajoutions trois nouveaux éléments. Il y a deuxmoyens de procéder.

• effacer dix huit enregistrements un à un puis en insérer trois

• effacer la totalité de la collection (en un DELETE SQL) puis insérer les cinqéléments restant un à un

Hibernate n'est pas assez intelligent pour savoir que, dans ce cas, laseconde méthode est plus rapide (Il plutôt heureux qu'Hibernate ne soit pastrop intelligent ; un tel comportement pourrait rendre l'utilisation de triggers debases de données plutôt aléatoire, etc...).

Heureusement, vous pouvez forcer ce comportement lorsque vous lesouhaitez, en liberant (c'est-à-dire en déréférençant) la collection initialeet en retournant une collection nouvellement instanciée avec les élémentsrestants. Ceci peut être très pratique et très puissant de temps en temps.

Bien sûr, la suppression en un coup ne s'applique pas pour les collectionsqui sont mappées avec inverse="true".

Page 306: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 19. Améliorer les performances

292 Hibernate 3.3.1

19.6. Moniteur de performance

L'optimisation n'est pas d'un grand intérêt sans le suivi et l'accès auxdonnées de performance. Hibernate fournit toute une panoplie de rapport surses opérations internes. Les statistiques dans Hibernate sont fournies parSessionFactory.

19.6.1. Suivi d'une SessionFactory

Vous pouvez accéder au métriques d'une SessionFactory de deux manières.La première option est d'appeler sessionFactory.getStatistics() et de lireou d'afficher les Statistics vous même.

Hibernate peut également utiliser JMX pour publier les métriques si vousactivez le MBean StatisticsService. Vous pouvez activer un seul MBeanpour toutes vos SessionFactory ou un par factory. Voici un code qui montreun exemple de configuration minimaliste :

// MBean service registration for a specific SessionFactory

Hashtable tb = new Hashtable();

tb.put("type", "statistics");

tb.put("sessionFactory", "myFinancialApp");

ObjectName on = new ObjectName("hibernate", tb); // MBean object

name

StatisticsService stats = new StatisticsService(); // MBean

implementation

stats.setSessionFactory(sessionFactory); // Bind the stats to a

SessionFactory

server.registerMBean(stats, on); // Register the Mbean on the server

// MBean service registration for all SessionFactory's

Hashtable tb = new Hashtable();

tb.put("type", "statistics");

tb.put("sessionFactory", "all");

ObjectName on = new ObjectName("hibernate", tb); // MBean object

name

StatisticsService stats = new StatisticsService(); // MBean

implementation

server.registerMBean(stats, on); // Register the MBean on the server

TODO: Cela n'a pas de sens : dans le premier cs on récupère et on utilise leMBean directement. Dans le second, on doit fournir le nom JNDI sous lequelest retenu la fabrique de session avant de l'utiliser. Pour cela il faut utiliserhibernateStatsBean.setSessionFactoryJNDIName("my/JNDI/Name")

Vous pouvez (dés)activer le suivi pour une SessionFactory

Page 307: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Métriques

Hibernate 3.3.1 293

• au moment de la configuration en mettant hibernate.generate_statisticsà false

• à chaud avec sf.getStatistics().setStatisticsEnabled(true) ouhibernateStatsBean.setStatisticsEnabled(true)

Statistics can be reset programmatically using the clear() method. Asummary can be sent to a logger (info level) using the logSummary() method.

19.6.2. Métriques

Hibernate fournit un certain nombre de métriques, qui vont des informationstrès basiques aux informations très spécialisées qui ne sont appropriées quedans certains scenarii. Tous les compteurs accessibles sont décrits dansl'API de l'interface Statistics dans trois catégories :

• Les métriques relatives à l'usage général de la Session comme le nombrede sessions ouvertes, le nombre de connexions JDBC récupérées, etc...

• Les métriques relatives aux entités, collections, requêtes et caches dansleur ensemble (métriques globales),

• Les métriques détaillées relatives à une entité, une collection, une requêteou une région de cache particulière.

For example, you can check the cache hit, miss, and put ratio of entities,collections and queries, and the average time a query needs. Beware thatthe number of milliseconds is subject to approximation in Java. Hibernateis tied to the JVM precision, on some platforms this might even only beaccurate to 10 seconds.

Des accesseurs simples sont utilisés pour accéder aux métriques globales(e.g. celles qui ne sont pas liées à une entité, collection ou région decache particulière). Vous pouvez accéder aux métriques d'une entité,collection, région de cache particulière à l'aide de son nom et à l'aidede sa représentation HQL ou SQL pour une requête. Référez vous à lajavadoc des APIS Statistics, EntityStatistics, CollectionStatistics,SecondLevelCacheStatistics, and QueryStatistics pour plus d'informations.Le code ci-dessous montre un exemple simple :

Statistics stats = HibernateUtil.sessionFactory.getStatistics();

double queryCacheHitCount = stats.getQueryCacheHitCount();

double queryCacheMissCount = stats.getQueryCacheMissCount();

double queryCacheHitRatio =

queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);

Page 308: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 19. Améliorer les performances

294 Hibernate 3.3.1

log.info("Query Hit ratio:" + queryCacheHitRatio);

EntityStatistics entityStats =

stats.getEntityStatistics( Cat.class.getName() );

long changes =

entityStats.getInsertCount()

+ entityStats.getUpdateCount()

+ entityStats.getDeleteCount();

log.info(Cat.class.getName() + " changed " + changes + "times" );

Pour travailler sur toutes les entités, collections, requêtes et régionsde cache, vous pouvez récupérer la liste des noms des entités,collections, requêtes et régions de cache avec les méthodes :getQueries(), getEntityNames(), getCollectionRoleNames(), etgetSecondLevelCacheRegionNames().

Page 309: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 295

Chapitre 20. Guide des outilsDes outils en ligne de commande, des plugins Eclipse ainsu que des tâchesAnt permettent de gérer de cycles de développement complet de projetsutilisant Hibernate.

Les outils Hibernate actuels incluent des plugins pour l'IDE Eclipse ainsi quedes tâches Ant pour l'ingénierie inverse de bases de données existantes :

• Mapping Editor : un éditeur pour les fichiers de mapping XML Hibernate,supportant l'auto-complétion et la mise en valeur de la syntaxe. Il supporteaussi l'auto-complétion automatique pour les noms de classes et les nomsde propriété/champ, le rendant beaucoup plus polyvalent qu'un éditeurXMLnormal.

• Console : la console est une nouvelle vue d'Eclipse. En plus de la vued'ensemble arborescente de vos configurations de console, vous obtenezaussi une vue interactive de vos classes persistantes et de leurs relations.La console vous permet d'exécuter des requête HQL dans votre base dedonnées et de parcourir les résultats directement dans Eclipse.

• Development Wizards : plusieurs assistants sont fournis avec les outilsd'Hibernate pour Eclipse ; vous pouvez utiliser un assistant pour générerrapidement les fichiers de configuration d'Hibernate (cfg.xml), ou vouspouvez même complètement générer les fichiers de mapping Hibernate etles sources des POJOs à partir d'un schéma de base de données existant.L'assistant d'ingénierie inverse supporte les modèles utilisateur.

• Tâches Ant :

Veuillez-vous référer au paquet outils Hibernate et sa documentation pourplus d'informations.

Pourtant, le paquet principal d'Hibernate arrive avec un lot d'outils intégrés (ilpeut même être utilisé de "l'intérieur" d'Hibernate à la volée) : SchemaExportaussi connu comme hbm2ddl.

20.1. Génération automatique du schéma

La DDL peut être générée à partir de vos fichiers de mapping par un utilitaired'Hibernate. Le schéma généré inclut les contraintes d'intégrité référentielle(clefs primaires et étrangères) pour les tables d'entités et de collections. Lestables et les séquences sont aussi créées pour les générateurs d'identifiantmappés.

Page 310: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 20. Guide des outils

296 Hibernate 3.3.1

Vous devez spécifier un Dialect SQL via la propriété hibernate.dialect lorsde l'utilisation de cet outils, puisque la DDL est fortement dépendante de labase de données.

D'abord, personnalisez vos fichiers de mapping pour améliorer le schémagénéré.

20.1.1. Personnaliser le schéma

Plusieurs éléments du mapping hibernate définissent des attributs optionnelsnommés length, precision et scale. Vous pouvez paramétrer la longueur, laprécision,... d'une colonne avec ces attributs.

<property name="zip" length="5"/>

<property name="balance" precision="12" scale="2"/>

Certains éléments acceptent aussi un attribut not-null (utilisé pour générerles contraintes de colonnes NOT NULL) et un attribut unique (pour générer unecontrainte de colonne UNIQUE).

<many-to-one name="bar" column="barId" not-null="true"/>

<element column="serialNumber" type="long" not-null="true"

unique="true"/>

Un attribut unique-key peut être utilisé pour grouper les colonnes en uneseule contrainte d'unicité. Actuellement, la valeur spécifiée par l'attributunique-key n'est pas utilisée pour nommer la contrainte dans le DDL généré,elle sert juste à grouper les colonnes dans le fichier de mapping.

<many-to-one name="org" column="orgId" unique-key="OrgEmployeeId"/>

<property name="employeeId" unique-key="OrgEmployee"/>

Un attribut index indique le nom d'un index qui sera créé en utilisant la oules colonnes mappées. Plusieurs colonnes peuvent être groupées dans unmême index, en spécifiant le même nom d'index.

<property name="lastName" index="CustName"/>

<property name="firstName" index="CustName"/>

Un attribut foreign-key peut être utilisé pour surcharger le nom des clésétrangères générées.

<many-to-one name="bar" column="barId" foreign-key="FKFooBar"/>

Page 311: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Personnaliser le schéma

Hibernate 3.3.1 297

Plusieurs éléments de mapping acceptent aussi un élément fils <column>.Ceci est utile pour les type multi-colonnes:

<property name="name" type="my.customtypes.Name"/>

<column name="last" not-null="true" index="bar_idx"

length="30"/>

<column name="first" not-null="true" index="bar_idx"

length="20"/>

<column name="initial"/>

</property>

L'attribut default vous laisse spécifier une valeur par défaut pour unecolonnes (vous devriez assigner la même valeur à la propriété mappée avantde sauvegarder une nouvelle instance de la classe mappée).

<property name="credits" type="integer" insert="false">

<column name="credits" default="10"/>

</property>

<version name="version" type="integer" insert="false">

<column name="version" default="0"/>

</property>

L'attribut sql-type laisse l'utilisateur surcharger le mapping par défaut dutype Hibernate vers un type SQL.

<property name="balance" type="float">

<column name="balance" sql-type="decimal(13,3)"/>

</property>

L'attribut check permet de spécifier une contrainte de vérification.

<property name="foo" type="integer">

<column name="foo" check="foo > 10"/>

</property>

<class name="Foo" table="foos" check="bar < 100.0">

...

<property name="bar" type="float"/>

</class>

Page 312: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 20. Guide des outils

298 Hibernate 3.3.1

Tableau 20.1. Summary

Attribut Valeur Interprétation

length numérique taille d'une colonne

precision numérique précision décimale de la colonne

scale numérique scale décimale de la colonne

not-null true|false spécifie que la colonne doit être non-nulle

unique true|false spécifie que la colonne doit avoir unecontrainte d'unicité

index index_name spécifie le nom d'un index(multi-colonnes)

unique-key unique_key_name spécifie le nom d'une contrainte d'unicitémulti-colonnes

foreign-key foreign_key_namespecifies the name of the foreign keyconstraint generated for an association,for a <one-to-one>, <many-to-one>, <key>,or <many-to-many> mapping element.Note that inverse="true" sides will not beconsidered by SchemaExport.

sql-type SQL column_type overrides the default column type(attribute of <column> element only)

default SQL expression spécifie une valeur par défaut pour lacolonne

check SQL expression crée une contrainte de vérification sur latable ou la colonne

L'élément <comment> vous permet de spécifier un commentaire pour leschéma généré.

<class name="Customer" table="CurCust">

<comment>Current customers only</comment>

...

</class>

<property name="balance">

<column name="bal">

<comment>Balance in USD</comment>

</column>

</property>

Ceci a pour résultat une expression comment on table ou comment on columndans la DDL générée (où supportée).

Page 313: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Exécuter l'outil

Hibernate 3.3.1 299

20.1.2. Exécuter l'outil

L'outil SchemaExport génère un script DDL vers la sortie standard et/ouexécute les ordres DDL.

java -cp hibernate_classpaths org.hibernate.tool.hbm2ddl.SchemaExportoptions mapping_files

Tableau 20.2. SchemaExport Options de la ligne de commande

Option Description

--quiet don't output the script to stdout

--drop supprime seuleument les tables

--create ne créé que les tables

--text ne pas exécuter sur la base de données

--output=my_schema.ddl écrit le script ddl vers un fichier

--naming=eg.MyNamingStrategy select a NamingStrategy

--config=hibernate.cfg.xml lit la configuration Hibernate à partir d'unfichier XML

--

properties=hibernate.properties

read database properties from a file

--format formatte proprement le SQL généré dansle script

--delimiter=x paramètre un délimiteur de fin de lignepour le script

Vous pouvez même intégrer SchemaExport dans votre application :

Configuration cfg = ....;

new SchemaExport(cfg).create(false, true);

20.1.3. Propriétés

Les propriétés de la base de données peuvent être spécifiées

• comme propriétés système avec -D<property>

• dans hibernate.properties

• dans un fichier de propriétés déclaré avec --properties

Les propriétés nécessaires sont :

Page 314: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 20. Guide des outils

300 Hibernate 3.3.1

Tableau 20.3. SchemaExport Connection Properties

Nom de la propriété Description

hibernate.connection.driver_classclasse du driver JDBC

hibernate.connection.url URL JDBC

hibernate.connection.username utilisateur de la base de données

hibernate.connection.password mot de passe de l'utilisateur

hibernate.dialect dialecte

20.1.4. Utiliser Ant

Vous pouvez appeler SchemaExport depuis votre script de construction Ant :

<target name="schemaexport">

<taskdef name="schemaexport"

classname="org.hibernate.tool.hbm2ddl.SchemaExportTask"

classpathref="class.path"/>

<schemaexport

properties="hibernate.properties"

quiet="no"

text="no"

drop="no"

delimiter=";"

output="schema-export.sql">

<fileset dir="src">

<include name="**/*.hbm.xml"/>

</fileset>

</schemaexport>

</target>

20.1.5. Mises à jour incrémentales du schéma

L'outil SchemaUpdate mettra à jour un schéma existant en effectuant leschangement par "incrément". Notez que SchemaUpdate dépends beaucoupde l'API JDBC metadata, il ne fonctionnera donc pas avec tous les driversJDBC.

java -cp hibernate_classpaths org.hibernate.tool.hbm2ddl.SchemaUpdateoptions mapping_files

Page 315: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Utiliser Ant pour des mises à jour deschéma par incrément

Hibernate 3.3.1 301

Tableau 20.4. SchemaUpdate Options de ligne de commande

Option Description

--quiet don't output the script to stdout

--text ne pas exporter vers la base de données

--naming=eg.MyNamingStrategy select a NamingStrategy

--

properties=hibernate.properties

read database properties from a file

--config=hibernate.cfg.xml specify a .cfg.xml file

Vous pouvez intégrer SchemaUpdate dans votre application :

Configuration cfg = ....;

new SchemaUpdate(cfg).execute(false);

20.1.6. Utiliser Ant pour des mises à jour de schéma parincrément

Vous pouvez appeler SchemaUpdate depuis le script Ant :

<target name="schemaupdate">

<taskdef name="schemaupdate"

classname="org.hibernate.tool.hbm2ddl.SchemaUpdateTask"

classpathref="class.path"/>

<schemaupdate

properties="hibernate.properties"

quiet="no">

<fileset dir="src">

<include name="**/*.hbm.xml"/>

</fileset>

</schemaupdate>

</target>

20.1.7. Validation du schéma

L'outil SchemaValidator validera que le schéma existant correspond à vosdocuments de mapping. Notez que le SchemaValidator dépends de l'APImetadata de JDBC, il ne fonctionnera donc pas avec tous les drivers JDBC.Cet outil est extrêmement utile pour tester.

java -cp hibernate_classpaths org.hibernate.tool.hbm2ddl.SchemaValidatoroptions mapping_files

Page 316: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 20. Guide des outils

302 Hibernate 3.3.1

Tableau 20.5. SchemaValidator Options de ligne decommande

Option Description

--naming=eg.MyNamingStrategy select a NamingStrategy

--

properties=hibernate.properties

read database properties from a file

--config=hibernate.cfg.xml specify a .cfg.xml file

Vous pouvez inclure SchemaValidator dans votre application:

Configuration cfg = ....;

new SchemaValidator(cfg).validate();

20.1.8. Utiliser Ant pour la validation du Schéma

Vous pouvez appeler SchemaValidator depuis le script Ant:

<target name="schemavalidate">

<taskdef name="schemavalidator"

classname="org.hibernate.tool.hbm2ddl.SchemaValidatorTask"

classpathref="class.path"/>

<schemavalidator

properties="hibernate.properties">

<fileset dir="src">

<include name="**/*.hbm.xml"/>

</fileset>

</schemavalidator>

</target>

Page 317: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 303

Chapitre 21. Exemple : Père/FilsL'une des premières choses que les nouveaux utilisateurs essaient de faireavec Hibernate est de modéliser une relation père/fils. Il y a deux approchesdifférentes pour cela. Pour un certain nombre de raisons, la méthode la pluscourante, en particulier pour les nouveaux utilisateurs, est de modéliserles deux relations Père et Fils comme des classes entités liées par uneassociation <one-to-many> du Père vers le Fils (l'autre approche est dedéclarer le Fils comme un <composite-element>). Il est évident que le sensde l'association un vers plusieurs (dans Hibernate) est bien moins prochedu sens habituel d'une relation père/fils que ne l'est celui d'un élémentcmposite. Nous allons vous expliquer comment utiliser une association unvers plusieurs bidirectionnelle avec cascade afin de modéliser efficacementet élégamment une relation père/fils, ce n'est vraiment pas difficile !

21.1. Une note à propos des collections

Les collections Hibernate sont considérées comme étant une partie logiquede l'entité dans laquelle elles sont contenues ; jamais des entités qu'ellecontient. C'est une distinction crutiale ! Les conséquences sont les suivantes:

• Quand nous ajoutons / retirons un objet d'une collection, le numéro deversion du propriétaire de la collection est incrémenté.

• Si un objet qui a été enlevé d'une collection est une instance de typevaleur (ex : élément composite), cet objet cessera d'être persistant et sonétat sera complètement effacé de la base de données. Par ailleurs, ajouterune instance de type valeur dans une collection aura pour conséquenceque son état sera immédiatement persistant.

• Si une entité est enlevée d'une collection (association un-vers-plusieursou plusieurs-vers-plusieurs), par défaut, elle ne sera pas effacée. Cecomportement est complètement logique - une modification de l'un desétats internes d'une entité ne doit pas causer la disparition de l'entitéassociée ! De même, l'ajout d'une entité dans une collection n'engendrepas, par défaut, la persistance de cette entité.

Le comportement par défaut est donc que l'ajout d'une entité dans unecollection créé simplement le lien entre les deux entités, et qu'effacer uneentité supprime ce lien. C'est le comportement le plus approprié dans laplupart des cas. Ce comportement n'est cependant pas approprié lorsque lavie du fils est liée au cycle de vie du père.

Page 318: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 21. Exemple : Père/Fils

304 Hibernate 3.3.1

21.2. un-vers-plusieurs bidirectionnel

Supposons que nous ayons une simple association <one-to-many> de Parentvers Child.

<set name="children">

<key column="parent_id"/>

<one-to-many class="Child"/>

</set>

Si nous executions le code suivant

Parent p = .....;

Child c = new Child();

p.getChildren().add(c);

session.save(c);

session.flush();

Hibernate exécuterait deux ordres SQL:

• un INSERT pour créer l'enregistrement pour c

• un UPDATE pour créer le lien de p vers c

Ceci est non seuleument inefficace, mais viole aussi toute contrainte NOTNULL sur la colonne parent_id. Nous pouvons réparer la contrainte de nullitéen spécifiant not-null="true" dans le mapping de la collection :

<set name="children">

<key column="parent_id" not-null="true"/>

<one-to-many class="Child"/>

</set>

Cependant ce n'est pas la solution recommandée.

La cause sous jacente à ce comportement est que le lien (la clé étrangèreparent_id) de p vers c n'est pas considérée comme faisant partie de l'état del'objet Child et n'est donc pas créé par l'INSERT. La solution est donc que celien fasse partie du mapping de Child.

<many-to-one name="parent" column="parent_id" not-null="true"/>

(Nous avons aussi besoin d'ajouter la propriété parent dans la classe Child).

Maintenant que l'état du lien est géré par l'entité Child, nous spécifions à lacollection de ne pas mettre à jour le lien. Nous utilisons l'attribut inverse.

<set name="children" inverse="true">

<key column="parent_id"/>

Page 319: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Cycle de vie en cascade

Hibernate 3.3.1 305

<one-to-many class="Child"/>

</set>

Le code suivant serait utilisé pour ajouter un nouveau Child

Parent p = (Parent) session.load(Parent.class, pid);

Child c = new Child();

c.setParent(p);

p.getChildren().add(c);

session.save(c);

session.flush();

Maintenant, seul un INSERT SQL est nécessaire !

Pour alléger encore un peu les choses, nous devrions créer une méthodeaddChild() dans Parent.

public void addChild(Child c) {

c.setParent(this);

children.add(c);

}

Le code d'ajout d'un Child serait alors

Parent p = (Parent) session.load(Parent.class, pid);

Child c = new Child();

p.addChild(c);

session.save(c);

session.flush();

21.3. Cycle de vie en cascade

L'appel explicite de save() est un peu fastidieux. Nous pouvons simplifiercela en utilisant les cascades.

<set name="children" inverse="true" cascade="all">

<key column="parent_id"/>

<one-to-many class="Child"/>

</set>

Simplifie le code précédent en

Parent p = (Parent) session.load(Parent.class, pid);

Child c = new Child();

p.addChild(c);

session.flush();

De la même manière, nous n'avons pas à itérer sur les fils lorsque noussauvons ou effacons un Parent. Le code suivant efface p et tous ses fils de labase de données.

Page 320: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 21. Exemple : Père/Fils

306 Hibernate 3.3.1

Parent p = (Parent) session.load(Parent.class, pid);

session.delete(p);

session.flush();

Par contre, ce code

Parent p = (Parent) session.load(Parent.class, pid);

Child c = (Child) p.getChildren().iterator().next();

p.getChildren().remove(c);

c.setParent(null);

session.flush();

n'effacera pas c de la base de données, il enlèvera seulement le lien vers p(et causera une violation de contrainte NOT NULL, dans ce cas). Vous devezexplicitement utiliser delete() sur Child.

Parent p = (Parent) session.load(Parent.class, pid);

Child c = (Child) p.getChildren().iterator().next();

p.getChildren().remove(c);

session.delete(c);

session.flush();

Dans notre cas, un Child ne peut pas vraiment exister sans son père. Sinous effacons un Child de la collection, nous voulons vraiment qu'il soiteffacé. Pour cela, nous devons utiliser cascade="all-delete-orphan".

<set name="children" inverse="true" cascade="all-delete-orphan">

<key column="parent_id"/>

<one-to-many class="Child"/>

</set>

A noter : même si le mapping de la collection spécifie inverse="true",les cascades sont toujours assurées par l'itération sur les éléments de lacollection. Donc, si vous avez besoin qu'un objet soit enregistré, effacé oumis à jour par cascade, vous devez l'ajouter dans la colleciton. Il ne suffit pasd'appeler explicitement setParent().

21.4. Cascades et unsaved-value

Supposons que nous ayons chargé un Parent dans une Session, que nousl'ayons ensuite modifié et que voulions persiter ces modifications dans unenouvelle session en appelant update(). Le Parent contiendra une collectionde fils et, puisque la cascade est activée, Hibernate a besoin de savoirquels fils viennent d'être instanciés et quels fils proviennent de la base dedonnées. Supposons aussi que Parent et Child ont tous deux des identifiantsdu type Long. Hibernate utilisera la propriété de l'identifiant et la propriété dela version/horodatage pour déterminer quels fils sont nouveaux (vous pouvez

Page 321: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Conclusion

Hibernate 3.3.1 307

aussi utiliser la propriété version ou timestamp, voir ???). Dans Hibernate3, iln'est plus nécessaire de spécifier une unsaved-value explicitement.

Le code suivant mettra à jour parent et child et insérera newChild.

//parent and child were both loaded in a previous session

parent.addChild(child);

Child newChild = new Child();

parent.addChild(newChild);

session.update(parent);

session.flush();

Ceci est très bien pour des identifiants générés, mais qu'en est-il desidentifiants assignés et des identifiants composés ? C'est plus difficile,puisqu'Hibernate ne peut pas utiliser la propriété de l'identifiant pourdistinguer un objet nouvellement instancié (avec un identifiant assignépar l'utilisateur) d'un objet chargé dans une session précédente. Dans cecas, Hibernate utilisera soit la propriété de version ou d'horodatage, soiteffectuera vraiment une requête au cache de second niveau, soit, dans lepire des cas, à la base de données, pour voir si la ligne existe.

21.5. Conclusion

Il y a quelques principes à maîtriser dans ce chapitre et tout cela peutparaître déroutant la première fois. Cependant, dans la pratique, toutfonctionne parfaitement. La plupart des applications Hibernate utilisent lepattern père / fils.

Nous avons évoqué une alternative dans le premier paragraphe. Aucundes points traités précédemment n'existe dans le cas d'un mapping<composite-element> qui possède exactement la sémantique d'une relationpère / fils. Malheureusement, il y a deux grandes limitations pour les classeséléments composites : les éléments composites ne peuvent contenir decollections, et ils ne peuvent être les fils d'entités autres que l'unique parent.

Page 322: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

308 Hibernate 3.3.1

Page 323: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 309

Chapitre 22. Exemple : applicationWeblog

22.1. Classes persistantes

Les classes persistantes representent un weblog, et un article posté dans unweblog. Il seront modélisés comme une relation père/fils standard, mais nousallons utiliser un "bag" trié au lieu d'un set.

package eg;

import java.util.List;

public class Blog {

private Long _id;

private String _name;

private List _items;

public Long getId() {

return _id;

}

public List getItems() {

return _items;

}

public String getName() {

return _name;

}

public void setId(Long long1) {

_id = long1;

}

public void setItems(List list) {

_items = list;

}

public void setName(String string) {

_name = string;

}

}

package eg;

import java.text.DateFormat;

import java.util.Calendar;

public class BlogItem {

private Long _id;

private Calendar _datetime;

private String _text;

private String _title;

private Blog _blog;

Page 324: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 22. Exemple : application W...

310 Hibernate 3.3.1

public Blog getBlog() {

return _blog;

}

public Calendar getDatetime() {

return _datetime;

}

public Long getId() {

return _id;

}

public String getText() {

return _text;

}

public String getTitle() {

return _title;

}

public void setBlog(Blog blog) {

_blog = blog;

}

public void setDatetime(Calendar calendar) {

_datetime = calendar;

}

public void setId(Long long1) {

_id = long1;

}

public void setText(String string) {

_text = string;

}

public void setTitle(String string) {

_title = string;

}

}

22.2. Mappings Hibernate

Le mapping XML doit maintenant être relativement simple à vos yeux.

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="eg">

<class

name="Blog"

table="BLOGS">

<id

name="id"

column="BLOG_ID">

<generator class="native"/>

Page 325: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Mappings Hibernate

Hibernate 3.3.1 311

</id>

<property

name="name"

column="NAME"

not-null="true"

unique="true"/>

<bag

name="items"

inverse="true"

order-by="DATE_TIME"

cascade="all">

<key column="BLOG_ID"/>

<one-to-many class="BlogItem"/>

</bag>

</class>

</hibernate-mapping>

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="eg">

<class

name="BlogItem"

table="BLOG_ITEMS"

dynamic-update="true">

<id

name="id"

column="BLOG_ITEM_ID">

<generator class="native"/>

</id>

<property

name="title"

column="TITLE"

not-null="true"/>

<property

name="text"

column="TEXT"

not-null="true"/>

Page 326: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 22. Exemple : application W...

312 Hibernate 3.3.1

<property

name="datetime"

column="DATE_TIME"

not-null="true"/>

<many-to-one

name="blog"

column="BLOG_ID"

not-null="true"/>

</class>

</hibernate-mapping>

22.3. Code Hibernate

La classe suivante montre quelques utilisations que nous pouvons faire deces classes.

package eg;

import java.util.ArrayList;

import java.util.Calendar;

import java.util.Iterator;

import java.util.List;

import org.hibernate.HibernateException;

import org.hibernate.Query;

import org.hibernate.Session;

import org.hibernate.SessionFactory;

import org.hibernate.Transaction;

import org.hibernate.cfg.Configuration;

import org.hibernate.tool.hbm2ddl.SchemaExport;

public class BlogMain {

private SessionFactory _sessions;

public void configure() throws HibernateException {

_sessions = new Configuration()

.addClass(Blog.class)

.addClass(BlogItem.class)

.buildSessionFactory();

}

public void exportTables() throws HibernateException {

Configuration cfg = new Configuration()

.addClass(Blog.class)

.addClass(BlogItem.class);

new SchemaExport(cfg).create(true, true);

}

public Blog createBlog(String name) throws HibernateException {

Page 327: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Code Hibernate

Hibernate 3.3.1 313

Blog blog = new Blog();

blog.setName(name);

blog.setItems( new ArrayList() );

Session session = _sessions.openSession();

Transaction tx = null;

try {

tx = session.beginTransaction();

session.persist(blog);

tx.commit();

}

catch (HibernateException he) {

if (tx!=null) tx.rollback();

throw he;

}

finally {

session.close();

}

return blog;

}

public BlogItem createBlogItem(Blog blog, String title, String

text)

throws HibernateException {

BlogItem item = new BlogItem();

item.setTitle(title);

item.setText(text);

item.setBlog(blog);

item.setDatetime( Calendar.getInstance() );

blog.getItems().add(item);

Session session = _sessions.openSession();

Transaction tx = null;

try {

tx = session.beginTransaction();

session.update(blog);

tx.commit();

}

catch (HibernateException he) {

if (tx!=null) tx.rollback();

throw he;

}

finally {

session.close();

}

return item;

}

public BlogItem createBlogItem(Long blogid, String title, String

text)

throws HibernateException {

Page 328: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 22. Exemple : application W...

314 Hibernate 3.3.1

BlogItem item = new BlogItem();

item.setTitle(title);

item.setText(text);

item.setDatetime( Calendar.getInstance() );

Session session = _sessions.openSession();

Transaction tx = null;

try {

tx = session.beginTransaction();

Blog blog = (Blog) session.load(Blog.class, blogid);

item.setBlog(blog);

blog.getItems().add(item);

tx.commit();

}

catch (HibernateException he) {

if (tx!=null) tx.rollback();

throw he;

}

finally {

session.close();

}

return item;

}

public void updateBlogItem(BlogItem item, String text)

throws HibernateException {

item.setText(text);

Session session = _sessions.openSession();

Transaction tx = null;

try {

tx = session.beginTransaction();

session.update(item);

tx.commit();

}

catch (HibernateException he) {

if (tx!=null) tx.rollback();

throw he;

}

finally {

session.close();

}

}

public void updateBlogItem(Long itemid, String text)

throws HibernateException {

Session session = _sessions.openSession();

Transaction tx = null;

try {

tx = session.beginTransaction();

BlogItem item = (BlogItem) session.load(BlogItem.class,

itemid);

Page 329: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Code Hibernate

Hibernate 3.3.1 315

item.setText(text);

tx.commit();

}

catch (HibernateException he) {

if (tx!=null) tx.rollback();

throw he;

}

finally {

session.close();

}

}

public List listAllBlogNamesAndItemCounts(int max)

throws HibernateException {

Session session = _sessions.openSession();

Transaction tx = null;

List result = null;

try {

tx = session.beginTransaction();

Query q = session.createQuery(

"select blog.id, blog.name, count(blogItem) " +

"from Blog as blog " +

"left outer join blog.items as blogItem " +

"group by blog.name, blog.id " +

"order by max(blogItem.datetime)"

);

q.setMaxResults(max);

result = q.list();

tx.commit();

}

catch (HibernateException he) {

if (tx!=null) tx.rollback();

throw he;

}

finally {

session.close();

}

return result;

}

public Blog getBlogAndAllItems(Long blogid)

throws HibernateException {

Session session = _sessions.openSession();

Transaction tx = null;

Blog blog = null;

try {

tx = session.beginTransaction();

Query q = session.createQuery(

"from Blog as blog " +

"left outer join fetch blog.items " +

"where blog.id = :blogid"

);

Page 330: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 22. Exemple : application W...

316 Hibernate 3.3.1

q.setParameter("blogid", blogid);

blog = (Blog) q.uniqueResult();

tx.commit();

}

catch (HibernateException he) {

if (tx!=null) tx.rollback();

throw he;

}

finally {

session.close();

}

return blog;

}

public List listBlogsAndRecentItems() throws HibernateException

{

Session session = _sessions.openSession();

Transaction tx = null;

List result = null;

try {

tx = session.beginTransaction();

Query q = session.createQuery(

"from Blog as blog " +

"inner join blog.items as blogItem " +

"where blogItem.datetime > :minDate"

);

Calendar cal = Calendar.getInstance();

cal.roll(Calendar.MONTH, false);

q.setCalendar("minDate", cal);

result = q.list();

tx.commit();

}

catch (HibernateException he) {

if (tx!=null) tx.rollback();

throw he;

}

finally {

session.close();

}

return result;

}

}

Page 331: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 317

Chapitre 23. Exemple : quelquesmappings

Ce chapitre montre quelques mappings plus complexes.

23.1. Employeur/Employé (Employer/Employee)

Le modèle suivant de relation entre Employer et Employee utilise une vraieclasse entité (Employment) pour représenter l'association. On a fait cela parcequ'il peut y avoir plus d'une période d'emploi pour les deux mêmes parties.Des composants sont utilisés pour modéliser les valeurs monétaires et lesnoms des employés.

Voici un document de mapping possible :

<hibernate-mapping>

<class name="Employer" table="employers">

<id name="id">

<generator class="sequence">

<param name="sequence">employer_id_seq</param>

</generator>

</id>

<property name="name"/>

</class>

<class name="Employment" table="employment_periods">

<id name="id">

<generator class="sequence">

<param name="sequence">employment_id_seq</param>

</generator>

</id>

<property name="startDate" column="start_date"/>

Page 332: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 23. Exemple : quelquesmappings

318 Hibernate 3.3.1

<property name="endDate" column="end_date"/>

<component name="hourlyRate" class="MonetaryAmount">

<property name="amount">

<column name="hourly_rate" sql-type="NUMERIC(12,

2)"/>

</property>

<property name="currency" length="12"/>

</component>

<many-to-one name="employer" column="employer_id"

not-null="true"/>

<many-to-one name="employee" column="employee_id"

not-null="true"/>

</class>

<class name="Employee" table="employees">

<id name="id">

<generator class="sequence">

<param name="sequence">employee_id_seq</param>

</generator>

</id>

<property name="taxfileNumber"/>

<component name="name" class="Name">

<property name="firstName"/>

<property name="initial"/>

<property name="lastName"/>

</component>

</class>

</hibernate-mapping>

Et voici le schéma des tables générées par SchemaExport.

create table employers (

id BIGINT not null,

name VARCHAR(255),

primary key (id)

)

create table employment_periods (

id BIGINT not null,

hourly_rate NUMERIC(12, 2),

currency VARCHAR(12),

employee_id BIGINT not null,

employer_id BIGINT not null,

end_date TIMESTAMP,

start_date TIMESTAMP,

primary key (id)

)

create table employees (

id BIGINT not null,

Page 333: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Auteur/Travail (Author/Work)

Hibernate 3.3.1 319

firstName VARCHAR(255),

initial CHAR(1),

lastName VARCHAR(255),

taxfileNumber VARCHAR(255),

primary key (id)

)

alter table employment_periods

add constraint employment_periodsFK0 foreign key (employer_id)

references employers

alter table employment_periods

add constraint employment_periodsFK1 foreign key (employee_id)

references employees

create sequence employee_id_seq

create sequence employment_id_seq

create sequence employer_id_seq

23.2. Auteur/Travail (Author/Work)

Soit le modèle de la relation entre Work, Author et Person. Nousreprésentons la relation entre Work et Author comme une associationplusieurs-vers-plusieurs. Nous avons choisi de représenter la relation entreAuthor et Person comme une association un-vers-un. Une autre possibilitéaurait été que Author hérite de Person.

Le mapping suivant représente exactement ces relations :

<hibernate-mapping>

<class name="Work" table="works" discriminator-value="W">

Page 334: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 23. Exemple : quelquesmappings

320 Hibernate 3.3.1

<id name="id" column="id">

<generator class="native"/>

</id>

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

<property name="title"/>

<set name="authors" table="author_work">

<key column name="work_id"/>

<many-to-many class="Author" column name="author_id"/>

</set>

<subclass name="Book" discriminator-value="B">

<property name="text"/>

</subclass>

<subclass name="Song" discriminator-value="S">

<property name="tempo"/>

<property name="genre"/>

</subclass>

</class>

<class name="Author" table="authors">

<id name="id" column="id">

<!-- The Author must have the same identifier as the

Person -->

<generator class="assigned"/>

</id>

<property name="alias"/>

<one-to-one name="person" constrained="true"/>

<set name="works" table="author_work" inverse="true">

<key column="author_id"/>

<many-to-many class="Work" column="work_id"/>

</set>

</class>

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

<id name="id" column="id">

<generator class="native"/>

</id>

<property name="name"/>

</class>

</hibernate-mapping>

Il y a quatre tables dans ce mapping. works, authors et persons quicontiennent respectivement les données de work, author et person.

Page 335: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Client/Commande/Produit(Customer/Order/Product)

Hibernate 3.3.1 321

author_work est une table d'association qui lie authors à works. Voici leschéma de tables, généré par SchemaExport.

create table works (

id BIGINT not null generated by default as identity,

tempo FLOAT,

genre VARCHAR(255),

text INTEGER,

title VARCHAR(255),

type CHAR(1) not null,

primary key (id)

)

create table author_work (

author_id BIGINT not null,

work_id BIGINT not null,

primary key (work_id, author_id)

)

create table authors (

id BIGINT not null generated by default as identity,

alias VARCHAR(255),

primary key (id)

)

create table persons (

id BIGINT not null generated by default as identity,

name VARCHAR(255),

primary key (id)

)

alter table authors

add constraint authorsFK0 foreign key (id) references persons

alter table author_work

add constraint author_workFK0 foreign key (author_id) references

authors

alter table author_work

add constraint author_workFK1 foreign key (work_id) references

works

23.3. Client/Commande/Produit(Customer/Order/Product)

Imaginons maintenant le modèle de relation entre Customer, Order, LineItemet Product. Il y a une association un-vers-plusieurs entre Customer et Order,mais comment devrions nous représenter Order / LineItem / Product? J'aichoisi de mapper LineItem comme une classe d'association représentantl'association plusieurs-vers-plusieurs entre Order et Product. Dans Hibernate,on appelle cela un élément composite.

Page 336: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 23. Exemple : quelquesmappings

322 Hibernate 3.3.1

Le document de mapping :

<hibernate-mapping>

<class name="Customer" table="customers">

<id name="id">

<generator class="native"/>

</id>

<property name="name"/>

<set name="orders" inverse="true">

<key column="customer_id"/>

<one-to-many class="Order"/>

</set>

</class>

<class name="Order" table="orders">

<id name="id">

<generator class="native"/>

</id>

<property name="date"/>

<many-to-one name="customer" column="customer_id"/>

<list name="lineItems" table="line_items">

<key column="order_id"/>

<list-index column="line_number"/>

<composite-element class="LineItem">

<property name="quantity"/>

<many-to-one name="product" column="product_id"/>

</composite-element>

</list>

</class>

<class name="Product" table="products">

<id name="id">

<generator class="native"/>

</id>

<property name="serialNumber"/>

</class>

</hibernate-mapping>

Page 337: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Divers mappings d'exemple

Hibernate 3.3.1 323

customers, orders, line_items et products contiennent les données decustomer, order, order line item et product. line_items est aussi la tabled'association liant orders à products.

create table customers (

id BIGINT not null generated by default as identity,

name VARCHAR(255),

primary key (id)

)

create table orders (

id BIGINT not null generated by default as identity,

customer_id BIGINT,

date TIMESTAMP,

primary key (id)

)

create table line_items (

line_number INTEGER not null,

order_id BIGINT not null,

product_id BIGINT,

quantity INTEGER,

primary key (order_id, line_number)

)

create table products (

id BIGINT not null generated by default as identity,

serialNumber VARCHAR(255),

primary key (id)

)

alter table orders

add constraint ordersFK0 foreign key (customer_id) references

customers

alter table line_items

add constraint line_itemsFK0 foreign key (product_id) references

products

alter table line_items

add constraint line_itemsFK1 foreign key (order_id) references

orders

23.4. Divers mappings d'exemple

Ces exemples sont tous pris de la suite de tests d'Hibernate. Vousen trouverez beaucoup d'autres. Regardez dans le dossier test de ladistribution d'Hibernate.

TODO: put words around this stuff

Page 338: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 23. Exemple : quelquesmappings

324 Hibernate 3.3.1

23.4.1. "Typed" one-to-one association

<class name="Person">

<id name="name"/>

<one-to-one name="address"

cascade="all">

<formula>name</formula>

<formula>'HOME'</formula>

</one-to-one>

<one-to-one name="mailingAddress"

cascade="all">

<formula>name</formula>

<formula>'MAILING'</formula>

</one-to-one>

</class>

<class name="Address" batch-size="2"

check="addressType in ('MAILING', 'HOME', 'BUSINESS')">

<composite-id>

<key-many-to-one name="person"

column="personName"/>

<key-property name="type"

column="addressType"/>

</composite-id>

<property name="street" type="text"/>

<property name="state"/>

<property name="zip"/>

</class>

23.4.2. Exemple de clef composée

<class name="Customer">

<id name="customerId"

length="10">

<generator class="assigned"/>

</id>

<property name="name" not-null="true" length="100"/>

<property name="address" not-null="true" length="200"/>

<list name="orders"

inverse="true"

cascade="save-update">

<key column="customerId"/>

<index column="orderNumber"/>

<one-to-many class="Order"/>

</list>

</class>

<class name="Order" table="CustomerOrder" lazy="true">

Page 339: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Exemple de clef composée

Hibernate 3.3.1 325

<synchronize table="LineItem"/>

<synchronize table="Product"/>

<composite-id name="id"

class="Order$Id">

<key-property name="customerId" length="10"/>

<key-property name="orderNumber"/>

</composite-id>

<property name="orderDate"

type="calendar_date"

not-null="true"/>

<property name="total">

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

</formula>

</property>

<many-to-one name="customer"

column="customerId"

insert="false"

update="false"

not-null="true"/>

<bag name="lineItems"

fetch="join"

inverse="true"

cascade="save-update">

<key>

<column name="customerId"/>

<column name="orderNumber"/>

</key>

<one-to-many class="LineItem"/>

</bag>

</class>

<class name="LineItem">

<composite-id name="id"

class="LineItem$Id">

<key-property name="customerId" length="10"/>

<key-property name="orderNumber"/>

<key-property name="productId" length="10"/>

</composite-id>

<property name="quantity"/>

<many-to-one name="order"

Page 340: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 23. Exemple : quelquesmappings

326 Hibernate 3.3.1

insert="false"

update="false"

not-null="true">

<column name="customerId"/>

<column name="orderNumber"/>

</many-to-one>

<many-to-one name="product"

insert="false"

update="false"

not-null="true"

column="productId"/>

</class>

<class name="Product">

<synchronize table="LineItem"/>

<id name="productId"

length="10">

<generator class="assigned"/>

</id>

<property name="description"

not-null="true"

length="200"/>

<property name="price" length="3"/>

<property name="numberAvailable"/>

<property name="numberOrdered">

<formula>

( select sum(li.quantity)

from LineItem li

where li.productId = productId )

</formula>

</property>

</class>

23.4.3. Many-to-many avec une clef composée partagée

<class name="User" table="`User`">

<composite-id>

<key-property name="name"/>

<key-property name="org"/>

</composite-id>

<set name="groups" table="UserGroup">

<key>

<column name="userName"/>

<column name="org"/>

</key>

<many-to-many class="Group">

<column name="groupName"/>

Page 341: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Contenu basé sur une discrimination

Hibernate 3.3.1 327

<formula>org</formula>

</many-to-many>

</set>

</class>

<class name="Group" table="`Group`">

<composite-id>

<key-property name="name"/>

<key-property name="org"/>

</composite-id>

<property name="description"/>

<set name="users" table="UserGroup" inverse="true">

<key>

<column name="groupName"/>

<column name="org"/>

</key>

<many-to-many class="User">

<column name="userName"/>

<formula>org</formula>

</many-to-many>

</set>

</class>

23.4.4. Contenu basé sur une discrimination

<class name="Person"

discriminator-value="P">

<id name="id"

column="person_id"

unsaved-value="0">

<generator class="native"/>

</id>

<discriminator

type="character">

<formula>

case

when title is not null then 'E'

when salesperson is not null then 'C'

else 'P'

end

</formula>

</discriminator>

<property name="name"

not-null="true"

length="80"/>

<property name="sex"

not-null="true"

update="false"/>

Page 342: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 23. Exemple : quelquesmappings

328 Hibernate 3.3.1

<component name="address">

<property name="address"/>

<property name="zip"/>

<property name="country"/>

</component>

<subclass name="Employee"

discriminator-value="E">

<property name="title"

length="20"/>

<property name="salary"/>

<many-to-one name="manager"/>

</subclass>

<subclass name="Customer"

discriminator-value="C">

<property name="comments"/>

<many-to-one name="salesperson"/>

</subclass>

</class>

23.4.5. Associations sur des clefs alternées

<class name="Person">

<id name="id">

<generator class="hilo"/>

</id>

<property name="name" length="100"/>

<one-to-one name="address"

property-ref="person"

cascade="all"

fetch="join"/>

<set name="accounts"

inverse="true">

<key column="userId"

property-ref="userId"/>

<one-to-many class="Account"/>

</set>

<property name="userId" length="8"/>

</class>

<class name="Address">

<id name="id">

<generator class="hilo"/>

Page 343: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Associations sur des clefs alternées

Hibernate 3.3.1 329

</id>

<property name="address" length="300"/>

<property name="zip" length="5"/>

<property name="country" length="25"/>

<many-to-one name="person" unique="true" not-null="true"/>

</class>

<class name="Account">

<id name="accountId" length="32">

<generator class="uuid"/>

</id>

<many-to-one name="user"

column="userId"

property-ref="userId"/>

<property name="type" not-null="true"/>

</class>

Page 344: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

330 Hibernate 3.3.1

Page 345: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 331

Chapitre 24. Meilleures pratiquesDécoupez finement vos classes et mappez les en utilisant <component>.

Utilisez une classe Adresse pour encapsuler Rue, Region, CodePostal. Cecipermet la réutilisation du code et simplifie la maintenance.

Déclarez des propriétés d'identifiants dans les classes persistantes.Hibernate rend les propriétés d'identifiants optionnelles. Il existebeaucoup de raisons pour lesquelles vous devriez les utiliser. Nousrecommandons que vous utilisiez des identifiants techniques (générés, etsans connotation métier).

Identifiez les clefs naturelles.Identifiez les clefs naturelles pour toutes les entités, et mappez les avec<natural-id>. Implémentez equals() et hashCode() pour comparer lespropriétés qui composent la clef naturelle.

Placez chaque mapping de classe dans son propre fichier.N'utilisez pas un unique document de mapping. Mappez com.eg.Foo dansle fichier com/eg/Foo.hbm.xml. Cela prend tout son sens lors d'un travailen équipe.

Chargez les mappings comme des ressources.Déployez les mappings en même temps que les classes qu'ils mappent.

Pensez à externaliser les chaînes de caractères.Ceci est une bonne habitude si vos requêtes appellent des fonctions SQLqui ne sont pas au standard ANSI. Cette externalisation dans les fichiersde mapping rendra votre application plus portable.

Utilisez les variables "bindées".Comme en JDBC, remplacez toujours les valeurs non constantes par"?". N'utilisez jamais la manipulation des chaînes de caractères pourremplacer des valeurs non constantes dans une requête ! Encore mieux,utilisez les paramètres nommés dans les requêtes.

Ne gérez pas vous même les connexions JDBC.Hibernate laisse l'application gérer les connexions JDBC. Vousne devriez gérer vos connexions qu'en dernier recours. Sivous ne pouvez pas utiliser les systèmes de connexions livrés,réfléchissez à l'idée de fournir votre propre implémentation deorg.hibernate.connection.ConnectionProvider.

Pensez à utiliser les types utilisateurs.Supposez que vous ayez une type Java, de telle bibliothèque, qui abesoin d'être persisté mais qui ne fournit pas les accesseurs nécessaires

Page 346: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 24. Meilleures pratiques

332 Hibernate 3.3.1

pour le mapper comme composant. Vous devriez implémenterorg.hibernate.UserType.Cette approche libère le code de l'application del'implémentation des transformations vers / depuis les types Hibernate.

Utilisez du JDBC pur dans les goulets d'étranglement.Dans certaines parties critiques de votre système d'un point de vueperformance, quelques opérations peuvent tirer partie d'un appel JDBCnatif. Mais attendez de savoir que c'est un goulet d'étranglement. Nesupposez jamais qu'un appel JDBC sera forcément plus rapide. Si vousavez besoin d'utiliser JDBC directement, ouvrez une Session Hibernateet utilisez la connexion SQL sous-jacente. Ainsi vous pourrez utiliser lamême stratégie de transation et la même gestion des connexions.

Comprendre le flush de Session.De temps en temps la Session synchronise ses états persistants avecla base de données. Les performances seront affectées si ce processusarrive trop souvent. Vous pouvez parfois minimiser les flush nonnécessaires en désactivant le flush automatique ou même en changeantl'ordre des opérations menées dans une transaction particulière.

Dans une architecture à trois couches, pensez à utiliser saveOrUpdate().Quand vous utilisez une architecture à base de servlet / session bean,vous pourriez passer des objets chargés dans le bean session vers etdepuis la couche servlet / JSP. Utilisez une nouvelle session pour traiterchaque requête. Utilisez Session.merge() ou Session.saveOrUpdate()pour synchroniser les objets avec la base de données.

Dans une architecture à deux couches, pensez à utiliser la déconnexion desession.

Les transactions de bases de données doivent être aussi courtes quepossible pour une meilleure montée en charge.Cependant, il est souventnécessaire d'implémenter de longues transactions applicatives, unesimple unité de travail du point de vue de l'utilisateur. Une transactionapplicative peut s'étaler sur plusieurs cycles de requêtes/réponses duclient. Il est commun d'utiliser des objets détachés pour implémenterdes transactions applicatives. Une alternative, extrêmement appropriéedans une architecture à 2 couches, est de maintenir un seul contact depersistance ouvert (session) pour toute la durée de vie de la transactionapplicative et simplement se déconnecter de la connexion JDBC à la finde chaque requête, et se reconnecter au début de la requête suivante.Ne partagez jamais une seule session avec plus d'une transactionapplicative, ou vous travaillerez avec des données périmées.

Page 347: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Hibernate 3.3.1 333

Considérez que les exceptions ne sont pas rattrapables.Il s'agit plus d'une pratique obligatoire que d'une "meilleure pratique".Quand une exception intervient, il faut faire un rollback de la Transactionet fermer la Session. Sinon, Hibernate ne peut garantir l'intégrité des étatspersistants en mémoire. En particulier, n'utilisez pas Session.load() pourdéterminer si une instance avec un identifiant donné existe en base dedonnées, utilisez Session.get() ou un requête.

Préférez le chargement tardif des associations.Utilisez le chargement complet avec modération. Utilisez les proxies etles collections chargées tardivement pour la plupart des associations versdes classes qui ne sont pas susceptibles d'être complètement retenuesdans le cache de second niveau. Pour les assocations de classes encache, où il y a une extrêmement forte probabilité que l'élément soit encache, désactivez explicitement le chargement par jointures ouvertes enutilisant outer-join="false". Lorsqu'un chargement par jointure ouverteest approprié pour un cas d'utilisation particulier, utilisez une requêteavec un left join fetch.

Utilisez le pattern d'une ouverture de session dans une vue, ou une phased'assemblage disciplinée pour éviter des problèmes avec des données nonrapatriées.

Hibernate libère les développeurs de l'écriture fastidieuse des objets detransfert de données (NdT : Data Transfer Objects) (DTO). Dans unearchitecture EJB traditionnelle, les DTOs ont deux buts : premièrement,ils contournent le problème des "entity bean" qui ne sont pas sérialisables; deuxièmement, ils définissent implicitement une phase d'assemblageoù toutes les données utilisées par la vue sont rapatriées et organiséesdans les DTOs avant de retourner sous le contrôle de la couche deprésentation. Hibernate élimine le premier but. Pourtant, vous aurezencore besoin d'une phase d'assemblage (pensez vos méthodes métiercomme ayant un contrat strict avec la couche de présentation à proposde quelles données sont disponibles dans les objets détachés) à moinsque vous soyez préparés à garder le contexte de persistance (la session)ouvert à travers tout le processus de rendu de la vue.

Pensez à abstraite votre logique métier d'Hibernate.Cachez le mécanisme d'accès aux données (Hibernate) derrière uneinterface. Combinez les patterns DAO et Thread Local Session. Vouspouvez même avoir quelques classes persistées par du JDBC pur,associées à Hibernate via un UserType (ce conseil est valable pourdes applications de taille respectables ; il n'est pas valable pour uneapplication avec cinq tables).

Page 348: HIBERNATE - Persistance relationnelle en Java standard · Hibernate 3.3.1 v uniques et non nulles. Si elle est aussi immuable, c'est encore mieux. Mappez les propriétés de la clé

Chapitre 24. Meilleures pratiques

334 Hibernate 3.3.1

N'utilisez pas d'associations de mapping exotiques.De bons cas d'utilisation pour de vraies associationsplusieurs-vers-plusieurs sont rares. La plupart du temps vousavez besoin d'informations additionnelles stockées dans la tabled'association. Dans ce cas, il est préférable d'utiliser deux associationsun-vers-plusieurs vers une classe de liaisons intermédiaire. En fait, nouspensons que la plupart des associations sont de type un-vers-plusieursou plusieurs-vers-un, vous devez être très attentifs lorsque vous utilisezautre chose et vous demander si c'est vraiment nécessaire.

Préférez les associations bidirectionnelles.Les associations unidirectionnelles sont plus difficiles à questionner.Dans une grande application, la plupart des associations devraient êtrenavigables dans les deux directions dans les requêtes.