Introduction À Java EE - 2012 PDF
Introduction À Java EE - 2012 PDF
par l'exemple
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
1/489
INTRODUCTION
Ce document reprend un précédent document écrit en 2010 et intitulé "Introduction à Java EE avec Netbeans 6.8 et le serveur
Glassfish v3". Celui-ci amène principalement les changements suivants :
• la partie JSF (Java Server Faces) est traitée dans un document à part : " Introduction à Java Server Faces, Primefaces et
Primefaces mobile " disponible à l'URL [[Link] On y utilise des caractéristiques
de la version 2 de JSF,
• les projets sont des projets Maven.
Java EE signifie Java Enterprise Edition. J2EE (Java 2 Enterprise Edition) était le terme précédent. J2EE désigne les
technologies Java utilisées pour créer des applications d'entreprise avec le JDK 1.4 ou antérieur. En même temps que le JDK 1.5
amenait de nombreuses nouveautés dans le langage Java, Sun introduisait de nouvelles technologies s'appuyant sur ce langage
amélioré afin de remédier à des lacunes de ces mêmes technologies dans J2EE. Le terme Java EE 5 a alors été utilisé pour désigner
l'ensemble des technologies qui concourent à créer une application d'entreprise avec la plate-forme Java. Au moment de la mise à
jour de ce document, la dernière version de Java EE est Java EE 6.
sont d'excellents livres pour découvrir les technologies de Java EE 5 et Java EE 6. Toutes les technologies importantes de Java EE
y sont passées en revue dans le contexte d'études de cas réalistes. L'auteur a un site [[Link] que le
lecteur est invité à visiter.
Le document présent étudie certaines des technologies de Java EE 5. Nous y créons une application basique à trois couches
[présentation, métier, accès aux données] déclinée en plusieurs versions :
Certaines technologies Java EE ne sont pas présentées telles les MDB (Message Driven Bean) ou les EJB3 stateful. Pour les
découvrir, on lira les livres d'Antonio Goncalves.
Il existe d'autres technologies Open Source, autres que Java EE, disponibles pour créer des applications trois couches. Une tandem
très populaire est Spring ([Link] / Hibernate ([Link] Afin de permettre au
lecteur de comparer les technologies EJB3 et Spring, l'application précédente a des versions où Spring remplace les EJB3.
Ce document est un TD (Travail Dirigé) utilisé en 5ième année de l'école d'ingénieurs ISTIA de l'université d'Angers
[[Link] Ce TD décrit l'application à construire, les technologies Java à utiliser, les endroits où trouver de
l'information. La solution proposée est souvent très cadrée. Le TD pose des questions dont il ne donne pas les réponses. C'est à
l'étudiant de les trouver.
L'apprentissage Java EE proposé ici nécessite un investissement du lecteur estimé entre 50 et 100 heures. Le document contient
beaucoup de code rendant possible le copier / coller. Par ailleurs, tous les projets Netbeans sont décrits dans le détail. Globalement,
le document donne les squelettes des solutions et il est demandé à l'étudiant d'en donner certains détails. Le document peut être
utile même à quelqu'un ne pouvant ou ne voulant pas s'investir autant. On peut s'intéresser uniquement aux architectures décrites et
délaisser la partie code qui fait l'objet des questions.
Pour développer et exécuter l'application, nous utilisons l'IDE Netbeans. Netbeans est un produit assez lourd : prévoir 1 Go de
Ram pour travailler confortablement. On peut le télécharger à l'url [[Link]
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
2/489
Le document fait référence aux cours suivants :
1. Persistance Java 5 par la pratique : [[Link] - donne les outils pour construire la couche
d'accès aux données avec JPA (Java Persistence API) ;
2. Introduction au langage Java [[Link] - pour les débutants ;
3. Introduction par l'exemple à Java Server Faces, Primefaces et Primefaces mobile
[[Link]
4. Introduction au langage Java et à l'écosystème Spring au travers d'une étude de cas [[Link]
tutoriels-cours/intro-java-spring/serge-tahe-introduction-au-langage-java-et-a-l-ecosysteme-spring/];
5. Exploiter une base de données relationnelle avec l'écosystème Spring
[[Link]
cors/]
Ces supports de cours sont par la suite référencés [ref1] [ref2] [ref3] [ref4] [ref5].
Notes :
• En utilisant ce document, vous devez faire preuve de souplesse. Vous allez peut-être utiliser une version de Netbeans
différente de celle utilisée pour l'écriture de ce document. Ainsi certaines copies d'écran peuvent avoir changé ou disparu.
Il en est de même pour les options de menu. A chaque fois, il faut vous adapter ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
3/489
Table des matières
1 LE COURS ET L'ÉTUDE DE CAS......................................................................................................................................10
1.1 ARCHITECTURE D'UNE APPLICATION JAVA EN COUCHES.................................................................................................................10
1.2 LES OUTILS DU DOCUMENT.........................................................................................................................................................13
1.2.1 MAVEN..................................................................................................................................................................................13
[Link] Introduction......................................................................................................................................................................13
[Link] Exécution du projet..........................................................................................................................................................16
[Link] Le système de fichiers d'un projet Maven.......................................................................................................................18
[Link] Le dépôt Maven local......................................................................................................................................................18
[Link] Chercher un artifact avec Maven.....................................................................................................................................19
1.3 JPA EN RÉSUMÉ........................................................................................................................................................................24
1.3.1 LA PLACE DE JPA DANS UNE ARCHITECTURE EN COUCHES.............................................................................................................24
1.3.2 JPA – EXEMPLES.....................................................................................................................................................................24
[Link] Exemple 1 - Représentation objet d'une table unique.....................................................................................................24
[Link].1 La table [personne].......................................................................................................................................................24
[Link].2 L'entité [Personne]........................................................................................................................................................25
[Link] Configuration de la couche JPA.......................................................................................................................................27
[Link] Exemple 2 : relation un-à-plusieurs.................................................................................................................................30
[Link].1 Le schéma de la base de données.................................................................................................................................30
[Link].2 Les objets @Entity représentant la base de données....................................................................................................30
1.3.3 L'API DE LA COUCHE JPA.......................................................................................................................................................32
1.3.4 LES REQUÊTES JPQL..............................................................................................................................................................35
[Link] La table [MEDECINS]....................................................................................................................................................35
[Link] La table [CLIENTS]........................................................................................................................................................35
[Link] La table [CRENEAUX]...................................................................................................................................................36
[Link] La table [RV]...................................................................................................................................................................36
[Link] Génération de la base.......................................................................................................................................................37
[Link] La couche [JPA]...............................................................................................................................................................38
[Link] Le projet Netbeans...........................................................................................................................................................39
[Link] Génération de la couche [JPA].........................................................................................................................................39
[Link].1 Création d'une connexion Netbeans à la base de données............................................................................................39
[Link].2 Création d'une unité de persistance..............................................................................................................................40
[Link].3 Génération des entités JPA............................................................................................................................................43
[Link].4 Les entités JPA générées...............................................................................................................................................45
[Link] Le code d'accès aux données...........................................................................................................................................52
1.3.5 LIENS ENTRE CONTEXTE DE PERSISTANCE ET SGBD.....................................................................................................................54
[Link] L'architecture de l'application..........................................................................................................................................54
[Link] La base de données..........................................................................................................................................................54
[Link] Le projet Maven [mv-personnes]....................................................................................................................................55
[Link] Configuration Maven.......................................................................................................................................................55
[Link] Configuration de la couche [jpa].....................................................................................................................................55
[Link] L'entité JPA [Personne]....................................................................................................................................................56
[Link] Création de la table [PERSONNES]................................................................................................................................57
[Link] Tests des liens entre contexte de persistance et base de données....................................................................................58
[Link].1 La méthode [test1]........................................................................................................................................................59
[Link].2 La méthode [test2]........................................................................................................................................................61
1.4 VERSION 1 : ARCHITECTURE SPRING / JPA................................................................................................................................63
1.4.1 LA BASE DE DONNÉES..............................................................................................................................................................63
1.4.2 MODE DE CALCUL DU SALAIRE D'UNE ASSISTANTE MATERNELLE......................................................................................................64
1.4.3 FONCTIONNEMENT DE L'APPLICATION CONSOLE.............................................................................................................................65
1.4.4 FONCTIONNEMENT DE L'APPLICATION GRAPHIQUE..........................................................................................................................66
1.4.5 CRÉATION DE LA BASE DE DONNÉES............................................................................................................................................66
1.4.6 IMPLÉMENTATION JPA.............................................................................................................................................................68
[Link] Couche JPA / Hibernate...................................................................................................................................................68
[Link].1 Configuration de la couche JPA....................................................................................................................................69
[Link].2 Les dépendances...........................................................................................................................................................72
[Link].3 Les entités JPA..............................................................................................................................................................73
[Link].4 Le code de la classe principale.....................................................................................................................................74
[Link].5 Tests..............................................................................................................................................................................75
[Link] Couche JPA / EclipseLink................................................................................................................................................79
[Link] Travail à faire...................................................................................................................................................................85
[Link] Lazy ou Eager ?...............................................................................................................................................................85
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
4/489
[Link] Travail à faire...................................................................................................................................................................89
1.4.7 LE PROJET MAVEN [MV-PAM-SPRING-HIBERNATE].........................................................................................................................95
[Link] Configuration Maven.......................................................................................................................................................95
[Link] La couche [JPA]...............................................................................................................................................................96
[Link] Les interfaces des couches [métier] et [DAO]...............................................................................................................100
[Link] La classe [PamException]..............................................................................................................................................106
1.4.8 LA COUCHE [DAO] DE L'APPLICATION [PAM].........................................................................................................................108
[Link] Implémentation..............................................................................................................................................................108
[Link] Configuration Spring / JPA............................................................................................................................................109
[Link] Tests...............................................................................................................................................................................110
[Link].1 JUnitInitDB.................................................................................................................................................................110
[Link].2 Mise en oeuvre des tests.............................................................................................................................................112
[Link].3 JUnitDao.....................................................................................................................................................................114
1.4.9 LA COUCHE [MÉTIER] DE L'APPLICATION [PAM]........................................................................................................................118
[Link] L'interface Java [IMetier]...............................................................................................................................................118
[Link] La classe [FeuilleSalaire]...............................................................................................................................................118
[Link] La classe d'implémentation [Metier] de la couche [metier]...........................................................................................119
[Link] Tests de la couche [metier]............................................................................................................................................120
1.4.10 LA COUCHE [UI] DE L'APPLICATION [PAM] – VERSION CONSOLE................................................................................................123
[Link] La classe [[Link]].........................................................................................................................................123
[Link] Exécution.....................................................................................................................................................................124
1.4.11 LA COUCHE [UI] DE L'APPLICATION [PAM] – VERSION GRAPHIQUE.............................................................................................126
[Link] Un rapide tutoriel.........................................................................................................................................................127
[Link] L'interface graphique [PamJFrame].............................................................................................................................129
[Link] Les événements de l'interface graphique.....................................................................................................................130
[Link] Initialisation de l'interface graphique...........................................................................................................................131
[Link] Gestionnaires d'événements.........................................................................................................................................133
[Link] Exécution de l'interface graphique...............................................................................................................................133
1.4.12 IMPLÉMENTATION DE LA COUCHE JPA AVEC ECLIPSELINK.........................................................................................................134
[Link] Le projet Netbeans.......................................................................................................................................................134
[Link] Mise en oeuvre des tests..............................................................................................................................................137
[Link].1 JUnitDao...................................................................................................................................................................138
[Link] Les autres tests.............................................................................................................................................................138
[Link] Travail à faire...............................................................................................................................................................138
1.5 VERSION 2 : ARCHITECTURE OPENEJB / JPA.........................................................................................................................139
1.5.1 INTRODUCTION AUX PRINCIPES DU PORTAGE...............................................................................................................................139
[Link] Les nouvelles architectures............................................................................................................................................139
[Link] Les bibliothèques des projets.........................................................................................................................................139
[Link] Configuration de la couche JPA / EclipseLink / OpenEJB............................................................................................139
[Link] Implémentation de la couche [DAO] par des EJB.........................................................................................................140
[Link] Implémentation de la couche [metier] par un EJB........................................................................................................142
[Link] Les clients des EJB........................................................................................................................................................142
1.5.2 TRAVAIL PRATIQUE.................................................................................................................................................................143
[Link] Configuration initiale du projet Netbeans [mv-pam-openejb-eclipselink]....................................................................144
[Link] Portage de la couche [DAO]..........................................................................................................................................146
[Link].1 L'EJB [CotisationDao]................................................................................................................................................147
[Link].2 Les EJB [EmployeDao] et [IndemniteDao]................................................................................................................147
[Link].3 La classe [PamException]...........................................................................................................................................148
[Link].4 Entités sérialisables.....................................................................................................................................................148
[Link].5 Tests de la couche [DAO]...........................................................................................................................................149
[Link] Portage de la couche [metier]........................................................................................................................................154
[Link].1 L'EJB [Metier]............................................................................................................................................................154
[Link].2 Tests de la couche [metier].........................................................................................................................................155
[Link] Portage de la couche [console]......................................................................................................................................156
[Link] Portage de la couche [swing].........................................................................................................................................159
1.5.3 CONCLUSION........................................................................................................................................................................159
1.6 VERSION 3 : PORTAGE DE L'APPLICATION PAM SUR UN SERVEUR D'APPLICATIONS GLASSFISH......................................................160
1.6.1 LA PARTIE SERVEUR DE L'APPLICATION CLIENT / SERVEUR PAM...................................................................................................160
[Link] L'architecture de l'application........................................................................................................................................160
[Link].1 Le projet Netbeans......................................................................................................................................................161
[Link].2 Configuration Maven..................................................................................................................................................162
[Link].3 Configuration de la couche de persistance.................................................................................................................164
[Link].4 Insertion des couches [JPA, DAO, metier].................................................................................................................168
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
5/489
[Link].5 Déploiement de l'application sur le serveur Glassfish................................................................................................168
[Link] Client console - version 1..............................................................................................................................................170
[Link] Client console - version 2..............................................................................................................................................175
[Link] Le client Swing..............................................................................................................................................................179
[Link] Portage des tests unitaires..............................................................................................................................................179
1.7 VERSION 4 – CLIENT / SERVEUR DANS UNE ARCHITECTURE DE SERVICE WEB SOAP......................................................................183
1.7.1 SERVICE WEB SOAP IMPLÉMENTÉ PAR UN EJB........................................................................................................................184
[Link] La partie serveur............................................................................................................................................................184
[Link] La partie cliente.............................................................................................................................................................187
[Link].1 Le projet Netbeans du client console..........................................................................................................................187
[Link].2 Le client console du service web Metier....................................................................................................................189
[Link].3 Le client swing du service web Metier.......................................................................................................................191
1.7.2 SERVICE WEB SOAP IMPLÉMENTÉ PAR UNE APPLICATION WEB.....................................................................................................192
[Link] La partie serveur............................................................................................................................................................192
[Link] La partie cliente.............................................................................................................................................................196
1.7.3 SERVICE WEB SOAP IMPLÉMENTÉ AVEC SPRING, HIBERNATE ET TOMCAT.....................................................................................196
[Link] La partie serveur............................................................................................................................................................197
[Link] La partie cliente.............................................................................................................................................................202
1.7.4 SERVICE WEB SOAP IMPLÉMENTÉ AVEC SPRING, ECLIPSELINK ET TOMCAT..................................................................................204
1.7.5 CONCLUSION........................................................................................................................................................................210
1.8 INTRODUCTION À JAVA SERVER FACES......................................................................................................................................212
1.9 VERSION 5 - APPLICATION PAM WEB / JSF...........................................................................................................................213
1.9.1 ARCHITECTURE DE L'APPLICATION............................................................................................................................................213
1.9.2 FONCTIONNEMENT DE L'APPLICATION........................................................................................................................................215
1.9.3 LE PROJET NETBEANS............................................................................................................................................................216
[Link] Les fichiers de configuration.........................................................................................................................................221
[Link] La feuille de style...........................................................................................................................................................223
[Link] Le fichier des messages.................................................................................................................................................223
[Link] La portée des beans........................................................................................................................................................224
[Link] La couche [métier].........................................................................................................................................................224
1.9.4 LE FORMULAIRE [[Link]] ET SON MODÈLE [[Link]]....................................................................................................225
[Link] étape 1............................................................................................................................................................................226
[Link] étape 2............................................................................................................................................................................226
[Link] étape 3............................................................................................................................................................................227
[Link] étape 4............................................................................................................................................................................228
1.10 VERSION 6 - INTÉGRATION DE LA COUCHE WEB DANS UNE ARCHITECTURE 3 COUCHES JSF / EJB...............................................229
1.10.1 ARCHITECTURE DE L'APPLICATION..........................................................................................................................................229
1.10.2 LE PROJET NETBEANS DE LA COUCHE WEB..............................................................................................................................229
1.10.3 LE PROJET NETBEANS DE L'APPLICATION D'ENTREPRISE.............................................................................................................231
1.11 VERSION 7 - APPLICATION WEB PAM MULTI-VUES / MULTI-PAGES............................................................................................235
1.11.1 LES VUES DE L'APPLICATION..................................................................................................................................................236
1.11.2 LE PROJET NETBEANS DE LA COUCHE [WEB]............................................................................................................................237
[Link] Les fichiers de configuration.......................................................................................................................................238
[Link] La feuille de style.........................................................................................................................................................238
[Link] Le fichier des messages...............................................................................................................................................239
[Link] La couche [métier].......................................................................................................................................................240
1.11.3 LES BEANS DE L'APPLICATION................................................................................................................................................241
[Link] Le bean ApplicationData..............................................................................................................................................241
[Link] Le bean SessionData....................................................................................................................................................242
[Link] Le bean Form...............................................................................................................................................................243
1.11.4 LES PAGES DE L'APPLICATION.................................................................................................................................................244
[Link] [[Link]]...............................................................................................................................................................244
[Link] L'entête [[Link]].................................................................................................................................................245
1.11.5 LES CAS D'UTILISATION DE L'APPLICATION................................................................................................................................245
[Link] Affichage de la page d'accueil.....................................................................................................................................245
[Link] L'action [faireSimulation]............................................................................................................................................246
[Link] La gestion des erreurs..................................................................................................................................................248
[Link] L'action [effacerSimulation]........................................................................................................................................250
[Link] L'action [enregistrerSimulation]..................................................................................................................................251
[Link] L'action [retourSimulateur]..........................................................................................................................................255
[Link] L'action [voirSimulations]...........................................................................................................................................255
[Link] L'action [retirerSimulation]..........................................................................................................................................256
[Link] L'action [terminerSession]...........................................................................................................................................257
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
6/489
1.11.6 INTÉGRATION DE LA COUCHE WEB DANS UNE ARCHITECTURE 3 COUCHES JSF / EJB.....................................................................258
1.12 VERSIONS 8 : PORTAGES DE L'APPLICATION JSF MULTIPAGES..................................................................................................260
1.12.1 ENVIRONNEMENT SPRING / HIBERNATE / TOMCAT....................................................................................................................260
1.12.2 ENVIRONNEMENT SPRING / ECLIPSELINK / TOMCAT.................................................................................................................267
1.12.3 ENVIRONNEMENT SPRING / ECLIPSELINK / GLASSFISH..............................................................................................................267
[Link] Etape 1.........................................................................................................................................................................268
[Link] Etape 2.........................................................................................................................................................................271
[Link] Exécution.....................................................................................................................................................................274
1.13 VERSION 9 : IMPLÉMENTATION DE LA COUCHE WEB AVEC PRIMEFACES......................................................................................276
2 ÉTUDE DE CAS N° 2 : CLIENT / SERVEUR - EJB / SOAP..........................................................................................280
2.1 LES COMPÉTENCES MISES EN OEUVRE........................................................................................................................................280
2.2 LE PROBLÈME.........................................................................................................................................................................280
2.3 LES ÉLÉMENTS DE L'APPLICATION SPRING / HIBERNATE.............................................................................................................280
2.3.1 LA BASE DE DONNÉES............................................................................................................................................................281
[Link] La table [MEDECINS]..................................................................................................................................................281
[Link] La table [CLIENTS]......................................................................................................................................................281
[Link] La table [CRENEAUX].................................................................................................................................................282
[Link] La table [RV].................................................................................................................................................................282
2.3.2 L'ARCHITECTURE DE L'APPLICATION SPRING / HIBERNATE............................................................................................................283
2.3.3 LE PROJET NETBEANS DE L'APPLICATION SPRING / HIBERNATE.....................................................................................................283
2.4 TESTS DE L'APPLICATION SPRING / HIBERNATE..........................................................................................................................284
2.5 CRÉATION D'UN SERVEUR EJB.................................................................................................................................................285
2.6 CRÉATION D'UN CLIENT POUR LE SERVEUR EJB........................................................................................................................287
2.7 CRÉATION D'UN SERVICE WEB SOAP.......................................................................................................................................287
2.8 CRÉATION D'UN CLIENT POUR LE SERVICE WEB SOAP...............................................................................................................292
3 ÉTUDE DE CAS N° 3 : GESTION DE BILANS DE FORMATION...............................................................................294
3.1 LES COMPÉTENCES MISES EN OEUVRE........................................................................................................................................294
3.2 LE PROBLÈME.........................................................................................................................................................................294
3.3 L'ARCHITECTURE DE L'APPLICATION.........................................................................................................................................295
3.4 LA BASE DE DONNÉES..............................................................................................................................................................295
3.5 LES COUCHES [DAO, JPA]....................................................................................................................................................299
3.5.1 INSTALLATION ET TESTS..........................................................................................................................................................299
3.5.2 L'INTERFACE DE LA COUCHE [DAO].......................................................................................................................................300
3.5.3 VERSIONS COURTES ET LONGUES DES ENTITÉS............................................................................................................................307
3.6 LA COUCHE [WEB / JSF].........................................................................................................................................................308
3.6.1 CONFIGURATION DE L'APPLICATION [WEB / JSF]........................................................................................................................309
3.6.2 L'IMPLÉMENTATION DE DÉMARRAGE..........................................................................................................................................311
3.6.3 LE BEAN [SESSIONMODEL]....................................................................................................................................................316
3.7 IMPLÉMENTATION DE L'APPLICATION.........................................................................................................................................317
3.7.1 ÉTAPE 1...............................................................................................................................................................................318
3.7.2 ÉTAPE 2...............................................................................................................................................................................321
3.7.3 ÉTAPE 3...............................................................................................................................................................................323
3.7.4 ÉTAPE 4...............................................................................................................................................................................323
3.7.5 ÉTAPE 5...............................................................................................................................................................................324
3.7.6 ÉTAPE 6...............................................................................................................................................................................324
3.7.7 ÉTAPE 7...............................................................................................................................................................................326
3.7.8 ÉTAPE 8...............................................................................................................................................................................327
3.7.9 ÉTAPE 9...............................................................................................................................................................................328
3.7.10 ÉTAPE 10...........................................................................................................................................................................329
3.7.11 ÉTAPE 11...........................................................................................................................................................................330
3.7.12 ÉTAPE 12...........................................................................................................................................................................330
4 ÉTUDE DE CAS N° 4 : CLIENT - SERVEUR WEB / JSON...........................................................................................332
4.1 OBJECTIFS..............................................................................................................................................................................332
4.2 LE PROBLÈME.........................................................................................................................................................................332
4.3 INTRODUCTION À L'IDE INTELLIJ IDEA COMMUNITY EDITION................................................................................................333
4.4 MANIPULER DU JSON............................................................................................................................................................337
4.5 LE SERVEUR WEB/JSON..........................................................................................................................................................339
4.5.1 LE PROJET INTELLIJ IDEA DU SERVEUR...................................................................................................................................340
4.5.2 INTRODUCTION À SPRING DATA...............................................................................................................................................341
4.5.3 INTRODUCTION À SPRING MVC..............................................................................................................................................342
4.5.4 LE FICHIER [[Link]]...........................................................................................................................................................342
4.5.5 LES ENTITÉS JPA..................................................................................................................................................................344
4.5.6 LA COUCHE [DAO]..............................................................................................................................................................344
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
7/489
4.5.7 CONFIGURATION DE LA COUCHE DE PERSISTANCE........................................................................................................................345
4.5.8 TESTS DE LA COUCHE [DAO]................................................................................................................................................347
4.5.9 LA COUCHE [MÉTIER].............................................................................................................................................................348
4.5.10 TESTS JUNIT DE LA COUCHE [MÉTIER]...................................................................................................................................349
4.5.11 TESTS CONSOLE DE LA COUCHE [MÉTIER]................................................................................................................................351
4.5.12 LA COUCHE [WEB/JSON]....................................................................................................................................................352
[Link] L'architecture d'un service web/jSON implémenté par Spring MVC..........................................................................352
[Link] Les URL du service web/jSON....................................................................................................................................353
[Link] Les réponses jSON du service web/jSON...................................................................................................................354
[Link] L'interface avec la couche [métier]..............................................................................................................................355
[Link] Le contrôleur................................................................................................................................................................356
4.5.13 CONFIGURATION DE L'APPLICATION WEB..................................................................................................................................358
4.5.14 LA CLASSE EXÉCUTABLE DE L'APPLICATION WEB.......................................................................................................................360
4.5.15 CRÉATION D'UNE ARCHIVE EXÉCUTABLE..................................................................................................................................361
4.6 LE CLIENT..............................................................................................................................................................................363
4.6.1 DÉPENDANCES MAVEN...........................................................................................................................................................363
4.6.2 LES ENTITÉS MANIPULÉES PAR LE CLIENT...................................................................................................................................364
4.6.3 CONFIGURATION DE LA COUCHE [DAO]...................................................................................................................................364
4.6.4 LA COUCHE [DAO]..............................................................................................................................................................365
4.6.5 LES TESTS DE LA COUCHE [DAO]...........................................................................................................................................369
4.6.6 LA COUCHE [CONSOLE]..........................................................................................................................................................370
5 ÉTUDE DE CAS N° 5 : COUCHES BASSES D'UNE APPLICATION E-COMMERCE.............................................371
5.1 LES COMPÉTENCES MISES EN OEUVRE........................................................................................................................................371
5.2 LE PROBLÈME.........................................................................................................................................................................371
5.3 LA BASE DE DONNÉES..............................................................................................................................................................371
5.4 MISE EN PLACE DE L'ENVIRONNEMENT DE TRAVAIL.....................................................................................................................374
5.5 L'APPLICATION.......................................................................................................................................................................375
5.6 LE SERVEUR...........................................................................................................................................................................375
5.6.1 LE PROJET NETBEANS............................................................................................................................................................376
5.6.2 CONFIGURATION MAVEN........................................................................................................................................................378
5.6.3 CONFIGURATION JPA / JDBC................................................................................................................................................379
5.6.4 LES ENTITÉS JPA..................................................................................................................................................................382
5.6.5 TESTS DES ENTITÉS JPA.........................................................................................................................................................384
5.6.6 LES [REPOSITORIES]...............................................................................................................................................................386
5.6.7 LA COUCHE [MÉTIER].............................................................................................................................................................390
5.6.8 TESTS DE LA COUCHE [MÉTIER]...............................................................................................................................................391
5.6.9 LA COUCHE WEB / JSON.......................................................................................................................................................392
[Link] Configuration.................................................................................................................................................................392
[Link] Contexte d'exécution......................................................................................................................................................394
[Link] Le contrôleur..................................................................................................................................................................394
[Link] URL [/getCategories].....................................................................................................................................................399
[Link] URL [getProductsByCategory]......................................................................................................................................400
[Link] URL [getOrderDetails]..................................................................................................................................................401
5.6.10 TEST DU SERVEUR WEB /JSON..............................................................................................................................................402
5.7 CONCLUSION...........................................................................................................................................................................403
6 ÉTUDE DE CAS N° 6 : GESTION DE BILANS DE FORMATION...............................................................................405
6.1 LES COMPÉTENCES MISES EN OEUVRE........................................................................................................................................405
6.2 LE PROBLÈME.........................................................................................................................................................................405
6.3 PRÉSENTATION DES COUCHES BASSES.........................................................................................................................................406
6.3.1 LA BASE DE DONNÉES............................................................................................................................................................406
6.3.2 LES ENTITÉS JPA..................................................................................................................................................................408
6.3.3 LA COUCHE [MÉTIER].............................................................................................................................................................413
[Link] Les objets de la couche [métier]....................................................................................................................................413
[Link] La couche [métier].........................................................................................................................................................415
6.3.4 MISE EN PLACE DE L'ENVIRONNEMENT DE TRAVAIL.....................................................................................................................416
[Link] Installation de la base de données..................................................................................................................................416
[Link] Installation de l'archive des couches basses dans le dépôt Maven................................................................................416
[Link] Le test JUnit de la couche [métier]................................................................................................................................416
6.4 ECRITURE DE LA COUCHE WEB.................................................................................................................................................418
6.4.1 LE PROJET NETBEANS............................................................................................................................................................419
6.4.2 LES VUES DE L'APPLICATION WEB.............................................................................................................................................420
6.4.3 CONFIGURATION DU PROJET.....................................................................................................................................................423
[Link] [[Link]]......................................................................................................................................................................423
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
8/489
[Link] [JsfConfig].....................................................................................................................................................................424
[Link] [[Link]]..........................................................................................................................................................425
[Link] [[Link]].......................................................................................................................................................................426
[Link] [[Link]].....................................................................................................................................................426
6.4.4 EXÉCUTION DU PROJET...........................................................................................................................................................428
6.4.5 LE BEAN [APPLICATIONDATA].................................................................................................................................................430
6.4.6 LE BEAN [SESSIONDATA].......................................................................................................................................................432
6.4.7 LE BEAN [FORM]..................................................................................................................................................................433
6.4.8 LE MODÈLE DES VUES............................................................................................................................................................434
6.4.9 LA PAGE D'ACCUEIL [[Link]].........................................................................................................................................435
6.4.10 LA PAGE DES PRODUITS VENDUS [[Link]]................................................................................................................437
6.4.11 LA PAGE DU PANIER [[Link]]........................................................................................................................................440
6.4.12 LA PAGE DE PAIEMENT [[Link]].............................................................................................................................443
6.4.13 LA PAGE DE CONFIRMATION [[Link]].................................................................................................................445
6.4.14 LES DERNIERS LIENS............................................................................................................................................................447
6.4.15 GESTION DES REDIRECTIONS..................................................................................................................................................447
7 ÉTUDE DE CAS N° 7...........................................................................................................................................................449
7.1 LES COMPÉTENCES MISES EN OEUVRE........................................................................................................................................449
7.2 LE PROBLÈME.........................................................................................................................................................................449
7.3 L'ARCHITECTURE DE L'APPLICATION WEB..................................................................................................................................450
7.4 LES PROJETS NETBEANS DE L'APPLICATION...............................................................................................................................451
7.5 LA BASE DE DONNÉES..............................................................................................................................................................451
7.6 L'ARCHIVE DES COUCHES [METIER, DAO, JPA]............................................................................................................................453
7.7 IMPLÉMENTATION DE LA COUCHE [WEB]....................................................................................................................................460
7.7.1 LE PROJET NETBEANS............................................................................................................................................................461
7.7.2 CONFIGURATION DU PROJET WEB..............................................................................................................................................461
[Link] Configuration Maven.....................................................................................................................................................461
[Link] Configuration web.........................................................................................................................................................462
[Link] Configuration JSF..........................................................................................................................................................462
[Link] Configuration Spring.....................................................................................................................................................463
[Link] Configuration de l'exécution du projet Netbeans..........................................................................................................464
7.8 LA VUE [[Link]]........................................................................................................................................................465
7.9 LA VUE [[Link]]........................................................................................................................................................466
7.10 LA VUE [[Link]]......................................................................................................................................................467
8 ÉTUDE DE CAS N° 8 : GESTION D'ÉLECTIONS.........................................................................................................469
8.1 LES COMPÉTENCES MISES EN OEUVRE........................................................................................................................................469
8.2 LE PROBLÈME.........................................................................................................................................................................469
8.3 L'ARCHITECTURE DE L'APPLICATION.........................................................................................................................................470
8.4 LA BASE DE DONNÉES..............................................................................................................................................................471
8.5 LES COUCHES [MÉTIER, DAO, JPA].......................................................................................................................................472
8.5.1 INSTALLATION ET TESTS..........................................................................................................................................................472
8.5.2 L'INTERFACE DE LA COUCHE [MÉTIER]......................................................................................................................................472
8.6 LA COUCHE [WEB / JSF].........................................................................................................................................................476
8.6.1 L'IMPLÉMENTATION DE DÉMARRAGE..........................................................................................................................................476
8.6.2 ÉTAPE 1...............................................................................................................................................................................479
8.6.3 ÉTAPE 2...............................................................................................................................................................................479
8.6.4 ÉTAPE 3...............................................................................................................................................................................479
8.6.5 ÉTAPE 4...............................................................................................................................................................................480
8.6.6 ÉTAPE 5...............................................................................................................................................................................481
8.6.7 ÉTAPE 6...............................................................................................................................................................................482
8.6.8 ÉTAPE 7...............................................................................................................................................................................482
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
9/489
1 Le cours et l'étude de cas
• la couche [1], appelée ici [ui] (User Interface) est la couche qui dialogue avec l'utilisateur, via une interface graphique
Swing, une interface console ou une interface web. Elle a pour rôle de fournir des données provenant de l'utilisateur à la
couche [2] ou bien de présenter à l'utilisateur des données fournies par la couche [2] ;
• la couche [2], appelée ici [metier] est la couche qui applique les règles dites métier, c.a.d. la logique spécifique de
l'application, sans se préoccuper de savoir d'où viennent les données qu'on lui donne, ni où vont les résultats qu'elle
produit ;
• la couche [3], appelée ici [DAO] (Data Access Object) est la couche qui fournit à la couche [2] des données pré-
enregistrées (fichiers, bases de données, services web, ...) et qui enregistre certains des résultats fournis par la couche [2] ;
Il existe différentes possibilités pour implémenter la couche [DAO] en relation avec une base de données. Examinons-en quelques-
unes :
La couche [JDBC] ci-dessus est la couche standard utilisée en Java pour accéder à des bases de données. Elle isole la couche [DAO]
du SGBD qui gère la base de données. On peut théoriquement changer de SGBD sans changer le code de la couche [DAO].
Malgré cet avantage, l'API JDBC présente certains inconvénients :
• toutes les opérations sur le SGBD sont susceptibles de lancer l'exception contrôlée (checked) SQLException. Ceci oblige
le code appelant (la couche [DAO] ici) à les entourer par des try / catch rendant ainsi le code assez lourd.
• la couche [DAO] n'est pas complètement insensible au SGBD. Ceux-ci ont par exemple des méthodes propriétaires quant
à la génération automatique de valeurs de clés primaires que la couche [DAO] ne peut ignorer. Ainsi lors de l'insertion
d'un enregistrement :
• avec Oracle, la couche [DAO] doit d'abord obtenir une valeur pour la clé primaire de l'enregistrement puis
insérer celui-ci ;
• avec SQL Server, la couche [DAO] insère l'enregistrement qui se voit donner automatiquement une valeur de clé
primaire par le SGBD, valeur rendue à la couche [DAO] ;
Ces différences peuvent être gommées via l'utilisation de procédures stockées. Dans l'exemple précédent, la couche
[DAO] appellera une procédure stockée dans Oracle ou SQL Server qui prendra en compte les particularités du SGBD.
Celles-ci seront cachées à la couche [DAO]. Néanmoins, si changer de SGBD n'impliquera pas de réécrire la couche
[DAO], cela implique quand même de réécrire les procédures stockées. Cela peut ne pas être considéré comme
rédhibitoire.
De multiples efforts ont été faits pour isoler la couche [DAO] des aspects propriétaires des SGBD. Une solution ayant eu un vrai
succès dans ce domaine ces dernières années, est celle d'Hibernate :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
10/489
4 Couche
Couche d'accès aux Objets image Couche Base de
données [DAO] de la BD [Hibernate] [JDBC] Données
3 5 6 7
La couche [Hibernate] vient se placer entre la couche [DAO] écrite par le développeur et la couche [JDBC]. Hibernate est un ORM
(Object Relational Mapper), un outil qui fait le pont entre le monde relationnel des bases de données et celui des objets manipulés
par Java. Le développeur de la couche [DAO] ne voit plus la couche [JDBC] ni les tables de la base de données dont il veut
exploiter le contenu. Il ne voit que l'image objet de la base de données, image objet fournie par la couche [Hibernate]. Le pont entre
les tables de la base de données et les objets manipulés par la couche [DAO] est fait principalement de deux façons :
• par des fichiers de configuration de type XML ;
• par des annotations Java dans le code, technique disponible seulement depuis le JDK 1.5 ;
La couche [Hibernate] est une couche d'abstraction qui se veut la plus transparente possible. L'idéal visé est que le développeur de
la couche [DAO] puisse ignorer totalement qu'il travaille avec une base de données. C'est envisageable si ce n'est pas lui qui écrit la
configuration qui fait le pont entre le monde relationnel et le monde objet. La configuration de ce pont est assez délicate et
nécessite une certaine habitude.
La couche [4] des objets, image de la BD est appelée "contexte de persistance". Une couche [DAO] s'appuyant sur Hibernate fait
des actions de persistance (CRUD, create - read - update - delete) sur les objets du contexte de persistance, actions traduites par
Hibernate en ordres SQL exécutés par la couche JDBC. Pour les actions d'interrogation de la base (le SQL Select), Hibernate
fournit au développeur, un langage HQL (Hibernate Query Language) pour interroger le contexte de persistance [4] et non la BD
elle-même.
Hibernate est populaire mais complexe à maîtriser. La courbe d'apprentissage souvent présentée comme facile est en fait assez
raide. Dès qu'on a une base de données avec des tables ayant des relations un-à-plusieurs ou plusieurs-à-plusieurs, la configuration
du pont relationnel / objets n'est pas à la portée du premier débutant venu. Des erreurs de configuration peuvent conduire à des
applications peu performantes.
Devant le succès des produits ORM, Sun le créateur de Java, a décidé de standardiser une couche ORM via une spécification
appelée JPA (Java Persistence API) apparue en même temps que Java 5. La spécification JPA a été implémentée par divers
produits : Hibernate, Toplink, OpenJpa, .... Avec JPA, l'architecture précédente devient la suivante :
4
Couche d'accès aux Objets image Interface Implémentation Couche Base de
données [DAO] de la BD [JPA] JPA [JDBC] Données
3 5 [Hibernate / ...] 6 7
La couche [DAO] dialogue maintenant avec la spécification JPA, un ensemble d'interfaces. Le développeur y a gagné en
standardisation. Avant, s'il changeait sa couche ORM, il devait également changer sa couche [DAO] qui avait été écrite pour
dialoguer avec un ORM spécifique. Maintenant, il va écrire une couche [DAO] qui va dialoguer avec une couche JPA. Quelque soit
le produit qui implémente celle-ci, l'interface de la couche JPA présentée à la couche [DAO] reste la même.
Dans ce document, nous utiliserons une couche [DAO] s'appuyant sur une couche JPA/Hibernate ou JPA/EclipseLink. Par ailleurs
nous utiliserons le framework Spring pour lier ces couches entre-elles.
4
Couche Couche Objets image Interface Implémentation Couche
[metier] [DAO] de la BD [JPA] JPA [EclipseLink [JDBC]
2 / Hibernate]
3 6
5
7 Spring
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
11/489
Le grand intérêt de Spring est qu'il permet de lier les couches par configuration et non dans le code. Ainsi si l'implémentation JPA /
Hibernate doit être remplacée par une implémentation Hibernate sans JPA, parce que par exemple l'application s'exécute dans un
environnement JDK 1.4 qui ne supporte pas JPA, ce changement d'implémentation de la couche [DAO] n'a pas d'impact sur le
code de la couche [métier]. Seul le fichier de configuration Spring qui lie les couches entre elles doit être modifié.
Avec Java EE 5, une autre solution existe : implémenter les couches [metier] et [DAO] avec des EJB3 (Enterprise Java Bean version
3) :
4
Couche Couche Objets image Interface Implémentation Couche
[metier] [DAO] de la BD [JPA] JPA [EclipseLink [JDBC]
2 / Hibernate]
3 6
5
7 conteneur Ejb3
Nous verrons que cette solution n'est pas très différente de celle utilisant Spring. L'environnement Java EE5 est disponible au sein
de serveurs dits serveurs d'applications tels que Sun Application Server 9.x (Glassfish), Jboss Application Server, Oracle Container for Java
(OC4J), ... Un serveur d'applications est essentiellement un serveur d'applications web. Il existe également des environnements EE 5
dits "stand-alone", c.a.d. pouvant être utilisés en-dehors d'un serveur d'applications. C'est le cas de JBoss EJB3 ou OpenEJB.
Dans un environnement EE5, les couches sont implémentées par des objets appelés EJB (Enterprise Java Bean). Dans les
précédentes versions d'EE, les EJB (EJB 2.x) étaient réputés difficiles à mettre en œuvre et à tester et considérés comme peu
performants. On distingue les EJB2.x "entity" et les EJB2.x "session". Pour faire court, un EJB2.x "entity" est l'image d'une ligne de
table de base de données et EJB2.x "session" un objet utilisé pour implémenter les couches [metier], [DAO] d'une architecture
multicouche. L'un des principaux reproches faits aux couches implémentées avec des EJB est qu'elles ne sont utilisables qu'au sein
de conteneurs EJB, un service délivré par l'environnement EE. Cet environnement, plus complexe à mettre en oeuvre qu'un
environnement SE (Standard Edition), peut décourager le développeur à faire fréquemment des tests. Néanmoins, il existe des
environnements de développement Java qui facilitent l'utilisation d'un serveur d'applications en automatisant le déploiement des
EJB sur le serveur : Eclipse, Netbeans, JDeveloper, IntelliJ IDEA. Nous utiliserons ici Netbeans 8.2 et le serveur d'application Glassfish 4.1.
Le framework Spring est né en réaction à la complexité des EJB2. Spring fournit dans un environnement SE un nombre important
des services habituellement fournis par les environnements EE. Ainsi dans la partie "Persistance de données", Spring fournit les
pools de connexion et les gestionnaires de transactions dont ont besoin les applications. L'émergence de Spring a favorisé la culture
des tests unitaires, devenus plus faciles à mettre en oeuvre dans le contexte SE que dans le contexte EE. Spring permet
l'implémentation des couches d'une application par des objets Java classiques (POJO, Plain Old/Ordinary Java Object), permettant
la réutilisation de ceux-ci dans un autre contexte. Enfin, il intègre de nombreux outils tiers de façon assez transparente, notamment
des outils de persistance tels que Hibernate, EclipseLink, Ibatis, ...
Java EE5 a été conçu pour corriger les lacunes de la spécification EJB2. Les EJB 2.x sont devenus les EJB3. Ceux-ci sont des
POJOs tagués par des annotations qui en font des objets particuliers lorsqu'ils sont au sein d'un conteneur EJB3. Dans celui-ci,
l'EJB3 va pouvoir bénéficier des services du conteneur (pool de connexions, gestionnaire de transactions, ...). En-dehors du
conteneur EJB3, l'EJB3 devient un objet Java normal. Ses annotations EJB sont ignorées.
Ci-dessus, nous avons représenté Spring et un conteneur EJB3 comme infrastructure (framework) possible de notre architecture
multicouche. C'est cette infrastructure qui délivrera les services dont nous avons besoin : un pool de connexions et un gestionnaire
de transactions.
• avec Spring, les couches seront implémentées avec des POJOs. Ceux-ci auront accès aux services de Spring (pool
de connexions, gestionnaire de transaction) par injection de dépendances dans ces POJOs : lors de la
construction de ceux-ci, Spring leur injecte des références sur les services dont il vont avoir besoin ;
• avec le conteneur EJB3, les couches seront implémentées avec des EJB. Une architecture en couches
implémentées avec des EJB3 est peu différente de celles implémentées avec des POJO instanciés par Spring.
Nous trouverons beaucoup de ressemblances ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
12/489
4
Couche Couche Couche Objets image Interface Implémentation Couche
[web] [metier] [DAO] de la BD [JPA] JPA [EclipseLink [JDBC]
1 2 / Hibernate]
3 6
5
7 Spring ou Ejb3
• Netbeans 8.1. L'installation de Netbeans est décrite dans [ref3] au paragraphe 1.3.1 ;
• Wampserver version 2.5. L'installation de WampServer est décrite dans [ref3] au paragraphe 1.3.3 ;
• Maven est intégré à Netbeans. Nous décrivons maintenant cet outil.
1.2.1 Maven
[Link] Introduction
Maven est disponible à l'URL [[Link] ]. Selon ses créateurs :
Maven's primary goal is to allow a developer to comprehend the complete state of a development effort in the shortest period of time. In order to attain this
goal there are several areas of concern that Maven attempts to deal with:
• Making the build process easy
• Providing a uniform build system
• Providing quality project information
• Providing guidelines for best practices development
• Allowing transparent migration to new features
Maven est intégré dans Netbeans et nous allons l'utiliser pour une seule de ses caractéristiques : la gestion des bibliothèques d'un
projet. Celles-ci sont formées de l'ensemble des archives jars qui doivent être dans le Classpath du projet. Elles peuvent être très
nombreuses. Par exemple, nos projets futurs vont utiliser l'ORM (Object Relational Mapper) Hibernate. Cet ORM est composé de
dizaines d'archives jar. L'intérêt de Maven est qu'il nous affranchit de les connaître toutes. Il nous suffit d'indiquer dans notre projet
que nous avons besoin d'Hibernate, en donnant toutes les informations utiles pour trouver l'archive principale de cet ORM. Maven
télécharge alors également toutes les bibliothèques nécessaires à Hibernate. On appelle cela les dépendances d'Hibernate. Une
bibliothèque nécessaire à Hibernate peut elle même dépendre d'autres archives. Celles-ci seront également téléchargées. Toutes ces
bibliothèques sont placées dans un dossier appelé le dépôt local de Maven.
Un projet Maven est facilement partageable. Si on le transfère d'un poste à un autre et que les dépendances du projet ne sont pas
présentes dans le dépôt local du nouveau poste, elles seront téléchargées.
Maven peut être utilisé seul ou intégré à un EDI (Environnement de Développement Intégré) tel Netbeans ou Eclipse.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
13/489
• en [1], créer un nouveau projet,
• en [2-3], choisir la catégorie [Maven] et le type de projet [Java Application],
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
14/489
Examinons les éléments du projet et explicitons le rôle de chacun :
Le fichier [[Link]] configure le projet Maven. Son contenu est pour l'instant le suivant :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <project xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
3. <modelVersion>4.0.0</modelVersion>
4. <groupId>[Link]</groupId>
5. <artifactId>mv-exemple</artifactId>
6. <version>1.0-SNAPSHOT</version>
7. <packaging>jar</packaging>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
15/489
8. <dependencies>
9. <dependency>
10. <groupId>junit</groupId>
11. <artifactId>junit</artifactId>
12. <version>4.12</version>
13. <scope>test</scope>
14. </dependency>
15. <dependency>
16. <groupId>[Link]</groupId>
17. <artifactId>hamcrest-core</artifactId>
18. <version>1.3</version>
19. <scope>test</scope>
20. </dependency>
21. </dependencies>
22. <properties>
23. <[Link]>UTF-8</[Link]>
24. <[Link]>1.8</[Link]>
25. <[Link]>1.8</[Link]>
26. </properties>
27. </project>
• les lignes 4-7 définissent l'objet (artifact) Java qui va être créé par le projet Maven, ici une archive jar. Ces informations
proviennent de l'assistant qui a été utilisé lors de la création du projet :
• ligne 4 : [groupId] : une information qui ressemble à un nom de package. Ainsi les bibliothèques du framework Spring ont
groupId=[Link], celles du framework JSF ont groupId=[Link],
• ligne 5 : [artifactId] : le nom de l'objet Maven. Dans le groupe [[Link]] on trouve ainsi les artifactId
suivants : spring-context, spring-core, spring-beans, ... Dans le groupe [[Link]], on trouve l'artifactId JSF-api,
• ligne 6 : [version] : n° de version de l'artifact Maven. Ainsi l'artifact [Link]-core a les versions suivantes :
2.5.4, 2.5.5, 2.5.6, 2.5.6.SECO1, ...
• ligne 7 : [packaging] : la forme prise par l'artifact, le plus souvent war ou jar.
Notre projet Maven va générer un [jar] (ligne 7) dans le groupe [[Link]] (ligne 4), nommé [mv-exemple] (ligne 5) et de version [1.0-
SNAPSHOT] (ligne 6). Ces quatre informations définissent de façon unique un artifact Maven.
Les lignes 8-21 listent les dépendances du projet Maven, c'est à dire la liste des bibliothèques nécessaires au projet. Chaque
bibliothèque est définie par les quatre informations (groupId, artifactId, version, packaging). Lorsque l'information packaging est
absente, le packaging jar est utilisé. On peut y ajouter une autre information, scope qui fixe à quels moments de la vie du projet on a
besoin de la bibliothèque. La valeur par défaut est compile qui indique que la bibliothèque est nécessaire à la compilation et à
l'exécution. La valeur test signifie que la bibliothèque n'est nécessaire que pour exécuter les tests du projet. C'est le cas ici avec les
bibliothèques JUnit (ligne 13) et Hamcrest (ligne 19). Si ces dépendances ne sont pas présentes dans le dépôt local du poste (notion
définie plus loin), elles seront téléchargées.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
16/489
Modifions la classe [Main] générée de la façon suivante :
1. package [Link];
2.
3. public class Main {
4.
5. public static void main(String[] args) {
6. [Link]("Hello world!");
7. }
8.
9. }
En [1], le projet Maven est construit puis exécuté [2]. Les logs dans la console Netbeans sont les suivants :
1. cd U:\Temp\16-08-23\mv-exemple; "JAVA_HOME=C:\\Program Files\\Java\\jdk1.8.0_77" "M2_HOME=C:\\Program Files\\apache-maven-
3.3.9" cmd /c "\"\"C:\\Program Files\\apache-maven-3.3.9\\bin\\[Link]\" -[Link]=\"-classpath %classpath
[Link]\" -[Link]=\"C:\\Program Files\\Java\\jdk1.8.0_77\\bin\\[Link]\" -
[Link]=runtime -[Link]=\"C:\\Program Files\\NetBeans 8.1\\java\\maven-nblib\\netbeans-
[Link]\" -[Link]=UTF-8 process-classes [Link]:exec-maven-plugin:1.2.1:exec\""
2. Scanning for projects...
3.
4. ------------------------------------------------------------------------
5. Building mv-exemple 1.0-SNAPSHOT
6. ------------------------------------------------------------------------
7.
8. --- maven-resources-plugin:2.6:resources (default-resources) @ mv-exemple ---
9. Using 'UTF-8' encoding to copy filtered resources.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
17/489
10. skip non existing resourceDirectory U:\Temp\16-08-23\mv-exemple\src\main\resources
11.
12. --- maven-compiler-plugin:3.1:compile (default-compile) @ mv-exemple ---
13. Changes detected - recompiling the module!
14. Compiling 1 source file to U:\Temp\16-08-23\mv-exemple\target\classes
15.
16. --- exec-maven-plugin:1.2.1:exec (default-cli) @ mv-exemple ---
17. Hello world!
18. ------------------------------------------------------------------------
19. BUILD SUCCESS
20. ------------------------------------------------------------------------
21. Total time: 2.744 s
22. Finished at: 2016-08-23T[Link]+02:00
23. Final Memory: 13M/194M
24. ------------------------------------------------------------------------
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
18/489
• en [3], on voit l'artifact Maven [[Link], mv-exemple, [Link]] créé par notre projet lors du [Clean and Build] ;
Les dépendances supprimées n'apparaissent plus dans [[Link]]. Maintenant, recherchons-les dans les dépôts Maven.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
19/489
• en [1-2], on ajoute une dépendance au projet ;
• en [3], on doit préciser des informations sur l'artifact cherché (groupId, artifactId, version, packaging (Type) et scope).
Nous commençons par préciser le [groupId] [3] ;
• en [4], nous tapons [espace] pour faire afficher la liste des artifacts possibles. Ici [junit] et [junit-dep]. Nous choisissons
[junit] ;
• en [5], en procédant de la même façon, on choisit la version la plus récente ;
• en [6], le type de packaging est jar,
• en [7], nous choisissons la portée test pour indiquer que la dépendance n'est nécessaire qu'aux tests.
En [8], les dépendances ajoutées apparaissent dans le projet. Le fichier [[Link]] reflète ces changements :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <project xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
3. <modelVersion>4.0.0</modelVersion>
4. <groupId>[Link]</groupId>
5. <artifactId>mv-exemple</artifactId>
6. <version>1.0-SNAPSHOT</version>
7. <packaging>jar</packaging>
8. <dependencies>
9. <dependency>
10. <groupId>junit</groupId>
11. <artifactId>junit</artifactId>
12. <version>4.12</version>
13. <type>jar</type>
14. <scope>test</scope>
15. </dependency>
16. <properties>
17. <[Link]>UTF-8</[Link]>
18. <[Link]>1.8</[Link]>
19. <[Link]>1.8</[Link]>
20. </properties>
21. </project>
On notera que fichier [[Link]] ne mentionne pas la dépendance [hamcrest-core-1.3] que nous voyons en [8]. Cela parce que c'est
une dépendance de JUnit 4.12 et non du projet lui-même. Cela est signalé par une icône différente dans la branche [Dependencies].
Elle a été téléchargée automatiquement.
Supposons maintenant qu'on ne connaisse pas le [groupId] de l'artifact que l'on désire. Par exemple, on veut utiliser Hibernate
comme ORM (Object Relational Mapper) et c'est tout ce qu'on sait. On peut aller alors sur le site [[Link] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
20/489
En [2], on peut taper des mots clés. Tapons hibernate et lançons la recherche [3-4] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
21/489
• en [5-6], nous obtenons le code Maven à coller dans le fichier [[Link]]. Nous le faisons :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <project xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
3. <modelVersion>4.0.0</modelVersion>
4. <groupId>[Link]</groupId>
5. <artifactId>mv-exemple</artifactId>
6. <version>1.0-SNAPSHOT</version>
7. <packaging>jar</packaging>
8. <dependencies>
9. <dependency>
10. <groupId>junit</groupId>
11. <artifactId>junit</artifactId>
12. <version>4.12</version>
13. <type>jar</type>
14. </dependency>
15. <!-- [Link] -->
16. <dependency>
17. <groupId>[Link]</groupId>
18. <artifactId>hibernate-core</artifactId>
19. <version>[Link]</version>
20. </dependency>
21. </dependencies>
22. <properties>
23. <[Link]>UTF-8</[Link]>
24. <[Link]>1.8</[Link]>
25. <[Link]>1.8</[Link]>
26. </properties>
27. </project>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
22/489
• en [7], la nouvelle dépendance apparaît. Une icône spéciale indique qu'elle n'est pas encore téléchargée ;
• pour télécharger la nouvelle dépendance, on fait un [Clean and Build] [8] du projet ;
Maven entreprend alors le téléchargement des nouvelles dépendances. Le projet évolue comme suit :
• en [9], la dépendance [hibernate-core-5.2.2-Final]. Dans le dépôt où il a été trouvé, cet [artifactId] est lui aussi décrit par un
fichier [[Link]]. Ce fichier a été lu et Maven a découvert que l'[artifactId] avait des dépendances. Il les télécharge
également. Il fera cela pour chaque [artifactId] téléchargé. Au final, on trouve en [10] des dépendances qu'on n'avait pas
demandées directement. Elles sont signalées par une icône différente de celle de l'[artifactId] principal ;
Dans ce document, nous utilisons Maven principalement pour cette caractéristique. Cela nous évite de connaître toutes les
dépendances d'une bibliothèque que l'on veut utiliser. On laisse Maven les gérer. Par ailleurs, en partageant un fichier [[Link]]
entre développeurs, on est assuré que chaque développeur utilise bien les mêmes bibliothèques.
Dans les exemples qui suivront, nous donnerons toujours le fichier [[Link]] utilisé. Le lecteur n'aura qu'à l'utiliser pour se trouver
dans les mêmes conditions que le document. Par ailleurs les projets Maven sont reconnus par les principaux IDE Java (Eclipse,
Netbeans, IntelliJ, JDeveloper). Aussi le lecteur pourra-t-il utiliser son IDE favori pour tester les exemples.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
23/489
1.3 JPA en résumé
Nous nous proposons d'introduire JPA (Java Persistence API) avec quelques exemples. JPA est développé dans le cours :
• Persistance Java 5 par la pratique : [[Link] - donne les outils pour construire la couche
d'accès aux données avec JPA
Nous présentons tout d'abord les fondements de JPA. On attendra le paragraphe 1.3.4, page 35 pour créer une application
exemple. D'ici là, il n'y a que de la lecture à faire.
La couche [DAO] dialogue avec la spécification JPA. Quelque soit le produit qui implémente celle-ci, l'interface de la couche JPA
présentée à la couche [DAO] reste la même. Nous présentons dans la suite quelques exemples tirés de [ ref1] qui nous permettront
de construire notre propre couche JPA.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
24/489
MARIE entier 0 (non marié) ou 1 (marié)
NBENFANTS nombre d'enfants de la personne
4
Programme de test Objets image Interface Implémentation Couche Base de
console [main] de la BD [JPA] 6 [Eclipselink [JDBC] Données
3 5 / Hibernate]
7
La couche JPA [5] doit faire un pont entre le monde relationnel de la base de données [7] et le monde objet [4] manipulé par les
programmes Java [3]. Ce pont est fait par configuration et il y a deux façons de le faire :
1. avec des fichiers XML. C'était quasiment l'unique façon de faire jusqu'à l'avènement du JDK 1.5 ;
2. avec des annotations Java depuis le JDK 1.5 ;
L'objet [Personne] image de la table [personne] présentée précédemment pourrait être le suivant :
1. ...
2.
3. @SuppressWarnings("unused")
4. @Entity
5. @Table(name="Personne")
6. public class Personne implements Serializable{
7.
8. @Id
9. @Column(name = "ID", nullable = false)
10. @GeneratedValue(strategy = [Link])
11. private Integer id;
12.
13. @Column(name = "VERSION", nullable = false)
14. @Version
15. private int version;
16.
17. @Column(name = "NOM", length = 30, nullable = false, unique = true)
18. private String nom;
19.
20. @Column(name = "PRENOM", length = 30, nullable = false)
21. private String prenom;
22.
23. @Column(name = "DATENAISSANCE", nullable = false)
24. @Temporal([Link])
25. private Date datenaissance;
26.
27. @Column(name = "MARIE", nullable = false)
28. private boolean marie;
29.
30. @Column(name = "NBENFANTS", nullable = false)
31. private int nbenfants;
32.
33. // constructeurs
34. public Personne() {
35. }
36.
37. public Personne(String nom, String prenom, Date datenaissance, boolean marie,
38. int nbenfants) {
39. setNom(nom);
40. setPrenom(prenom);
41. setDatenaissance(datenaissance);
42. setMarie(marie);
43. setNbenfants(nbenfants);
44. }
45.
46. // toString
47. public String toString() {
48. ...
49. }
50.
51. // getters and setters
52. ...
53. }
La configuration se fait à l'aide d'annotations Java @Annotation. Les annotations Java sont soit exploitées par le compilateur, soit
par des outils spécialisés, au moment de l'exécution. En-dehors de l'annotation de la ligne 3 destinée au compilateur, toutes les
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
25/489
annotations sont ici destinées à l'implémentation JPA utilisée, Hibernate ou EclipseLink. Elles seront donc exploitées à l'exécution.
En l'absence des outils capables de les interpréter, ces annotations sont ignorées. Ainsi la classe [Personne] ci-dessus pourrait être
exploitée dans un contexte hors JPA.
Il faut distinguer deux cas d'utilisation des annotations JPA dans une classe C associée à une table T :
1. la table T existe déjà : les annotations JPA doivent alors reproduire l'existant (nom et définition des colonnes, contraintes
d'intégrité, clés étrangères, clés primaires, ...) ;
2. la table T n'existe pas et elle peut alors être créée d'après les annotations trouvées dans la classe C ;
Le cas 2 est le plus facile à gérer. A l'aide des annotations JPA, nous indiquons la structure de la table T que nous voulons. Le cas 1
est souvent plus complexe. La table T a pu être construite, il y a longtemps, en-dehors de tout contexte JPA. Sa structure peut alors
être mal adaptée au pont relationnel / objet de JPA. Pour simplifier, nous nous plaçons dans le cas 2 où la table T associée à la
classe C va être créée d'après les annotations JPA de la classe C.
• ligne 4 : l'annotation @Entity est la première annotation indispensable. Elle se place avant la ligne qui déclare la classe et
indique que la classe en question doit être gérée par la couche de persistance JPA. En l'absence de cette annotation, toutes
les autres annotations JPA seraient ignorées ;
• ligne 5 : l'annotation @Table désigne la table de la base de données dont la classe est une représentation. Son principal
argument est name qui désigne le nom de la table. En l'absence de cet argument, la table portera le nom de la classe, ici
[Personne]. Dans notre exemple, l'annotation @Table est donc superflue ;
• ligne 8 : l'annotation @Id sert à désigner le champ dans la classe qui est image de la clé primaire de la table. Cette
annotation est obligatoire. Elle indique ici que le champ id de la ligne 11 est l'image de la clé primaire de la table ;
• ligne 9 : l'annotation @Column sert à faire le lien entre un champ de la classe et la colonne de la table dont le champ est
l'image. L'attribut name indique le nom de la colonne dans la table. En l'absence de cet attribut, la colonne porte le même
nom que le champ. Dans notre exemple, l'argument name n'était donc pas obligatoire. L'argument nullable=false
indique que la colonne associée au champ ne peut avoir la valeur NULL et que donc le champ doit avoir nécessairement
une valeur ;
• ligne 10 : l'annotation @GeneratedValue indique comment est générée la clé primaire lorsqu'elle est générée
automatiquement par le SGBD. Ce sera le cas dans tous nos exemples. Ce n'est pas obligatoire. Ainsi notre personne
pourrait avoir un n° étudiant qui servirait de clé primaire et qui ne serait pas généré par le SGBD mais fixé par
l'application. Dans ce cas, l'annotation @GeneratedValue serait absente. L'argument strategy indique comment est
générée la clé primaire lorsqu'elle est générée par le SGBD. Les SGBD n'ont pas tous la même technique de génération
des valeurs de clé primaire. Par exemple :
La couche JPA doit générer des ordres SQL différents selon les SGBD pour créer le générateur de valeurs. On lui indique
par configuration le type de SGBD qu'elle a à gérer. Du coup, elle peut savoir quelle est la stratégie habituelle de
génération de valeurs de clé primaire de ce SGBD. L'argument strategy = [Link] indique à la couche
JPA qu'elle doit utiliser cette stratégie habituelle. Cependant, si on souhaite changer d'implémentation JPA, il ne faut pas
utiliser cette stratégie mais en imposer une à la couche JPA :
• la statégie [[Link]] peut être utilisée avec les SGBD MySQL, SQL Server, IBM DB2 et
les versions récentes d'Oracle ;
• la statégie [[Link]] peut être utilisée avec les SGBD Firebird, Postgresql et les versions
anciennes d'Oracle ;
• ligne 14 : l'annotation @Version désigne le champ qui sert à gérer les accès concurrents à une même ligne de la table.
Pour comprendre ce problème d'accès concurrents à une même ligne de la table [personne], supposons qu'une
application web permette la mise à jour d'une personne et examinons le cas suivant :
Au temps T1, un utilisateur U1 entre en modification d’une personne P. A ce moment, le nombre d’enfants est 0. Il
passe ce nombre à 1 mais avant qu’il ne valide sa modification, un utilisateur U2 entre en modification de la même
personne P. Puisque U1 n’a pas encore validé sa modification, U2 voit sur son écran le nombre d’enfants à 0. U2
passe le nom de la personne P en majuscules. Puis U1 et U2 valident leurs modifications dans cet ordre. C’est la
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
26/489
modification de U2 qui va gagner : dans la base, le nom va passer en majuscules et le nombre d’enfants va rester à
zéro alors même que U1 croit l’avoir changé en 1.
La notion de version de personne nous aide à résoudre ce problème. On reprend le même cas d’usage :
Au temps T1, un utilisateur U1 entre en modification d’une personne P. A ce moment, le nombre d’enfants est 0 et
la version V1. Il passe le nombre d’enfants à 1 mais avant qu’il ne valide sa modification, un utilisateur U2 entre en
modification de la même personne P. Puisque U1 n’a pas encore validé sa modification, U2 voit le nombre d’enfants
à 0 et la version à V1. U2 passe le nom de la personne P en majuscules. Puis U1 et U2 valident leurs modifications
dans cet ordre. Avant de valider une modification, on vérifie que celui qui modifie une personne P détient la même
version que la personne P actuellement en base. Ce sera le cas de l’utilisateur U1. Sa modification est donc acceptée
et la version de la personne modifiée passe de V1 à V2 pour noter le fait que la personne a subi un changement. Lors
de la validation de la modification de U2, on va s’apercevoir que U2 détient une version V1 de la personne P, alors
qu’actuellement la version de celle-ci en base est V2. On va alors pouvoir dire à l’utilisateur U2 que quelqu’un est
passé avant lui et qu’il doit repartir de la nouvelle version de la personne P. Il le fera, récupèrera une personne P de
version V2 qui a maintenant un enfant, passera le nom en majuscules, validera. Sa modification sera acceptée si la
personne P enregistrée a toujours la version V2. Au final, les modifications faites par U1 et U2 seront prises en
compte alors que dans le cas d’usage sans version, l’une des modifications était perdue.
La couche [DAO] de l'application cliente peut gérer elle-même la version de la classe [Personne]. A chaque fois qu'il
y aura une modification d'un objet P, la version de cet objet sera incrémentée de 1 dans la table. L'annotation
@Version permet de transférer cette gestion à la couche JPA. Le champ concerné n'a nul besoin de s'appeler version
comme dans l'exemple. Il peut porter un nom quelconque.
Les champs correspondant aux annotations @Id et @Version sont des champs présents à cause de la persistance.
On n'en aurait pas besoin si la classe [Personne] n'avait pas besoin d'être persistée. On voit donc qu'un objet n'a pas
la même représentation selon qu'il a besoin ou non d'être persisté.
• ligne 17 : de nouveau l'annotation @Column pour donner des informations sur la colonne de la table [personne] associée
au champ nom de la classe Personne. On trouve ici deux nouveaux arguments :
• unique=true indique que le nom d'une personne doit être unique. Cela va se traduire dans la base de données
par l'ajout d'une contrainte d'unicité sur la colonne NOM de la table [personne] ;
• length=30 fixe à 30 le nombre de caractères de la colonne NOM. Cela signifie que le type de cette colonne sera
VARCHAR(30) ;
• ligne 24 : l'annotation @Temporal sert à indiquer quel type SQL donner à une colonne / champ de type date / heure. Le
type [Link] désigne une date seule sans heure associée. Les autres types possibles sont [Link]
pour coder une heure et [Link] pour coder une date avec heure ;
• ligne 6 : la classe implémente l'interface Serializable. La sérialisation d'un objet consiste à le transformer en une suite de bits.
La désérialisation est l'opération inverse. La sérialisation / désérialisation est notamment utilisée dans les applications client /
serveur où des objets sont échangés via le réseau. Les applications clientes ou serveur sont ignorantes de cette opération
qui est faite de façon transparente par les JVM. Pour qu'elle soit possible, il faut cependant que les classes des objets
échangés soit " taguées " avec le mot clé Serializable ;
• ligne 37 : un constructeur de la classe. On notera que les champs id et version ne font pas partie des paramètres. En effet,
ces deux champs sont gérés par la couche JPA et non par l'application ;
• lignes 51 et au-delà : les méthodes get et set de chacun des champs de la classe. Il est à noter que les annotations JPA
peuvent être placées sur les méthodes get des champs au lieu d'être placées sur les champs eux-mêmes. La place des
annotations indique le mode que doit utiliser JPA pour accéder aux champs :
• si les annotations sont mises au niveau champ, JPA accèdera directement aux champs pour les lire ou les écrire ;
• si les annotations sont mises au niveau get, JPA accèdera aux champs via les méthodes get / set pour les lire ou
les écrire ;
C'est la position de l'annotation @Id qui fixe la position des annotations JPA d'une classe. Placée au niveau champ, elle
indique un accès direct aux champs et placée au niveau get, un accès aux champs via les get et set. Les autres annotations
doivent alors être placées de la même façon que l'annotation @Id.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
27/489
Objets image Couche
Programme de test Interface Implémentation Base de
de la BD [JDBC]
console [main] [JPA] 6 [Hibernate] Données
3 5
4 7
• en [7] : la base de données qui sera générée à partir des annotations de l'entité [Personne] ainsi que de configurations
complémentaires faites dans un fichier appelé [[Link]]
• en [5, 6] : une couche JPA implémentée par Hibernate
• en [4] : l'entité [Personne]
• en [3] : un programme de test de type console
A l'exécution, le fichier [META-INF/[Link]] est cherché dans le Classpath de l'application. Il doit donc s'y trouver.
Examinons la configuration de la couche JPA faite dans le fichier [[Link]] de notre projet :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="1.0" xmlns="[Link]
3. <persistence-unit name="JPA" transaction-type="RESOURCE_LOCAL">
4. <!-- provider -->
5. <provider>[Link]</provider>
6. <properties>
7. <!-- Classes persistantes -->
8. <property name="[Link]" value="class, hbm" />
9. <!-- logs SQL
10. <property name="hibernate.show_sql" value="true"/>
11. <property name="hibernate.format_sql" value="true"/>
12. <property name="use_sql_comments" value="true"/>
13. -->
14. <!-- connexion JDBC -->
15. <property name="[Link].driver_class" value="[Link]" />
16. <property name="[Link]" value="jdbc:mysql://localhost:3306/JPA" />
17. <property name="[Link]" value="JPA" />
18. <property name="[Link]" value="JPA" />
19. <!-- création automatique du schéma -->
20. <property name="[Link]" value="create" />
21. <!-- Dialecte -->
22. <property name="[Link]" value="[Link].MySQL5InnoDBDialect" />
23. <!-- propriétés DataSource c3p0 -->
24. <property name="hibernate.c3p0.min_size" value="5" />
25. <property name="hibernate.c3p0.max_size" value="20" />
26. <property name="[Link]" value="300" />
27. <property name="hibernate.c3p0.max_statements" value="50" />
28. <property name="hibernate.c3p0.idle_test_period" value="3000" />
29. </properties>
30. </persistence-unit>
31. </persistence>
Pour comprendre cette configuration, il nous faut revenir sur l'architecture de l'accès aux données de notre application :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
28/489
• le fichier [[Link]] va configurer les couches [4, 5, 6] ;
• [4] : implémentation Hibernate de JPA ;
• [5] : Hibernate accède à la base de données via un pool de connexions. Un pool de connexions est une réserve de
connexions ouvertes avec le SGBD. Un SGBD est accédé par de multiples utilisateurs alors même que pour des raisons de
performances, il ne peut dépasser un nombre limite N de connexions ouvertes simultanément. Un code bien écrit ouvre
une connexion avec le SGBD un minimum de temps : il émet des ordres SQL et ferme la connexion. Il va faire cela de
façon répétée, à chaque fois qu'il a besoin de travailler avec la base. Le coût d'ouverture / fermeture d'une connexion n'est
pas négligeable et c'est là qu'intervient le pool de connexions. Celui-ci va au démarrage de l'application ouvrir N1
connexions avec le SGBD. C'est à lui que l'application demandera une connexion ouverte lorsqu'elle en aura besoin. Celle-
ci sera rendue au pool dès que l'application n'en aura plus besoin, de préférence le plus vite possible. La connexion n'est
pas fermée et reste disponible pour l'utilisateur suivant. Un pool de connexions est donc un système de partage de
connexions ouvertes ;
• [6] : le pilote JDBC du SGBD utilisé ;
Maintenant voyons comment le fichier [[Link]] configure les couches [4, 5, 6] ci-dessus :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
29/489
• ligne 28 : fréquence de vérification en milli-secondes de la validité des connexions. Une connexion du pool peut
devenir invalide pour diverses raisons (le pilote JDBC invalide la connexion parce qu'elle est trop longue, le pilote
JDBC présente des " bugs ", ...) ;
• ligne 20 : on demande ici, qu'à l'initialisation de l'unité de persistance, la base de données image des objets @Entity soit
générée.
Hibernate a désormais tous les outils pour émettre les ordres SQL de génération des tables de la base de données :
• la configuration des objets @Entity lui permet de connaître les tables à générer ;
• les lignes 15-18 et 24-28 lui permettent d'obtenir une connexion avec le SGBD ;
• la ligne 22 lui permet de savoir quel dialecte SQL utiliser pour générer les tables ;
Le fichier [[Link]] utilisé ici recrée une base neuve à chaque nouvelle exécution de l'application. Les tables sont recréées
(create table) après avoir été détruites (drop table) si elles existaient. On notera que ce n'est évidemment pas à faire avec une base en
production.
Un article A(id, version, nom) appartient exactement à une catégorie C(id, version, nom). Une catégorie C peut contenir 0, 1 ou
plusieurs articles. On a une relation un-à-plusieurs (Categorie -> Article) et la relation inverse plusieurs-à-un (Article ->
Categorie). Cette relation est matérialisée par la clé étrangère que possède la table [article] sur la table [categorie] (lignes 24-28 de la
DDL).
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
30/489
16.
17. @Column(length = 30)
18. private String nom;
19.
20. // relation principale Article (many) -> Category (one)
21. // implémentée par une clé étrangère (categorie_id) dans Article
22. // 1 Article a nécessairement 1 Categorie (nullable=false)
23. @ManyToOne(fetch=[Link])
24. @JoinColumn(name = "categorie_id", nullable = false)
25. private Categorie categorie;
26.
27. // constructeurs
28. public Article() {
29. }
30.
31. // getters et setters
32. ...
33. // toString
34. public String toString() {
35. return [Link]("Article[%d,%d,%s,%d]", id, version, nom, [Link]());
36. }
37.
38. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
31/489
• lignes 19-24 : l'ensemble (set) des articles de la catégorie ;
• ligne 23 : l'annotation @OneToMany désigne une relation un-à-plusieurs. Le One désigne l'@Entity
[Categorie] dans laquelle on se trouve, le Many le type [Article] de la ligne 24 : une (One) catégorie a plusieurs
(Many) articles ;
• ligne 23 : l'annotation est l'inverse (mappedBy) de l'annotation ManyToOne placée sur le champ categorie de
l'@Entity Article : mappedBy=categorie. La relation ManyToOne placée sur le champ categorie de l'@Entity
Article est la relation principale. Elle est indispensable. Elle matérialise la relation de clé étrangère qui lie
l'@Entity Article à l'@Entity Categorie. La relation OneToMany placée sur le champ articles de l'@Entity Categorie
est la relation inverse. Elle n'est pas indispensable. C'est une commodité pour obtenir les articles d'une
catégorie. Sans cette commodité, ces articles seraient obtenus par une requête JPQL ;
• ligne 23 : [Link] demande à que les opérations (persist, merge, remove) faites sur une @Entity
Categorie soient cascadées sur ses articles ;
• ligne 24 : les articles d'une catégorie seront placés dans un objet de type Set<Article>. Le type Set n'accepte pas
les doublons. Ainsi on ne peut pas mettre deux fois le même article dans l'objet Set<Article>. Que veut dire "le
même article" ? Pour dire que l'article a est le même que l'article b, Java utilise l'expression [Link](b). Dans la
classe Object, mère de toutes les classes, [Link](b) est vraie si a==b, c.a.d. si les objets a et b ont le même
emplacement mémoire. On pourrait vouloir dire que les articles a et b sont les mêmes s'ils ont le même nom.
Dans ce cas, le développeur doit redéfinir deux méthodes dans la classe [Article] :
• equals : qui doit rendre vrai si les deux articles ont le même nom ;
• hashCode : doit rendre une valeur entière identique pour deux objets [Article] que la méthode equals
considère comme égaux. Ici, la valeur sera construite à partir du nom de l'article. La valeur rendue par
hashCode peut être un entier quelconque. Elle est utilisée dans différents conteneurs d'objets,
notamment les dictionnaires (Hashtable) ;
La relation OneToMany peut utiliser d'autres types que le Set pour stocker le Many, des objets List, par
exemple.
• ligne 38 : la méthode [addArticle] nous permet d'ajouter un article à une catégorie. La méthode prend soin de mettre à jour
les deux extrémités de la relation OneToMany qui lie [Categorie] à [Article] ;
Maintenant que nous avons défini le rôle des entités JPA, nous allons apprendre à les manipuler via l'API JPA.
Nous savons que le couche JPA [2] crée un pont objet [3] / relationnel [4]. On appelle " contexte de persistance " l'ensemble des
objets gérés par la couche JPA dans le cadre de ce pont objet / relationnel. Pour accéder aux données du contexte de persistance,
un client JPA [1] doit passer par la couche JPA [2] :
1. il peut créer un objet et demander à la couche JPA de le rendre persistant. L'objet fait alors partie du contexte de
persistance ;
2. il peut demander à la couche [JPA] une référence d'un objet persistant existant ;
3. il peut modifier un objet persistant obtenu de la couche JPA ;
4. il peut demander à la couche JPA de supprimer un objet du contexte de persistance ;
La couche JPA présente au client une interface appelée [EntityManager] qui, comme son nom l'indique permet de gérer les objets
@Entity du contexte de persistance. Nous présentons ci-dessous, les principales méthodes de cette interface :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
32/489
l'objet entity déjà présent dans le contexte de persistance et ayant la même clé
primaire. Le résultat rendu est l'objet entity du contexte de persistance.
<T> T find(Class<T> entityClass, Object met dans le contexte de persistance, un objet cherché dans la base de données
primaryKey)
via sa clé primaire. Le type T de l'objet permet à la couche JPA de savoir quelle
table requêter. L'objet persistant ainsi créé est rendu au client.
Query createQuery(String queryText) crée un objet Query à partir d'une requête JPQL (Java Persistence Query
Language). Une requête JPQL est analogue à une requête SQL si ce n'est qu'on
requête des objets plutôt que des tables.
Query createNativeQuery(String queryText) méthode analogue à la précédente, si ce n'est que queryText est un ordre SQL et
non JPQL.
Query createNamedQuery(String name) méthode identique à createQuery, si ce n'est que l'ordre JPQL queryText a été
externalisé dans un fichier de configuration et associé à un nom. C'est ce nom
qui est le paramètre de la méthode.
Un objet EntityManager a un cycle de vie qui n'est pas forcément celui de l'application. Il a un début et une fin. Ainsi un client
JPA peut travailler successivement avec différents objets EntityManager. Le contexte de persistance associé à un EntityManager a le
même cycle de vie que lui. Ils sont indissociables l'un de l'autre. Lorsqu'un objet EntityManager est fermé, son contexte de
persistance est, si nécessaire, synchronisé avec la base de données puis il n'existe plus. Il faut créer un nouvel EntityManager pour
avoir de nouveau un contexte de persistance.
Le client JPA peut créer un EntityManager et donc un contexte de persistance avec l'instruction suivante :
EntityManagerFactory emf = [Link]("nom d'une unité de persistance");
• [Link] est une classe statique permettant d'obtenir une fabrique (factory) d'objets EntityManager.
Cette fabrique est liée à une unité de persistance précise. On se rappelle que le fichier de configuration
[META-INF/[Link]] permet de définir des unités de persistance et que celles-ci ont un nom :
<persistence-unit name="elections-dao-jpa-mysql-01PU" transaction-type="RESOURCE_LOCAL">
Ci-dessus, l'unité de persistance s'appelle elections-dao-jpa-mysql-01PU. Avec elle, vient toute une configuration qui lui est
propre, notamment le SGBD avec lequel elle travaille. L'instruction [[Link]("elections-dao-
jpa-mysql-01PU")] crée une fabrique d'objets de type EntityManagerFactory capable de fournir des objets EntityManager
destinés à gérer des contextes de persistance liés à l'unité de persistance nommée elections-dao-jpa-mysql-01PU.
L'obtention d'un objet EntityManager et donc d'un contexte de persistance se fait à partir de l'objet EntityManagerFactory de
la façon suivante :
EntityManager em = [Link]();
Les méthodes suivantes de l'interface [EntityManager] permettent de gérer le cycle de vie du contexte de persistance :
void close() le contexte de persistance est fermé. Force la synchronisation du contexte de persistance avec la base de données :
• si un objet du contexte n'est pas présent dans la base, il y est mis par une opération SQL INSERT ;
• si un objet du contexte est présent dans la base et qu'il a été modifié depuis qu'il a été lu, une opération
SQL UPDATE est faite pour persister la modification ;
•si un objet du contexte a été marqué comme " supprimé " à l'issue d'une opération remove sur lui, une
opération SQL DELETE est faite pour le supprimer de la base ;
void clear() le contexte de persistance est vidé de tous ses objets mais pas fermé.
void flush() le contexte de persistance est synchronisé avec la base de données de la façon décrite pour close().
Le client JPA peut forcer la synchronisation du contexte de persistance avec la base de données avec la méthode
[EntityManager].flush précédente. La synchronisation peut être explicite ou implicite. Dans le premier cas, c'est au client de faire des
opérations flush lorsqu'il veut faire des synchronisations, sinon celles-ci se font à certains moments que nous allons préciser. Le
mode de synchronisation est géré par les méthodes suivantes de l'interface [EntityManager] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
33/489
base.
FlushModeType getFlushMode() rend le mode actuel de synchronisation
Résumons. En mode [Link] qui est le mode par défaut, le contexte de persistance sera synchronisé avec la base
de données aux moments suivants :
1. avant chaque opération SELECT sur la base
2. à la fin d'une transaction sur la base
3. à la suite d'une opération flush ou close sur le contexte de persistance
En mode [Link], c'est la même chose sauf pour l'opération 1 qui n'a pas lieu. Le mode normal d'interaction
avec la couche JPA est un mode transactionnel. Le client fait diverses opérations sur le contexte de persistance, à l'intérieur d'une
transaction. Dans ce cas, les moments de synchronisation du contexte de persistance avec la base de données sont les cas 1 et 2 ci-
dessus en mode AUTO, et le cas 2 uniquement en mode COMMIT.
Terminons par l'API de l'interface Query, interface qui permet d'émettre des ordres JPQL sur le contexte de persistance ou bien
des ordres SQL directement sur la base pour y retrouver des données. L'interface Query est la suivante :
• 1 - la méthode getResultList execute un SELECT qui ramène plusieurs objets. Ceux-ci seront obtenus dans un objet List.
Cet objet est une interface. Celle-ci offre un objet Iterator qui permet de parcourir les éléments de la liste L sous la forme
suivante :
1. Iterator iterator = [Link]();
2. while ([Link]()) {
3. // exploiter l'objet [Link]() qui représente l'élément courant de la liste
4. ...
5. }
• 2 - la méthode getSingleResult exécute un ordre JPQL / SQL SELECT qui ramène un unique objet ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
34/489
• 3 - la méthode executeUpdate exécute un ordre SQL update ou delete et rend le nombre de lignes affectées
l'opération ;
• 4 - la méthode setParameter(String, Object) permet de donner une valeur à un paramètre nommé d'un ordre JPQL
paramétré ;
• 5 - la méthode setParameter(int, Object) mais le paramètre n'est pas désigné par son nom mais par sa position dans
l'ordre JPQL ;
JPQL (Java Persistence Query Language) est le langage de requêtes de la couche JPA. Le langage JPQL est apparenté au langage
SQL des bases de données. Alors que SQL travaille avec des tables, JPQL travaille avec les objets images de ces tables. Nous allons
étudier un exemple au sein de l'architecture suivante :
Couche Couche
couche [JPA / [JDBC] SGBD BD
[DAO] Hibernate]
La base de données qu'on appellera [dbrdvmedecins2] est une base de données MySQL5 avec quatre tables :
Elle rassemble des informations permettant de gérer les rendez-vous d'un groupe de médecins.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
35/489
• ID : n° identifiant le client - clé primaire de la table ;
• VERSION : n° identifiant la version de la ligne dans la table. Ce nombre est incrémenté de 1 à chaque fois qu'une
modification est apportée à la ligne ;
• NOM : le nom du client ;
• PRENOM : son prénom ;
• TITRE : son titre (Melle, Mme, Mr) ;
La seconde ligne de la table [CRENEAUX] (cf [1] ci-dessus) indique, par exemple, que le créneau n° 2 commence à 8 h 20 et se
termine à 8 h 40 et appartient au médecin n° 1 (Mme Marie PELISSIER).
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
36/489
• ID : n° identifiant le RV de façon unique – clé primaire ;
• JOUR : jour du RV ;
• ID_CRENEAU : créneau horaire du RV - clé étrangère sur le champ [ID] de la table [CRENEAUX] – fixe à la fois le
créneau horaire et le médecin concerné ;
• ID_CLIENT : n° du client pour qui est faite la réservation – clé étrangère sur le champ [ID] de la table [CLIENTS] ;
Cette table a une contrainte d'unicité sur les valeurs des colonnes jointes (JOUR, ID_CRENEAU) :
ALTER TABLE RV ADD CONSTRAINT UNQ1_RV UNIQUE (JOUR, ID_CRENEAU);
Si une ligne de la table[RV] a la valeur (JOUR1, ID_CRENEAU1) pour les colonnes (JOUR, ID_CRENEAU), cette valeur ne peut
se retrouver nulle part ailleurs. Sinon, cela signifierait que deux RV ont été pris au même moment pour le même médecin. D'un
point de vue programmation Java, le pilote JDBC de la base lance une SQLException lorsque ce cas se produit.
La ligne d'id égal à 3 (cf [1] ci-dessus) signifie qu'un RV a été pris pour le créneau n° 20 et le client n° 4 le 23/08/2006. La table
[CRENEAUX] nous apprend que le créneau n° 20 correspond au créneau horaire 16 h 20 - 16 h 40 et appartient au médecin n° 1
(Mme Marie PELISSIER). La table [CLIENTS] nous apprend que le client n° 4 est Melle Brigitte BISTROU.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
37/489
• en [2], on crée une base de données dont on a donné le nom [4] et l'encodage [5],
• en [7], la base a été créée. On clique sur son lien,
Par la suite, nous ne reviendrons plus sur cette base. Le lecteur est cependant invité à consulter son contenu pour savoir ce que
doivent produire les requêtes JPQL qu'il va écrire.
Couche Couche
couche [JPA / [JDBC] SGBD BD
[DAO] Hibernate]
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
38/489
[Link] Le projet Netbeans
C'est le suivant :
Couche Couche
couche [JPA / [JDBC] SGBD BD
[DAO] Hibernate]
Avec Netbeans, il est possible de générer automatiquement la couche [JPA] . Il est intéressant de connaître ces méthodes de
génération automatique car le code généré donne de précieuses indications sur la façon d'écrire des entités JPA.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
39/489
• dans l'onglet [Services] [1], dans la branche [Databases] [2], sélectionner le pilote JDBC MySQL [3],
• puis sélectionner l'option [4] "Connect Using" permettant de créer une connexion avec une base MySQL,
• en [5], donner les informations qui sont demandées. En [6], le nom de la base, en [7] l'utilisateur de la base et son mot de
passe,
• en [8], on peut tester les informations qu'on a fournies,
• en [9], le message attendu lorsque celles-ci sont bonnes,
• en [10], la connexion est créée. On y voit les quatre tables de la base de données connectée.
Couche Couche
couche [JPA / [JDBC] SGBD BD
[DAO] Hibernate]
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
40/489
Nous sommes en train de construire la couche [JPA]. La configuration de celle-ci est faite dans un fichier [[Link]] dans
lequel on définit des unités de persistance. Chacune d'elles a besoin des informations suivantes :
• cliquer droit sur le projet et choisir la création d'une unité de persistance [1-4],
• en [5-6], créer une unité de persistance,
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
41/489
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link]
instance" xsi:schemaLocation="[Link]
[Link]
3. <persistence-unit name="mv-rdvmedecins-jpql-hibernate-PU" transaction-type="RESOURCE_LOCAL">
4. <provider>[Link]</provider>
5. <properties>
6. <property name="[Link]" value="jdbc:mysql://localhost:3306/dbrdvmedecins2?
zeroDateTimeBehavior=convertToNull"/>
7. <property name="[Link]" value="root"/>
8. <property name="[Link]" value="[Link]"/>
9. <property name="[Link]" value=""/>
10. <property name="[Link].provider_class" value="[Link]"/>
11. </properties>
12. </persistence-unit>
13. </persistence>
Dans l'onglet [Design], on peut avoir une vue globale du fichier [[Link]] :
Pour avoir des logs d'Hibernate lors de l'exécution du projet, nous complétons le fichier [[Link]] de la façon suivante :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link]
instance" xsi:schemaLocation="[Link]
[Link]
3. <persistence-unit name="mv-rdvmedecins-jpql-hibernate-PU" transaction-type="RESOURCE_LOCAL">
4. <provider>[Link]</provider>
5. <properties>
6. <property name="[Link]" value="jdbc:mysql://localhost:3306/dbrdvmedecins2?
zeroDateTimeBehavior=convertToNull"/>
7. <property name="[Link]" value="root"/>
8. <property name="[Link]" value="[Link]"/>
9. <property name="[Link]" value=""/>
10. <property name="[Link].provider_class" value="[Link]"/>
11. <property name="hibernate.show_sql" value="true"/>
12. <property name="hibernate.format_sql" value="true"/>
13. </properties>
14. </persistence-unit>
15. </persistence>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
42/489
Des dépendances ont été ajoutées au projet. Le fichier [[Link]] est le suivant :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <project xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
3. <modelVersion>4.0.0</modelVersion>
4. <groupId>[Link]</groupId>
5. <artifactId>mv-rdvmedecins-jpql-hibernate</artifactId>
6. <version>1.0-SNAPSHOT</version>
7. <packaging>jar</packaging>
8. <repositories>
9. <repository>
10. <id>unknown-jars-temp-repo</id>
11. <name>A temporary repository created by NetBeans for libraries and jars it could not identify. Please replace
the dependencies in this repository with correct ones and delete this repository.</name>
12. <url>file:${[Link]}/lib</url>
13. </repository>
14. </repositories>
15. <dependencies>
16. <dependency>
17. <groupId>[Link]</groupId>
18. <artifactId>hibernate-entitymanager</artifactId>
19. <version>[Link]</version>
20. </dependency>
21. <dependency>
22. <groupId>[Link]</groupId>
23. <artifactId>[Link]</artifactId>
24. <version>SNAPSHOT</version>
25. <scope>provided</scope>
26. </dependency>
27. </dependencies>
28. <properties>
29. <[Link]>UTF-8</[Link]>
30. <[Link]>1.8</[Link]>
31. <[Link]>1.8</[Link]>
32. </properties>
33. </project>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
43/489
• en [1-5], on crée des entités JPA à partir d'une base de données,
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
44/489
• en [8], on donne un nom aux classes Java associées aux quatre tables. Ici on a enlevé le pluriel des noms des classes,
• ainsi qu'un nom de paquetage [9],
• en [10], JPA rassemble des lignes de tables de BD dans des collections. Nous choisissons la liste comme collection,
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
45/489
26. private String prenom;
27.
28. @OneToMany(cascade = [Link], mappedBy = "idMedecin")
29. private List<Creneau> creneauList;
30.
31. public Medecin() {
32. }
33.
34. public Medecin(Long id) {
35. [Link] = id;
36. }
37.
38. public Medecin(Long id, String titre, String nom, int version, String prenom) {
39. [Link] = id;
40. [Link] = titre;
41. [Link] = nom;
42. [Link] = version;
43. [Link] = prenom;
44. }
45.
46. @Override
47. public int hashCode() {
48. int hash = 0;
49. hash += (id != null ? [Link]() : 0);
50. return hash;
51. }
52.
53. @Override
54. public boolean equals(Object object) {
55. // TODO: Warning - this method won't work in the case the id fields are not set
56. if (!(object instanceof Medecin)) {
57. return false;
58. }
59. Medecin other = (Medecin) object;
60. if (([Link] == null && [Link] != null) || ([Link] != null && )) {
61. return false;
62. }
63. return true;
64. }
65.
66. @Override
67. public String toString() {
68. return "[Link][ id=" + id + " ]";
69. }
70.
71. // getters et setters
72. ...
73. }
• ligne 6, l'annotation @Entity fait de la classe [Medecin], une entité JPA, c.a.d. une classe liée à une table de BD via l'API
JPA,
• ligne 7, le nom de la table de BD associée à l'entité JPA. Chaque champ de la table fait l'objet d'un champ dans la classe
Java,
• ligne 8, la classe implémente l'interface Serializable. Ceci est nécessaire dans les applications client / serveur, où les entités
sont sérialisées entre le client et le serveur.
• lignes 11-14 : le champ id de la classe [Medecin] correspond au champ [ID] (ligne 10) de la table [medecins],
• lignes 16-17 : le champ titre de la classe [Medecin] correspond au champ [TITRE] (ligne 13) de la table [medecins],
• lignes 19-20 : le champ nom de la classe [Medecin] correspond au champ [NOM] (ligne 16) de la table [medecins],
• lignes 22-23 : le champ version de la classe [Medecin] correspond au champ [VERSION] (ligne 19) de la table [medecins].
Ici, l'assistant ne reconnaît pas le fait que la colonne est en fait un colonne de version qui doit être incrémentée à chaque
modification de la ligne à laquelle elle appartient. Pour lui donner ce rôle, il faut ajouter l'annotation @ Version. Nous le
ferons dans une prochaine étape,
• lignes 25-26 : le champ prenom de la classe [Medecin] correspond au champ [PRENOM] de la table [medecins],
• lignes 11-14 : le champ id correspond à la clé primaire [ID] de la table. Les annotations des lignes 8-9 précisent ce point,
• ligne 11 : l'annotation @Id indique que le champ annoté est associé à la clé primaire de la table,
• ligne 12 : la couche [JPA] va générer la clé primaire des lignes qu'elle insèrera dans la table [Medecins]. Il y a plusieurs
stratégies possibles. Ici la stratégie [Link] indique que la couche JPA va utiliser le mode auto_increment
de la table MySQL,
• lignes 28-29 : la table [creneaux] a une clé étrangère sur la table [medecins]. Un créneau appartient à un médecin.
Inversement, un médecin a plusieurs créneaux qui lui sont associés. On a donc une relation un (médecin) à plusieurs
(créneaux), une relation qualifiée par l'annotation @OneToMany par JPA (ligne 28). Le champ de la ligne 26 contiendra
tous les créneaux du médecin. Ceci sans programmation. Pour comprendre totalement la ligne 28, il nous faudra présenter
la classe [Creneau] ;
• lignes 53-64 : deux entités [Medecin] seront dites égales si elles ont la même clé primaire [id] ;
• lignes 46-51 : le hashCode de l'entité. Ici la méthode assure que deux entités [Medecin] de même [id] auront le même
hashCode ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
46/489
2.
3. import [Link];
4. ...
5.
6. @Entity
7. @Table(name = "creneaux")
8. public class Creneau implements Serializable {
9.
10. private static final long serialVersionUID = 1L;
11. @Id
12. @GeneratedValue(strategy = [Link])
13. @Column(name = "ID")
14. private Long id;
15.
16. @Column(name = "MDEBUT")
17. private int mdebut;
18.
19. @Column(name = "HFIN")
20. private int hfin;
21.
22. @Column(name = "HDEBUT")
23. private int hdebut;
24.
25. @Column(name = "MFIN")
26. private int mfin;
27.
28. @Column(name = "VERSION")
29. private int version;
30.
31. @OneToMany(cascade = [Link], mappedBy = "idCreneau")
32. private List<Rv> rvList;
33.
34. @JoinColumn(name = "ID_MEDECIN", referencedColumnName = "ID")
35. @ManyToOne(optional = false)
36. private Medecin idMedecin;
37.
38. public Creneau() {
39. }
40.
41. public Creneau(Long id) {
42. [Link] = id;
43. }
44.
45. public Creneau(Long id, int mdebut, int hfin, int hdebut, int mfin, int version) {
46. [Link] = id;
47. [Link] = mdebut;
48. [Link] = hfin;
49. [Link] = hdebut;
50. [Link] = mfin;
51. [Link] = version;
52. }
53.
54. @Override
55. public int hashCode() {
56. ...
57. }
58.
59. @Override
60. public boolean equals(Object object) {
61. ...
62. }
63.
64. @Override
65. public String toString() {
66. return "[Link][ id=" + id + " ]";
67. }
68.
69. // getters et setters
70. ...
71. }
• nous avons dit que la table [CRENEAUX] avait une clé étrangère vers la table [MEDECINS] : un créneau est associé à un
médecin. Plusieurs créneaux peuvent être associés au même médecin. On a une relation de la table [CRENEAUX] vers la
table [MEDECINS] qui est qualifiée de plusieurs (créneaux) à un (médecin). C'est l'annotation @ManyToOne de la
ligne 35 qui sert à qualifier la clé étrangère,
• la ligne 34 avec l'annotation @JoinColumn précise la relation de clé étrangère : la colonne [ID_MEDECIN] de la table
[CRENEAUX] est clé étrangère sur la colonne [ID] de la table [MEDECINS],
• ligne 36 : une référence sur le médecin propriétaire du créneau. On l'obtient là encore sans programmation. On
remarquera le nom du champ : [idMedecin]. Cela fait penser à l'identifiant du médecin alors qu'il s'agit du médecin lui-
même ;
Le lien de clé étrangère entre l'entité [Creneau] et l'entité [Medecin] est donc matérialisé par deux annotations :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
47/489
3. private Medecin idMedecin;
Les deux annotations reflètent la même relation : celle de la clé étrangère de la table [CRENEAUX] vers la table [MEDECINS]. On
dit qu'elles sont inverses l'une de l'autre. Seule la relation @ManyToOne est indispensable. Elle qualifie sans ambiguïté la relation
de clé étrangère. La relation @OneToMany est facultative. Si elle est présente, elle se contente de référencer la relation
@ManyToOne à laquelle elle est associée. C'est le sens de l'attribut mappedBy de la ligne 1 de l'entité [Medecin]. La valeur de cet
attribut est le nom du champ de l'entité [Creneau] qui a l'annotation @ManyToOne qui spécifie la clé étrangère. Toujours dans
cette même ligne 1 de l'entité [Medecin], l'attribut cascade=[Link] fixe le comportement de l'entité [Medecin] vis à
vis de l'entité [Creneau] :
• si on insère une nouvelle entité [Medecin] dans la base, alors les entités [Creneau] du champ de la ligne 2 doivent être
insérées elles-aussi,
• si on modifie une entité [Medecin] dans la base, alors les entités [Creneau] du champ de la ligne 2 doivent être modifiées
elles-aussi,
• si on supprime une entité [Medecin] dans la base, alors les entités [Creneau] du champ de la ligne 2 doivent être
supprimées elles-aussi.
Nous donnons le code des deux autres entités sans commentaires particuliers puisqu'elles n'introduisent pas de nouvelles notations.
L'entité [Client]
1. package [Link];
2.
3. ...
4. @Entity
5. @Table(name = "clients")
6. public class Client implements Serializable {
7. @Id
8. @GeneratedValue(strategy = [Link])
9. @Column(name = "ID")
10. private Long id;
11.
12. @Column(name = "TITRE")
13. private String titre;
14.
15. @Column(name = "NOM")
16. private String nom;
17.
18. @Column(name = "VERSION")
19. private int version;
20.
21. @Column(name = "PRENOM")
22. private String prenom;
23.
24. @OneToMany(cascade = [Link], mappedBy = "idClient")
25. private List<Rv> rvList;
26.
27. // constructeurs
28. ...
29. // getters et setters
30. ...
31.
32. @Override
33. public int hashCode() {
34. ...
35. }
36.
37. @Override
38. public boolean equals(Object object) {
39. ...
40. }
41.
42. @Override
43. public String toString() {
44. ...
45. }
46.
47. }
• les lignes 24-25 reflètent la relation de clé étrangère entre la table [rv] et la table [clients].
L'entité [Rv] :
1. package [Link];
2.
3. ...
4. @Entity
5. @Table(name = "rv")
6. public class Rv implements Serializable {
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
48/489
7. @Id
8. @GeneratedValue(strategy = [Link])
9. @Column(name = "ID")
10. private Long id;
11.
12. @Column(name = "JOUR")
13. @Temporal([Link])
14. private Date jour;
15.
16. @JoinColumn(name = "ID_CRENEAU", referencedColumnName = "ID")
17. @ManyToOne(optional = false)
18. private Creneau idCreneau;
19.
20. @JoinColumn(name = "ID_CLIENT", referencedColumnName = "ID")
21. @ManyToOne(optional = false)
22. private Client idClient;
23.
24. // constructeurs
25. ...
26.
27. // getters et setters
28. ...
29.
30. @Override
31. public int hashCode() {
32. ...
33. }
34.
35. @Override
36. public boolean equals(Object object) {
37. ...
38. }
39.
40. @Override
41. public String toString() {
42. ...
43. }
44.
45. }
• la ligne 13 qualifie le champ jour de type Java Date. On indique que dans la table [rv], la colonne [JOUR] (ligne 12) est de
type date (sans heure),
• lignes 16-18 : qualifient la relation de clé étrangère qu'a la table [RV] vers la table [CRENEAUX],
• lignes 20-22 : qualifient la relation de clé étrangère qu'a la table [RV] vers la table [CLIENTS].
La génération automatique des entités JPA nous permet d'obtenir une base de travail. Parfois elle est suffisante, parfois pas. C'est le
cas ici :
• il faut ajouter l'annotation @Version aux différents champs version des entités,
• il faut écrire des méthodes toString plus explicites que celles générées,
• les entités [Medecin] et [Client] sont analogues. On va les faire dériver d'une classe [Personne],
• on va supprimer les relations @OneToMany inverses des relations @ManyToOne. Elles ne sont pas indispensables et
elles amènent parfois des complications de programmation. On supprime et l'annotation et le champ annoté.
La classe Personne est utilisée pour représenter les médecins et les clients :
1. package [Link];
2.
3. import [Link];
4. import [Link].*;
5.
6. @MappedSuperclass
7. public class Personne implements Serializable {
8. private static final long serialVersionUID = 1L;
9. @Id
10. @GeneratedValue(strategy = [Link])
11. @Column(name = "ID")
12. private Long id;
13.
14. @Basic(optional = false)
15. @Column(name = "TITRE")
16. private String titre;
17.
18. @Basic(optional = false)
19. @Column(name = "NOM")
20. private String nom;
21.
22. @Basic(optional = false)
23. @Column(name = "VERSION")
24. @Version
25. private int version;
26.
27. @Basic(optional = false)
28. @Column(name = "PRENOM")
29. private String prenom;
30.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
49/489
31. // constructeurs
32.
33. public Personne() {
34. }
35.
36. public Personne(Long id) {
37. [Link] = id;
38. }
39.
40. public Personne(Long id, String titre, String nom, int version, String prenom) {
41. [Link] = id;
42. [Link] = titre;
43. [Link] = nom;
44. [Link] = version;
45. [Link] = prenom;
46. }
47.
48. // getters et setters
49. ...
50.
51. @Override
52. public String toString() {
53. return [Link]("[%s,%s,%s,%s,%s]", id, version, titre, prenom, nom);
54. }
55.
56. }
• ligne 6 : on notera que la classe [Personne] n'est pas elle-même une entité (@Entity). Elle va être la classe parent d'entités.
L'annotation @MappedSuperClass désigne cette situation.
L'entité [Client] encapsule les lignes de la table [clients]. Elle dérive de la classe [Personne] précédente :
1. package [Link];
2.
3. import [Link];
4. import [Link].*;
5.
6. @Entity
7. @Table(name = "clients")
8. public class Client extends Personne implements Serializable {
9. private static final long serialVersionUID = 1L;
10.
11. // constructeurs
12. public Client() {
13. super();
14. }
15.
16. public Client(Long id) {
17. super(id);
18. }
19.
20. public Client(Long id, String titre, String nom, int version, String prenom) {
21. super(id, titre, nom, version, prenom);
22. }
23.
24. @Override
25. public int hashCode() {
26. ...
27. }
28.
29. @Override
30. public boolean equals(Object object) {
31. ...
32. }
33.
34. @Override
35. public String toString() {
36. return [Link]("Client[%s,%s,%s,%s]", getId(), getTitre(), getPrenom(), getNom());
37. }
38.
39. }
L'entité [Medecin] qui encapsule les lignes de la table [MEDECINS] suit le même modèle :
1. package [Link];
2.
3. import [Link];
4. import [Link].*;
5.
6. @Entity
7. @Table(name = "medecins")
8. public class Medecin extends Personne implements Serializable {
9. private static final long serialVersionUID = 1L;
10.
11. // constructeurs
12. public Medecin() {
13. super();
14. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
50/489
15.
16. public Medecin(Long id) {
17. super(id);
18. }
19.
20. public Medecin(Long id, String titre, String nom, int version, String prenom) {
21. super(id, titre, nom, version, prenom);
22. }
23.
24. @Override
25. public int hashCode() {
26. ...
27. }
28.
29. @Override
30. public boolean equals(Object object) {
31. ...
32. }
33.
34. @Override
35. public String toString() {
36. return [Link]("Médecin[%s,%s,%s,%s]", getId(), getTitre(), getPrenom(), getNom());
37. }
38.
39. }
• les lignes 40-42 modélisent la relation "plusieurs à un" qui existe entre la table [creneaux] et la table [medecins] de la base
de données : un médecin a plusieurs créneaux, un créneau appartient à un seul médecin.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
51/489
L'entité [Rv] encapsule les lignes de la table [RV] :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link].*;
6.
7. @Entity
8. @Table(name = "rv")
9. public class Rv implements Serializable {
10.
11. private static final long serialVersionUID = 1L;
12. @Id
13. @GeneratedValue(strategy = [Link])
14. @Basic(optional = false)
15. @Column(name = "ID")
16. private Long id;
17.
18. @Basic(optional = false)
19. @Column(name = "JOUR")
20. @Temporal([Link])
21. private Date jour;
22.
23. @JoinColumn(name = "ID_CRENEAU", referencedColumnName = "ID")
24. @ManyToOne(optional = false)
25. private Creneau creneau;
26.
27. @JoinColumn(name = "ID_CLIENT", referencedColumnName = "ID")
28. @ManyToOne(optional = false)
29. private Client client;
30.
31. // constructeurs
32. ...
33.
34. // getters et setters
35. ...
36.
37. @Override
38. public int hashCode() {
39. ...
40. }
41.
42. @Override
43. public boolean equals(Object object) {
44. ...
45. }
46.
47. @Override
48. public String toString() {
49. return [Link]("Rv[%s, %s, %s]", id, creneau, client);
50. }
51. }
• les lignes 27-29 modélisent la relation "plusieurs à un" qui existe entre la table [RV] et la table [CLIENTS] (un client peut
apparaître dans plusieurs Rv) de la base de données et les lignes 23-25 la relation "plusieurs à un" qui existe entre la table
[RV] et la table [CRENEAUX] (un créneau peut apparaître dans plusieurs Rv).
Couche Couche
couche [JPA / [JDBC] SGBD BD
[console] Hibernate]
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
52/489
La classe [MainJpql] est la suivante :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7.
8. public class MainJpql {
9.
10. public static void main(String[] args) {
11. // EntityManagerFactory
12. EntityManagerFactory emf = [Link]("mv-rdvmedecins-jpql-hibernatePU");
13. // entityManager
14. EntityManager em = [Link]();
15. // scanner clavier
16. Scanner clavier = new Scanner([Link]);
17. // boucle de saisie des requêtes JPQL
18. [Link]("Requete JPQL sur la base dbrdvmedecins2 (* pour arrêter) :");
19. String requete = [Link]();
20. while (![Link]().equals("*")) {
21. try {
22. // affichage résultat requête
23. for (Object o : [Link](requete).getResultList()) {
24. [Link](o);
25. }
26. } catch (Exception e) {
27. [Link]("L'exception suivante s'est produite : " + e);
28. }
29. // on vide le contexte de persistance
30. [Link]();
31. // nouvelle requête
32. [Link]("---------------------------------------------");
33. [Link]("Requete JPQL sur la base dbrdvmedecins2 (* pour arrêter) :");
34. requete = [Link]();
35. }
36. // fermeture des ressources
37. [Link]();
38. [Link]();
39. }
40. }
• ligne 12 : création de l'EntityManagerFactory associé à l'unité de persistance que nous avons créée précédemment. le
paramètre de la méthode createEntityManagerFactory est le nom de cette unité de persistance :
1. <persistence-unit name="mv-rdvmedecins-jpql-hibernate-PU" transaction-type="RESOURCE_LOCAL">
2. ...
3. </persistence-unit>
Travail à faire : donner les requêtes JPQL permettant d'obtenir les informations suivantes :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
53/489
On s'inspirera de l'exemple du paragraphe 2.7 de [ref1]. Voici un exemple d'exécution :
1. Requete JPQL sur la base dbrdvmedecins2 (* pour arrêter) :
2. select c from Client c
3. Hibernate:
4. select
5. client0_.ID as ID2_,
6. client0_.NOM as NOM2_,
7. client0_.PRENOM as PRENOM2_,
8. client0_.TITRE as TITRE2_,
9. client0_.version as version2_
10. from
11. clients client0_
12. Client[1,Mr,Jules,MARTIN]
13. Client[2,Mme,Christine,GERMAN]
14. Client[3,Mr,Jules,JACQUARD]
15. Client[4,Melle,Brigitte,BISTROU]
Nous allons étudier une application qui montre les liens entre le contexte de persistance construit par une couche JPA et la base de
données à laquelle est adossée cette couche JPA. Les éléments utilisés par ce paragraphe sont présents dans le dossier [chap-03] du
support du TD :
Travail : créez la base de données [dbpersonnes2] avec WampServer et PhpMyAdmin. La base de données générée est sans tables.
Nous allons générer celles-ci à partir des entités JPA.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
54/489
[Link] Le projet Maven [mv-personnes]
Nous allons utiliser le projet Maven [mv-personnes] suivant :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
55/489
10. <property name="[Link]" value=""/>
11. <property name="[Link].provider_class" value="[Link]"/>
12. <property name="[Link]" value="drop-and-create"/>
13. <property name="hibernate.show_sql" value="true"/>
14. <property name="hibernate.format_sql" value="true"/>
15. <property name="use_sql_comments" value="true"/>
16. </properties>
17. </persistence-unit>
18. </persistence>
• ligne 12 : le mode [drop-and-create] d'Hibernate va créer les tables de la base de données [dbpersonnes2] (ligne 7) à
chaque instanciation de la couche JPA. Normalement, une unique fois au démarrage de l'application. Si les tables existent
déjà, elle seront tout d'abord supprimées (drop) avant d'être recréées (create) ;
• lignes 13-15 : demandent à ce que les ordres SQL émis par Hibernate soient logués sur la console ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
56/489
Les champs de cette entité ont été enrichis d'informations qui vont permettre de créer la table [personnes] liée à l'entité (ligne 17) :
• ligne 33 : le nom fait au plus 30 caractères et doit être unique dans la table ;
• ligne 36 : le prénom fait au plus 30 caractères ;
• lignes 32-50 : tous les champs ont l'attribut [nullable=false], ce qui va entraîner que les colonnes de la table
[PERSONNES] qui leur sont liées auront, elles, l'attribut SQL [NOT NULL] ;
• ligne 11 : on crée l'EntityManagerFactory correspondant à l'unité de persistance [mv-personnesPU] définie dans le fichier
[[Link]] que nous avons présenté ;
• ligne 13 : la création d'un [EntityManager] à partir de l'[EntityManagerFactory] précédent va créer le contexte de
persistance et la couche JPA. A cause de la propriété [drop-and-create] présente dans le fichier [[Link]], la table
[PERSONNES] associée à l'entité JPA [Personne] va être créée. Les attributs des différentes colonnes de cette table vont
être tirés des attributs donnés aux champs de l'entité JPA [Personne] ;
• lignes 15-16 : on libère les ressources ;
On peut vérifier avec PhpMyAdmin que la table [personnes] a bien été créée :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
57/489
[Link] Tests des liens entre contexte de persistance et base de données
La classe [Main] a pour but de montrer les liens entre contexte de persistance et base de données. Son code est le suivant :
1. package main;
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10.
11. public class Main {
12. // constantes
13.
14. // Contexte de persistance
15. private final static String TABLE_NAME = "PERSONNES";
16. private static EntityManagerFactory emf;
17. private static Personne p1;
18.
19. public static void main(String[] args) throws Exception {
20. // EntityManagerFactory
21. emf = [Link]("mv-personnesPU");
22. // nettoyage base
23. log("clean");
24. clean();
25.
26. // dump
27. log("dump");
28. dump();
29.
30. // test1
31. log("test1");
32. test1();
33.
34. // test2
35. log("test2");
36. test2();
37.
38. // fermeture EntityManagerFactory
39. [Link]();
40. }
41.
42. // affichage contenu table
43. private static void dump() {
44. // contexte de persistance
45. EntityManager em = [Link]();
46. // début transaction
47. EntityTransaction tx = [Link]();
48. [Link]();
49. // affichage personnes
50. for (Object p : [Link]("select p from Personne p order by [Link] asc").getResultList()) {
51. [Link](p);
52. }
53. // fin transaction
54. [Link]();
55. // fin contexte
56. [Link]();
57. }
58.
59. // raz BD
60. private static void clean() {
61. // contexte de persistance
62. EntityManager em = [Link]();
63. // début transaction
64. EntityTransaction tx = [Link]();
65. [Link]();
66. // supprimer les éléments de la table PERSONNES
67. [Link]("delete from " + TABLE_NAME).executeUpdate();
68. // fin transaction
69. [Link]();
70. // fin contexte
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
58/489
71. [Link]();
72. }
73.
74. // logs
75. private static void log(String message) {
76. [Link]("main : ----------- " + message);
77. }
78.
79. // gestion d'objets persistés
80. public static void test1() throws ParseException {
81. ...
82. }
83.
84. // gestion d'objets persistés
85. public static void test2() throws ParseException {
86. ...
87. }
88. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
59/489
4. EntityManager em = [Link]();
5. // création personnes
6. p1 = new Personne("Martin", "Paul", new SimpleDateFormat("dd/MM/yy").parse("31/01/2000"), true, 2);
7. Personne p2 = new Personne("Durant", "Sylvie", new SimpleDateFormat("dd/MM/yy").parse("05/07/2001"), false, 0);
8. // début transaction
9. EntityTransaction tx = [Link]();
10. [Link]("début transaction");
11. [Link]();
12. // persistance des personnes
13. // les logs montrent que l'opération SQL INSERT est immédiatement générée après l'opération persist
14. // probablement pour avoir la clé primaire
15. [Link]([Link]("Personne p1 %s non persistée", p1));
16. [Link]("[Link](p1)");
17. [Link](p1);
18. [Link]([Link]("Personne p1 %s persistée", p1));
19. // personne p2
20. // INSERT est généré dès l'opération persist
21. [Link]([Link]("Personne p2 %s non persistée", p2));
22. [Link]("[Link](p2)");
23. [Link](p2);
24. [Link]([Link]("Personne p2 %s persistée", p2));
25. [Link](true);
26. [Link]([Link]("Personne p2 %s modifiée", p2));
27. // l'opération DELETE liée à l'opération remove n'est faite qu'à la fin de la transaction
28. [Link]("[Link](p2)");
29. [Link](p2);
30. [Link]([Link]("Personne p2 %s supprimée", p2));
31. // modification p1
32. [Link]("P1");
33. // fin transaction
34. [Link]("fin transaction");
35. [Link]();
36. // fin contexte
37. [Link]();
38. // on affiche la table
39. dump();
40. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
60/489
59. personne0_.NOM asc
60. [1,1,P1,Paul,31/01/2000,true,2]
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
61/489
25. DATENAISSANCE=?,
26. MARIE=?,
27. NBENFANTS=?,
28. NOM=?,
29. PRENOM=?,
30. VERSION=?
31. where
32. ID=?
33. and VERSION=?
34. Hibernate:
35. select
36. personne0_.ID as ID1_0_,
37. personne0_.DATENAISSANCE as DATENAIS2_0_,
38. personne0_.MARIE as MARIE3_0_,
39. personne0_.NBENFANTS as NBENFANT4_0_,
40. personne0_.NOM as NOM5_0_,
41. personne0_.PRENOM as PRENOM6_0_,
42. personne0_.VERSION as VERSION7_0_
43. from
44. personnes personne0_
45. order by
46. personne0_.NOM asc
47. [1,2,P1,Paul,31/01/2000,false,2]
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
62/489
1.4 Version 1 : Architecture Spring / JPA
Note : le script SQL de la base de données de ce paragraphe est disponible dans le support du cours (voir site du document).
On se propose d’écrire une application console ainsi qu'une application graphique permettant d’établir le bulletin de salaire des
assistantes maternelles employées par la "Maison de la petite enfance" d'une commune. Cette application aura l'architecture
suivante :
7 Spring
Table EMPLOYES : rassemble des informations sur les différentes assistantes maternelles
Structure :
ID clé primaire
VERSION n° de version – augmente à chaque modification de la ligne
SS numéro de sécurité sociale de l'employé - unique
NOM nom de l'employé
PRENOM son prénom
ADRESSE son adresse
VILLE sa ville
CODEPOSTAL son code postal
INDEMNITE_ID clé étrangère sur le champ [ID] de la table [INDEMNITES]
Table COTISATIONS : rassemble des pourcentages nécessaires au calcul des cotisations sociales
Structure :
ID clé primaire
VERSION n° de version – augmente à chaque modification de la ligne
CSGRDS pourcentage : contribution sociale généralisée + contribution au remboursement de la dette
sociale
CSGD pourcentage : contribution sociale généralisée déductible
SECU pourcentage : sécurité sociale, veuvage, vieillesse
RETRAITE pourcentage : retraite complémentaire + assurance chômage
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
63/489
Son contenu pourrait être le suivant :
Les taux des cotisations sociales sont indépendants du salarié. La table précédente n'a qu'une ligne.
ID clé primaire
VERSION n° de version – augmente à chaque modification de la ligne
INDICE indice de traitement - unique
BASEHEURE prix net en euro d’une heure de garde
ENTRETIENJOUR indemnité d’entretien en euro par jour de garde
REPASJOUR indemnité de repas en euro par jour de garde
INDEMNITESCP indemnité de congés payés. C'est un pourcentage à appliquer au salaire
de base.
On notera que les indemnités peuvent varier d'une assistante maternelle à une autre. Elles sont en effet associées à une assistante
maternelle précise via l'indice de traitement de celle-ci. Ainsi Mme Marie Jouveinal qui a un indice de traitement de 2 (table
EMPLOYES) a un salaire horaire de 2,1 euro (table INDEMNITES).
[COTISATIONSSOCIALES]=[SALAIREBASE]*(CSGRD [COTISATIONSSOCIALES]=97,48
Total des cotisations sociales : S+CSGD+SECU+RETRAITE)/100
[INDEMNITÉS]=[TOTALJOURS]*(ENTRETIENJOUR+R [INDEMNITES]=104
Par ailleurs, l'assistante maternelle a droit, EPASJOUR)
chaque jour travaillé, à une indemnité
d'entretien ainsi qu'à une indemnité de
repas. A ce titre elle reçoit les indemnités
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
64/489
[TOTALHEURES]: total des heures [TOTALHEURES]=150
Les éléments suivants sont pris en compte travaillées dans le mois [TOTALJOURS]= 20
:
[TOTALJOURS]: total des jours travaillés
dans le mois
suivantes :
[SALAIREBASE]-[COTISATIONSSOCIALES]+ [salaire NET]=368,77
Au final, le salaire net à payer à l'assistante [INDEMNITÉS]
maternelle est le suivant :
On voit que :
• lignes 9-14 : affichent les informations concernant l'employé dont on a donné le n° de sécurité sociale ;
• lignes 17-20 : affichent les taux des différentes cotisations ;
• lignes 23-26 : affichent les indemnités associées à l'indice de traitement de l'employé (ici l'indice 2) ;
• lignes 29-33 : affichent les éléments constitutifs du salaire à payer ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
65/489
1.4.4 Fonctionnement de l'application graphique
L'application graphique permet le calcul des salaires des assistantes maternelles au travers d'un formulaire Swing :
1
2 3
4
• les informations passées en paramètres au programme console, sont maintenant saisies au moyen des champs de saisie [1,
2, 3] ;
• le bouton [4] demande le calcul du salaire ;
• le formulaire affiche les différents éléments du salaire jusqu'au salaire net à payer [5] ;
La liste déroulante [1, 6] ne présente pas les n°s SS des employés mais les noms et prénoms de ceux-ci. On fait ici l'hypothèse qu'il
n'y a pas deux employés de mêmes nom et prénom.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
66/489
• en [2-4], on importe un script SQL ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
67/489
• en [6], la base de données [dbpam_hibernate] a été créée. Son contenu est le suivant :
table EMPLOYES
table INDEMNITES
table COTISATIONS
Nous créons le projet Maven [mv-pam-jpa-hibernate] [1] en suivant la procédure décrite au paragraphe [Link], page 13 :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
68/489
Dans l'architecture de notre application il nous faut les éléments suivants :
• la base de données,
• le pilote JDBC du SGBD MySQL,
• la couche JPA / Hibernate (entités et configuration),
• le programme console de test.
• dans l'onglet [services] [1], on se connecte à la base de données avec le pilote JDBC de MySQL [2],
• en [3], le nom de la base de données à laquelle on veut se connecter,
• en [4], l'URL JDBC de la base,
• en [5], on se connecte en tant que root sans mot de passe,
• en [6], la base à laquelle on veut se connecter ;
• en [7], on peut tester la connexion,
• en [8], la connexion a réussi.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
69/489
• la connexion apparaît en [9],
• en [10-12], on ajoute un nouvel élément au projet,
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
70/489
• le fichier apparaît dans une nouvelle branche du projet, dans un dossier [META-INF] [19],
• en [20], des dépendances sont apparues. Ce sont celles du fournisseur JPA / Hibernate.
• ligne 3 : le nom de l'unité de persistance et le type de transactions. RESOURCE_LOCAL indique que le projet gère lui-
même les transactions. C'est ici le programme console qui devra le faire,
• ligne 4 : l'implémentation JPA utilisée est Hibernate,
• lignes 6-9 : les caractéristiques JDBC de la connexion à la base de données,
• ligne 11 : demande la création des tables correspondant aux entités JPA si elles n'existent pas. Lorsque la base existe et
qu'on ne veut plus la modifier, cette ligne doit être mise en commentaires,
Elles demandent à Hibernate d'afficher les ordres SQL qu'il envoie à la base de données. Le fichier complet est donc le suivant :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link]
instance" xsi:schemaLocation="[Link]
[Link]
3. <persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
4. <provider>[Link]</provider>
5. <properties>
6. <property name="[Link]" value="jdbc:mysql://localhost:3306/dbpam_hibernate?
zeroDateTimeBehavior=convertToNull"/>
7. <property name="[Link]" value="root"/>
8. <property name="[Link]" value="[Link]"/>
9. <property name="[Link]" value=""/>
10. <property name="[Link].provider_class" value="[Link]"/>
11. <property name="[Link]" value="create"/>
12. <property name="hibernate.show_sql" value="true"/>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
71/489
13. <property name="hibernate.format_sql" value="true"/>
14. <property name="use_sql_comments" value="true"/>
15. </properties>
16. </persistence-unit>
17. </persistence>
Nous avons configuré la couche JPA via le fichier [[Link]]. L'implémentation choisie a été Hibernate. Cela a amené des
dépendances dans le projet [20] :
Ces dépendances sont dues à l'inclusion d'Hibernate dans le projet. Elles sont inscrites dans le fichier [[Link]] qui configure le
projet Maven :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <project xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
3. <modelVersion>4.0.0</modelVersion>
4. <groupId>[Link]</groupId>
5. <artifactId>mv-pam-jpa-hibernate</artifactId>
6. <version>1.0-SNAPSHOT</version>
7. <packaging>jar</packaging>
8. <repositories>
9. <repository>
10. <id>unknown-jars-temp-repo</id>
11. <name>A temporary repository created by NetBeans for libraries and jars it could not identify. Please replace
the dependencies in this repository with correct ones and delete this repository.</name>
12. <url>file:${[Link]}/lib</url>
13. </repository>
14. </repositories>
15. <dependencies>
16. <dependency>
17. <groupId>[Link]</groupId>
18. <artifactId>hibernate-entitymanager</artifactId>
19. <version>[Link]</version>
20. </dependency>
21. <dependency>
22. <groupId>[Link]</groupId>
23. <artifactId>[Link]</artifactId>
24. <version>SNAPSHOT</version>
25. <scope>provided</scope>
26. </dependency>
27. </dependencies>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
72/489
28. <properties>
29. <[Link]>UTF-8</[Link]>
30. <[Link]>1.8</[Link]>
31. <[Link]>1.8</[Link]>
32. </properties>
33. </project>
Il nous faut ajouter une autre dépendance, celle du pilote JDBC de MySQL qui implémente la couche JDBC de l'architecture. Par
ailleurs, certaines des dépendances sont inutiles. Nous faisons évoluer le fichier [[Link]] de la façon suivante :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <project xmlns="[Link] xmlns:xsi="[Link]
3. xsi:schemaLocation="[Link] [Link]
4. <modelVersion>4.0.0</modelVersion>
5. <groupId>[Link]</groupId>
6. <artifactId>mv-pam-jpa-hibernate</artifactId>
7. <version>1.0-SNAPSHOT</version>
8. <packaging>jar</packaging>
9. <dependencies>
10. <!-- hibernate -->
11. <dependency>
12. <groupId>[Link]</groupId>
13. <artifactId>hibernate-entitymanager</artifactId>
14. <version>[Link]</version>
15. </dependency>
16. <!-- mysql -->
17. <dependency>
18. <groupId>mysql</groupId>
19. <artifactId>mysql-connector-java</artifactId>
20. <version>5.1.39</version>
21. </dependency>
22. </dependencies>
23. <properties>
24. <[Link]>UTF-8</[Link]>
25. <[Link]>1.8</[Link]>
26. <[Link]>1.8</[Link]>
27. </properties>
28. </project>
Note : pour connaître les n°s de version disponibles pour les différentes archives du fichier [[Link]], on peut procéder de la
façon suivante : sélectionner la version et faire [Ctrl-Espace] :
Note : ci-dessus, nous n'avons pas choisi les versions 6.x car elles provoquaient des erreurs lors de l'exécution du projet avec un
SGBD MySQL 5.x.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
73/489
Travail à faire : Ecrire les entités [Cotisation, Indemnite, Employe].
Notes :
• on pourra suivre la procédure décrite au paragraphe [Link].3, page 43,
• les entités feront partie d'un paquetage nommé [jpa],
• chaque entité aura un n° de version,
• si deux entités sont liées par une relation, seule la relation principale @ManyToOne sera construite. La relation inverse
@OneToMany ne le sera pas,
• on prendra la stratégie de génération IDENTITY pour les clés primaires.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
74/489
• ligne 12 : on crée l'EntityManager. Cette création crée la couche JPA. Le fichier [[Link]] va être exploité et alors les
tables de la base de données vont être créées,
• lignes 14-15 : on libère les ressources.
[Link].5 Tests
Revenons à l'architecture de notre projet :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
75/489
1. ------------------------------------------------------------------------
2. Building mv-pam-jpa-hibernate 1.0-SNAPSHOT
3. ------------------------------------------------------------------------
4.
5. --- exec-maven-plugin:1.2.1:exec (default-cli) @ mv-pam-jpa-hibernate ---
6. août 25, 2016 [Link] AM [Link] logDeprecation
7. WARN: HHH015016: Encountered a deprecated [Link]
[[Link]]; use [[Link]] instead.
8. août 25, 2016 [Link] AM [Link] logDeprecation
9. WARN: HHH015016: Encountered a deprecated [Link]
[[Link]]; use [[Link]] instead.
10. août 25, 2016 [Link] AM [Link] logDeprecation
11. WARN: HHH015016: Encountered a deprecated [Link]
[[Link]]; use [[Link]] instead.
12. août 25, 2016 [Link] AM [Link] logPersistenceUnitInformation
13. INFO: HHH000204: Processing PersistenceUnitInfo [
14. name: mv-pam-jpa-hibernatePU
15. ...]
16. août 25, 2016 [Link] AM [Link] logVersion
17. INFO: HHH000412: Hibernate Core {[Link]}
18. août 25, 2016 [Link] AM [Link] <clinit>
19. INFO: HHH000206: [Link] not found
20. août 25, 2016 [Link] AM [Link] buildBytecodeProvider
21. INFO: HHH000021: Bytecode provider name : javassist
22. août 25, 2016 [Link] AM [Link] <clinit>
23. INFO: HCANN000001: Hibernate Commons Annotations {[Link]}
24. août 25, 2016 [Link] AM [Link] configure
25. WARN: HHH000402: Using Hibernate built-in connection pool (not for production use!)
26. août 25, 2016 [Link] AM [Link] buildCreator
27. INFO: HHH000401: using driver [[Link]] at URL [jdbc:mysql://localhost:3306/dbpam_hibernate?
zeroDateTimeBehavior=convertToNull]
28. août 25, 2016 [Link] AM [Link] buildCreator
29. INFO: HHH000046: Connection properties: {user=root}
30. août 25, 2016 [Link] AM [Link] buildCreator
31. INFO: HHH000006: Autocommit mode: false
32. août 25, 2016 [Link] AM [Link] configure
33. INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
34. août 25, 2016 [Link] AM [Link] <init>
35. INFO: HHH000400: Using dialect: [Link].MySQL5Dialect
36. août 25, 2016 [Link] AM [Link] <init>
37. INFO: HHH000397: Using ASTQueryTranslatorFactory
38. août 25, 2016 [Link] AM [Link] <init>
39. INFO: HHH000400: Using dialect: [Link].MySQL5Dialect
40. Hibernate:
41. create table cotisations (
42. ID bigint not null auto_increment,
43. CSGD double precision not null,
44. CSGRDS double precision not null,
45. RETRAITE double precision not null,
46. SECU double precision not null,
47. VERSION integer not null,
48. primary key (ID)
49. )
50. Hibernate:
51. create table employes (
52. ID bigint not null auto_increment,
53. ADRESSE varchar(255) not null,
54. CP varchar(255) not null,
55. NOM varchar(255) not null,
56. PRENOM varchar(255) not null,
57. SS varchar(255) not null,
58. VERSION integer not null,
59. VILLE varchar(255) not null,
60. INDEMNITE_ID bigint not null,
61. primary key (ID)
62. )
63. Hibernate:
64. create table indemnites (
65. ID bigint not null auto_increment,
66. BASE_HEURE double precision not null,
67. ENTRETIEN_JOUR double precision not null,
68. INDEMNITES_CP double precision not null,
69. INDICE integer not null,
70. REPAS_JOUR double precision not null,
71. VERSION integer not null,
72. primary key (ID)
73. )
74. Hibernate:
75. alter table employes
76. add constraint FK_7xa7rdyjqxrbvew7n6f83ey5h
77. foreign key (INDEMNITE_ID)
78. references indemnites (ID)
79. août 25, 2016 [Link] AM [Link] stop
80. INFO: HHH000030: Cleaning up connection pool [jdbc:mysql://localhost:3306/dbpam_hibernate?
zeroDateTimeBehavior=convertToNull]
On trouve dans la console uniquement des logs d'Hibernate puisque le programme exécuté ne fait rien en-dehors d'instancier la
couche JPA. On notera les points suivants :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
76/489
• lignes 74-78 : création de la clé étrangère de la table [EMPLOYES] sur la table [INDEMNITES].
Dans Netbeans, on peut voir les tables dans la connexion qui a été créée précédemment :
Hibernate a tenté une seconde fois de recréer les tables de la base de données [dbpam_hibernate]. Il y a eu alors une erreur parce
que ces tables existent déjà. Cette erreur peut être évitée de la façon suivante :
Nous sélectionnons le fichier [[Link]] du projet et en [1-2] nous indiquons que la stratégie de génération des tables est
[Drop and Create]. Ainsi si les tables existent, elles seront supprimées avant d'être recréées. Le fichier [[Link]] évolue de la
façon suivante :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link]
instance" xsi:schemaLocation="[Link]
[Link]
3. <persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
4. <provider>[Link]</provider>
5. <class>[Link]</class>
6. <class>[Link]</class>
7. <class>[Link]</class>
8. <properties>
9. ..
10. <property name="[Link]" value="drop-and-create"/>
11. ...
12. </properties>
13. </persistence-unit>
14. </persistence>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
77/489
Si nous exécutons de nouveau le projet, il n'y a cette fois-ci pas d'erreurs. Hibernate ajoute les logs suivants :
1. Hibernate:
2. alter table employes
3. drop
4. foreign key FK_7xa7rdyjqxrbvew7n6f83ey5h
5. Hibernate:
6. drop table if exists cotisations
7. Hibernate:
8. drop table if exists employes
9. Hibernate:
10. drop table if exists indemnites
• lignes 1-4 : Hibernate supprime la clé étrangère de la table [EMPLOYES] sur la table [INDEMNITES] ;
• lignes 5-6 : suppression de la table [COTISATIONS] ;
• lignes 7-8 : suppression de la table [EMPLOYES] ;
• lignes 9-10 : suppression de la table [INDEMNITES] ;
Les tables créées dépendent à la fois de l'implémentation de la couche JPA utilisée, du SGBD utilisé et des annotations JPA utilisées
dans les entités. Ainsi une implémentation JPA / EclipseLink avec le même SGBD peut générer des tables différentes de celles
générées par une implémentation JPA / Hibernate. C'est ce que nous verrons prochainement.
Revenons sur la structure des tables générées. Dans les logs, il est écrit :
81. Hibernate:
82. create table cotisations (
83. ID bigint not null auto_increment,
84. CSGD double precision not null,
85. CSGRDS double precision not null,
86. RETRAITE double precision not null,
87. SECU double precision not null,
88. VERSION integer not null,
89. primary key (ID)
90. )
91. Hibernate:
92. create table employes (
93. ID bigint not null auto_increment,
94. ADRESSE varchar(255) not null,
95. CP varchar(255) not null,
96. NOM varchar(255) not null,
97. PRENOM varchar(255) not null,
98. SS varchar(255) not null,
99. VERSION integer not null,
100. VILLE varchar(255) not null,
101. INDEMNITE_ID bigint not null,
102. primary key (ID)
103. )
104. Hibernate:
105. create table indemnites (
106. ID bigint not null auto_increment,
107. BASE_HEURE double precision not null,
108. ENTRETIEN_JOUR double precision not null,
109. INDEMNITES_CP double precision not null,
110. INDICE integer not null,
111. REPAS_JOUR double precision not null,
112. VERSION integer not null,
113. primary key (ID)
114. )
115. Hibernate:
116. alter table employes
117. add constraint FK_7xa7rdyjqxrbvew7n6f83ey5h
118. foreign key (INDEMNITE_ID)
119. references indemnites (ID)
• lignes 94-100 : en l'absence d'informations, Hibernate a généré des colonnes de 255 caractères. C'est trop ;
• on voudrait par ailleurs, que le n° SS de l'employé, ligne 98, soit unique dans la table [employes] ;
Ces contraintes sur la génération des tables peuvent être obtenues à partir d'informations supplémentaires sur certains champs des
entités JPA. L'entité JPA [Employe] pourrait maintenant être la suivante :
1. @Entity
2. @Table(name = "employes")
3. @NamedQueries({
4. @NamedQuery(name = "[Link]", query = "SELECT e FROM Employe e")})
5. public class Employe implements Serializable {
6.
7. private static final long serialVersionUID = 1L;
8. @Id
9. @GeneratedValue(strategy = [Link])
10. @Basic(optional = false)
11. @Column(name = "ID")
12. private Long id;
13.
14. @Basic(optional = false)
15. @Column(name = "VERSION")
16. @Version
17. private int version;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
78/489
18.
19. @Basic(optional = false)
20. @Column(name = "PRENOM", length = 30)
21. private String prenom;
22.
23. @Basic(optional = false)
24. @Column(name = "NOM", length = 30)
25. private String nom;
26.
27. @Basic(optional = false)
28. @Column(name = "SS", length = 15, unique = true)
29. private String ss;
30.
31. @Basic(optional = false)
32. @Column(name = "ADRESSE", length = 50)
33. private String adresse;
34.
35. @Basic(optional = false)
36. @Column(name = "CP", length = 5)
37. private String cp;
38.
39. @Basic(optional = false)
40. @Column(name = "VILLE", length = 20)
41. private String ville;
42.
43. @JoinColumn(name = "INDEMNITE_ID", referencedColumnName = "ID")
44. @ManyToOne(optional = false, fetch = [Link])
45. private Indemnite indemniteId;
• lignes 20, 24, 28, 32, 36, 40 : avec l'attribut [length], on fixe la taille des colonnes associées ;
• ligne 28 : l'attribut [unique=true] va forcer la génération d'une contrainte d'unicité sur la colonne [SS] de la table
[employes] ;
• lignes 4-8, 10 : les colonnes ont la taille fixée dans l'entité [Employe] ;
• lignes 15-16 : la contrainte d'unicité de la colonne [SS] ;
Dans la même veine, nous souhaitons que dans la table [INDEMNITES], la colonne [INDICE] ait l'attribut UNIQUE : deux lignes
ne peuvent avoir le même indice. Nous faisons évoluer le champ [[Link]] de la façon suivante :
1. @Column(name = "INDICE", unique = true, nullable = false)
2. private int indice;
• créer une base de données MySQL [dbpam_eclipselink] sans tables pour le moment [1-6],
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
79/489
• créer un nouveau projet Maven [mv-pam-jpa-eclipselink] en suivant la démarche du paragraphe [Link], page 13 :
• en suivant la démarche du paragraphe [Link], page 39, créer le fichier [[Link]] du projet. Prendre
l'implémentation JPA 2.1 EclipseLink,
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
80/489
20. <artifactId>eclipselink</artifactId>
21. <version>2.6.3</version>
22. </dependency>
23. </dependencies>
24. <properties>
25. <[Link]>UTF-8</[Link]>
26. <[Link]>1.8</[Link]>
27. <[Link]>1.8</[Link]>
28. </properties>
29. </project>
• ajouter les entités JPA et le programme console du précédent projet. Adaptez le programme console pour qu'il utilise la
bonne unité de persistance :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
81/489
20. Internal Exception: [Link]: Table 'dbpam_eclipselink.employes' doesn't
exist
21. Error Code: 1146
22. Call: ALTER TABLE employes DROP FOREIGN KEY FK_employes_INDEMNITE_ID
23. Query: DataModifyQuery(sql="ALTER TABLE employes DROP FOREIGN KEY FK_employes_INDEMNITE_ID")
24. [EL Fine]: sql: 2016-08-25 [Link].85--ServerSession(1725097945)--Connection(848409667)--Thread(Thread[main,5,main])--DROP
TABLE cotisations
25. [EL Fine]: sql: 2016-08-25 [Link].866--ServerSession(1725097945)--Thread(Thread[main,5,main])--SELECT 1
26. [EL Warning]: 2016-08-25 [Link].866--ServerSession(1725097945)--Thread(Thread[main,5,main])--Exception [EclipseLink-4002]
(Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): [Link]
27. Internal Exception: [Link]: Unknown table
'dbpam_eclipselink.cotisations'
28. Error Code: 1051
29. Call: DROP TABLE cotisations
30. Query: DataModifyQuery(sql="DROP TABLE cotisations")
31. [EL Fine]: sql: 2016-08-25 [Link].866--ServerSession(1725097945)--Connection(848409667)--Thread(Thread[main,5,main])--
DROP TABLE employes
32. [EL Fine]: sql: 2016-08-25 [Link].866--ServerSession(1725097945)--Thread(Thread[main,5,main])--SELECT 1
33. [EL Warning]: 2016-08-25 [Link].866--ServerSession(1725097945)--Thread(Thread[main,5,main])--Exception [EclipseLink-4002]
(Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): [Link]
34. Internal Exception: [Link]: Unknown table 'dbpam_eclipselink.employes'
35. Error Code: 1051
36. Call: DROP TABLE employes
37. Query: DataModifyQuery(sql="DROP TABLE employes")
38. [EL Fine]: sql: 2016-08-25 [Link].866--ServerSession(1725097945)--Connection(848409667)--Thread(Thread[main,5,main])--
DROP TABLE indemnites
39. [EL Fine]: sql: 2016-08-25 [Link].866--ServerSession(1725097945)--Thread(Thread[main,5,main])--SELECT 1
40. [EL Warning]: 2016-08-25 [Link].866--ServerSession(1725097945)--Thread(Thread[main,5,main])--Exception [EclipseLink-4002]
(Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): [Link]
41. Internal Exception: [Link]: Unknown table 'dbpam_eclipselink.indemnites'
42. Error Code: 1051
43. Call: DROP TABLE indemnites
44. Query: DataModifyQuery(sql="DROP TABLE indemnites")
45. ...
46. [EL Fine]: sql: 2016-08-25 [Link].897--ServerSession(1725097945)--Connection(848409667)--Thread(Thread[main,5,main])--
CREATE TABLE cotisations (ID BIGINT AUTO_INCREMENT NOT NULL, CSGD DOUBLE, CSGRDS DOUBLE, RETRAITE DOUBLE, SECU DOUBLE,
VERSION INTEGER, PRIMARY KEY (ID))
47. [EL Fine]: sql: 2016-08-31 [Link].653--ServerSession(2032251042)--Connection(1883652579)--Thread(Thread[main,5,main])--
CREATE TABLE employes (ID BIGINT AUTO_INCREMENT NOT NULL, ADRESSE VARCHAR(50), CP VARCHAR(5), NOM VARCHAR(30), PRENOM
VARCHAR(30), SS VARCHAR(15) UNIQUE, VERSION INTEGER, VILLE VARCHAR(20), INDEMNITE_ID BIGINT, PRIMARY KEY (ID))
48. [EL Fine]: sql: 2016-08-31 [Link].715--ServerSession(2032251042)--Connection(1883652579)--Thread(Thread[main,5,main])--
CREATE TABLE indemnites (ID BIGINT AUTO_INCREMENT NOT NULL, BASE_HEURE DOUBLE NOT NULL, ENTRETIEN_JOUR DOUBLE,
INDEMNITES_CP DOUBLE, INDICE INTEGER UNIQUE, REPAS_JOUR DOUBLE, VERSION INTEGER, PRIMARY KEY (ID))
49. [EL Fine]: sql: 2016-08-25 [Link].085--ServerSession(1725097945)--Connection(848409667)--Thread(Thread[main,5,main])--
ALTER TABLE employes ADD CONSTRAINT FK_employes_INDEMNITE_ID FOREIGN KEY (INDEMNITE_ID) REFERENCES indemnites (ID)
50. [EL Config]: connection: 2016-08-25 [Link].272--ServerSession(1725097945)--Connection(848409667)--
Thread(Thread[main,5,main])--disconnect
51. [EL Info]: connection: 2016-08-25 [Link].272--ServerSession(1725097945)--Thread(Thread[main,5,main])--file:/E:/istia/
istia-1617/netbeans/support-td/chap-04/mv-pam-jpa-eclipselink/target/classes/_mv-pam-jpa-eclipselinkPU logout successful
52. [EL Config]: connection: 2016-08-25 [Link].272--ServerSession(1725097945)--Connection(768669591)--
Thread(Thread[main,5,main])--disconnect
53. ------------------------------------------------------------------------
54. BUILD SUCCESS
55. ------------------------------------------------------------------------
56. Total time: 3.157 s
57. Finished at: 2016-08-25T[Link]+02:00
58. Final Memory: 7M/245M
59. ------------------------------------------------------------------------
L'existence des tables générées peut être vérifiée dans Netbeans [1-2] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
82/489
Ligne 47 des logs, on remarque que les colonnes de la table [EMPLOYES] n'ont pas l'attribut [NOT NULL] alors qu'avec
Hibernate elles avaient cet attribut. Avec [Hibernate], l'annotation JPA [@Bacic(optional=false)] avait suffi pour générer les
attributs [NOT NULL] des colonnes. Avec EclipseLink ce n'est pas le cas. Pour obtenir cet attribut [NOT NULL], on peut utiliser
l'attribut [nullable=false] :
1. @Column(name = "PRENOM", length = 30, nullable = false)
2. private String prenom;
Avec ce changement, les logs de création de la table [EMPLOYES] deviennent les suivants :
1. [EL Fine]: sql: 2016-08-31 [Link].955--ServerSession(2032251042)--Connection(1883652579)--Thread(Thread[main,5,main])--
CREATE TABLE employes (ID BIGINT AUTO_INCREMENT NOT NULL, ADRESSE VARCHAR(50) NOT NULL, CP VARCHAR(5) NOT NULL, NOM
VARCHAR(30) NOT NULL, PRENOM VARCHAR(30) NOT NULL, SS VARCHAR(15) NOT NULL UNIQUE, VERSION INTEGER NOT NULL, VILLE
VARCHAR(20) NOT NULL, INDEMNITE_ID BIGINT, PRIMARY KEY (ID))
Cette nouvelle version de l'entité [Employe] fonctionne également avec l'implémentation JPA / Hibernate. Pour ces raisons, nous
garderons cette définition pour la suite. Nous faisons ce même type de modifications dans les deux autres entités : [Cotisation] et
[Indemnite].
Maintenant, modifions le mode de génération des clés primaires des entités JPA [Cotisation, Employe, Indemnite] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
83/489
1. @Id
2. //@GeneratedValue(strategy = [Link])
3. @GeneratedValue(strategy = [Link])
4. @Basic(optional = false)
5. @Column(name = "ID")
• ligne 2, on indique avec le mode [AUTO] qu'on laisse l'implémentation JPA décider du mode de génération des clés
primaires.
On fait cette modification dans les trois entités JPA. Puis procédez de la façon suivante :
On voit en [3] qu'une table [SEQUENCE] est apparue. Les logs de création des tables sont les suivants :
1. [EL Fine]: sql: 2016-08-31 [Link].948--ServerSession(2032251042)--Connection(1883652579)--Thread(Thread[main,5,main])--
CREATE TABLE cotisations (ID BIGINT NOT NULL, CSGD DOUBLE NOT NULL, CSGRDS DOUBLE NOT NULL, RETRAITE DOUBLE NOT NULL, SECU
DOUBLE NOT NULL, VERSION INTEGER NOT NULL, PRIMARY KEY (ID))
2. [EL Fine]: sql: 2016-08-31 [Link].026--ServerSession(2032251042)--Connection(1883652579)--Thread(Thread[main,5,main])--
CREATE TABLE employes (ID BIGINT NOT NULL, ADRESSE VARCHAR(50) NOT NULL, CP VARCHAR(5) NOT NULL, NOM VARCHAR(30) NOT NULL,
PRENOM VARCHAR(30) NOT NULL, SS VARCHAR(15) NOT NULL UNIQUE, VERSION INTEGER NOT NULL, VILLE VARCHAR(20) NOT NULL,
INDEMNITE_ID BIGINT, PRIMARY KEY (ID))
3. [EL Fine]: sql: 2016-08-31 [Link].104--ServerSession(2032251042)--Connection(1883652579)--Thread(Thread[main,5,main])--
CREATE TABLE indemnites (ID BIGINT NOT NULL, BASE_HEURE DOUBLE NOT NULL, ENTRETIEN_JOUR DOUBLE NOT NULL, INDEMNITES_CP
DOUBLE NOT NULL, INDICE INTEGER NOT NULL UNIQUE, REPAS_JOUR DOUBLE NOT NULL, VERSION INTEGER NOT NULL, PRIMARY KEY (ID))
4. [EL Fine]: sql: 2016-08-31 [Link].198--ServerSession(2032251042)--Connection(1883652579)--Thread(Thread[main,5,main])--
ALTER TABLE employes ADD CONSTRAINT FK_employes_INDEMNITE_ID FOREIGN KEY (INDEMNITE_ID) REFERENCES indemnites (ID)
5. [EL Fine]: sql: 2016-08-31 [Link].323--ServerSession(2032251042)--Connection(1883652579)--Thread(Thread[main,5,main])--
CREATE TABLE SEQUENCE (SEQ_NAME VARCHAR(50) NOT NULL, SEQ_COUNT DECIMAL(38), PRIMARY KEY (SEQ_NAME))
6. [EL Fine]: sql: 2016-08-31 [Link].37--ServerSession(2032251042)--Connection(1883652579)--Thread(Thread[main,5,main])--
SELECT * FROM SEQUENCE WHERE SEQ_NAME = SEQ_GEN
7. [EL Fine]: sql: 2016-08-31 [Link].37--ServerSession(2032251042)--Connection(1883652579)--Thread(Thread[main,5,main])--
INSERT INTO SEQUENCE(SEQ_NAME, SEQ_COUNT) values (SEQ_GEN, 0)
• ligne 1 : création de la table [COTISATIONS]. La clé primaire [ID] n'a pas l'attribut SQL [auto_increment] qu'elle avait
dans la version précédente du projet. C'est le cas également pour les deux autres tables (lignes 2 et 3),
• ligne 4 : création de la clé étrangère de la table [EMPLOYES] vers la table [INDEMNITES],
• ligne 5 : création d'une table [SEQUENCE] avec deux colonnes [SEQ_NAME, SEQ_COUNT]. La colonne
[SEQ_NAME] est clé primaire,
• ligne 7 : création d'une ligne dans la table [SEQUENCE] avec les valeurs (SEQ_GEN, 0).
EclipseLink utilise la table [SEQUENCE] pour générer les clés primaires des tables [COTISATIONS, EMPLOYES,
INDEMNITES].
Donc, à partir des mêmes entités JPA, les implémentations JPA peuvent ne pas générer les mêmes tables selon le mode de
génération des clés primaires :
• avec MySQL et Hibernate, les modes [[Link]] et [[Link]] génèrent les mêmes trois
tables en utilisant le mode [autoincrement] pour la génération des clés primaires,
• avec MySQL, EclipseLink et le mode [[Link]], la base générée est identique à celle générée par
Hibernate,
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
84/489
• avec MySQL, EclipseLink et le mode [[Link]], la base générée est différente des précédentes : elle
contient quatre tables et la génération des clés primaires n'utilise pas le mode [autoincrement] de MySQL mais une table
supplémentaire appelée [SEQUENCE].
Dans la suite du document, quelque soit l'implémentation JPA utilisée, Hibernate ou EclipseLink :
• on utilisera la stratégie [[Link]] pour générer les clés primaires des entités JPA ;
• on utilisera la base de données [dbpam_hibernate] ;
1. créer et tester un projet [mv-pam-jpa-hibernate-oracle] utilisant une implémentation JPA Hibernate et un SGBD Oracle,
2. créer et tester un projet [mv-pam-jpa-hibernate-mssql] utilisant une implémentation JPA Hibernate et un SGBD SQL
Server,
3. créer et tester un projet [mv-pam-jpa-eclipselink-oracle] utilisant une implémentation JPA EclipseLink et un SGBD
Oracle,
4. créer et tester un projet [mv-pam-jpa-eclipselink-mssql] utilisant une implémentation JPA EclipseLink et un SGBD SQL
Server,
Les lignes 44-46 définissent la clé étrangère de la table [EMPLOYES] vers la table [INDEMNITES]. L'attribut fetch de la ligne 46
définit la stratégie de recherche du champ Indemnite indemniteId de la ligne 46. Il y a deux modes :
• [Link] : lorsqu'un employé est cherché, l'indemnité qui lui correspond n'est pas ramenée. Elle le sera lorsque
le champ [Employe].indemniteId sera référencé pour la première fois.
• [Link] : lorsqu'un employé est cherché, l'indemnité qui lui correspond est ramenée. C'est le mode par
défaut lorsqu'aucun mode n'est précisé.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
85/489
Pour comprendre l'intérêt de l'option [Link], on peut prendre l'exemple suivant. Une liste d'employés sans les indemnités
est présentée dans une page web avec un lien [Details]. Un clic sur ce lien présente alors les indemnités de l'employé sélectionné. On
voit que :
• pour afficher la première page on n'a pas besoin des employés avec leurs indemnités. Le mode [Link] convient
alors,
• pour afficher la seconde page avec les détails, une requête supplémentaire doit être faite à la base de données pour avoir
les indemnités de l'employé sélectionné.
Le mode [Link] évite de ramener des données dont l'application n'a pas besoin tout de suite. Voyons un exemple.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
86/489
• en [1-2], on renomme le projet,
• en [3-4], on renomme le projet et son artifactId,
• en [5], le nouveau projet.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
87/489
• ligne 16 : on obtient l'EntityManager qui nous permet de dialoguer avec la couche JPA,
• ligne 17 : on demande l'employé de nom Jouveinal,
• ligne 18 : on ferme l'EntityManager. Cela a pour effet de fermer le contexte de persistence,
• ligne 23 : on affiche l'employé reçu,
• ligne 26 : on recrée un nouveau contexte de persistance,
• ligne 27 : on exécute une requête JPQL pour ramener l'employé de nom Jouveinal, avec ses indemnités. Pour cela, on
contourne le mode [LAZY] en forçant une jointure entre les tables [EMPLOYES] et [INDEMNITES] (left join) en y
ajoutant le moté clé [fetch] qui va forcer la recherche de l'indemnité de l'employé malgré l'attribut [LAZY] de ce champ ;
• lignes 7 et 18 : la méthode [Employe].toString() va être appelée. Celle-ci va rendre la chaîne jSON de l'objet [Employe]
suivant :
1. package jpa;
2.
3. import [Link];
4. ...
5.
6. @Entity
7. @Table(name = "employes")
8. public class Employe implements Serializable {
9.
10. private static final long serialVersionUID = 1L;
11.
12. // clé primaire
13. @Id
14. @GeneratedValue(strategy = [Link])
15. @Column(name = "ID")
16. private Long id;
17.
18. // prénom
19. @Column(name = "PRENOM", length = 30, nullable = false)
20. private String prenom;
21.
22. // nom
23. @Column(name = "NOM", length = 30, nullable = false)
24. private String nom;
25.
26. // n° de sécu
27. @Column(name = "SS", length = 15, nullable = false)
28. private String ss;
29.
30. // adresse
31. @Column(name = "ADRESSE", length = 50, nullable = false)
32. private String adresse;
33.
34. // code postal
35. @Column(name = "CP", length = 5, nullable = false)
36. private String cp;
37.
38. // ville
39. @Column(name = "VILLE", length = 20, nullable = false)
40. private String ville;
41.
42. // n° de version
43. @Column(name = "VERSION", nullable = false)
44. @Version
45. private int version;
46.
47. // indemnités
48. @JoinColumn(name = "INDEMNITE_ID", referencedColumnName = "ID")
49. @ManyToOne(optional = false, fetch = [Link])
50. private Indemnite indemniteId;
51.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
88/489
52. // constructeurs
53. ...
54.
55. // toString
56. @Override
57. public String toString() {
58. return [Link]("Employé=[id=%s, version=%s, prénom=%s, nom=%s, adresse=%s, ville=%s, code postal=%s, indemnite=
%s]",
59. id, version, prenom, nom, adresse, ville, cp, indemniteId);
60. }
61.
62. // getters et setters ---------
63. ...
64.
65. // égalité d'instances ------------
66. ...
67. }
• ligne 49 : le champ indemniteId est ramené en mode LAZY. Pour produire la chaîne jSON de l'objet [Employe], la
bibliothèque jSON va automatiquement vouloir produire la chaîne jSON du champ [Indemnite indemniteId] de la ligne
49. Elle va alors faire une opération [[Link]()] pour obtenir l'objet [Indemnite] associé à l'employé. Avec
Hibernate, cette opération n'est possible que si le contexte de persistance est encore ouvert ;
• lignes 6-10 : on devrait avoir une exception. En effet, la méthode toString va être appelée. Elle va utiliser le champ
indemniteId. Celui-ci va être cherché. Comme le contexte de persistance a été fermé, une exception va se produire,
• ligne 12 : on crée un nouvel EntityManager,
• ligne 13 : on demande l'employé Jouveinal en demandant explicitement dans la requête JPQL l'indemnité qui va avec. Cette
demande explicite est nécessaire parce que le mode de recherche de cette indemnité est LAZY,
• ligne 15 : on ferme l'EntityManager,
• lignes 17-21 : on réaffiche l'employé. Il ne devrait pas y avoir d'exception.
Pour exécuter le projet, on a besoin d'une base de données remplie. On la créera en suivant la démarche du paragraphe 1.4.5, page
66. Par ailleurs, le fichier [[Link]] doit être modifié :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.0" xmlns="[Link] xmlns:xsi="[Link]
instance" xsi:schemaLocation="[Link]
[Link]
3. <persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
4. <provider>[Link]</provider>
5. <class>[Link]</class>
6. <class>[Link]</class>
7. <class>[Link]</class>
8. <properties>
9. <property name="[Link]" value="jdbc:mysql://localhost:3306/dbpam_hibernate"/>
10. <property name="[Link]" value=""/>
11. <property name="[Link]" value="[Link]"/>
12. <property name="[Link]" value="root"/>
13. <property name="[Link].provider_class" value="[Link]"/>
14. </properties>
15. </persistence-unit>
16. </persistence>
• on a enlevé l'option qui créait les tables. La base de données ici existe déjà et est remplie,
• on a enlevé les options qui faisaient qu'Hibernate loguait les ordres SQL qu'il émettait vers la base de données.
• ligne 1 : l'exception qui s'est produite lorsqu'il a fallu chercher l'indemnité qui manquait, alors que la session était fermée.
On voit que l'indemnité n'avait pas été ramenée à cause du mode LAZY,
• ligne 2 : l'employé avec son indemnité obtenue par une requête qui a contourné le mode LAZY.
EclipseLink nécessite une configuration spéciale pour que le mode [LAZY] du champ [[Link]] soit respecté. Si on suit
la même démarche que précédemment avec les fichiers suivants :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
89/489
[[Link]]
1. <?xml version="1.0" encoding="UTF-8"?>
2. <project xmlns="[Link] xmlns:xsi="[Link]
3. xsi:schemaLocation="[Link] [Link]
4. <modelVersion>4.0.0</modelVersion>
5. <groupId>[Link]</groupId>
6. <artifactId>mv-pam-jpa-eclipselink-lazy</artifactId>
7. <version>1.0-SNAPSHOT</version>
8. <packaging>jar</packaging>
9. <name>mv-pam-jpa-eclipselink-lazy</name>
10. <dependencies>
11. <!-- mysql -->
12. <dependency>
13. <groupId>mysql</groupId>
14. <artifactId>mysql-connector-java</artifactId>
15. <version>5.1.39</version>
16. </dependency>
17. <!-- [Link] -->
18. <dependency>
19. <groupId>[Link]</groupId>
20. <artifactId>jackson-core</artifactId>
21. <version>2.8.1</version>
22. </dependency>
23. <dependency>
24. <groupId>[Link]</groupId>
25. <artifactId>jackson-databind</artifactId>
26. <version>2.8.1</version>
27. </dependency>
28. <!-- eclipselink -->
29. <dependency>
30. <groupId>[Link]</groupId>
31. <artifactId>eclipselink</artifactId>
32. <version>2.6.3</version>
33. </dependency>
34. </dependencies>
35. <properties>
36. <[Link]>UTF-8</[Link]>
37. <[Link]>1.8</[Link]>
38. <[Link]>1.8</[Link]>
39. </properties>
40. </project>
[[Link]]
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link]
instance" xsi:schemaLocation="[Link]
[Link]
3. <persistence-unit name="mv-pam-jpa-eclipselink-lazyPU" transaction-type="RESOURCE_LOCAL">
4. <provider>[Link]</provider>
5. <class>[Link]</class>
6. <class>[Link]</class>
7. <class>[Link]</class>
8. <properties>
9. <property name="[Link]" value="jdbc:mysql://localhost:3306/dbpam_hibernate?
zeroDateTimeBehavior=convertToNull"/>
10. <property name="[Link]" value="root"/>
11. <property name="[Link]" value="[Link]"/>
12. <property name="[Link]" value=""/>
13. <property name="[Link]" value="FINE"/>
14. <!-- propriétés nécessaires pour que le [@ManyToOne] puisse être cherché en mode LAZY -->
15. </properties>
16. </persistence-unit>
17. </persistence>
Ce log indique que le mode [LAZY] du champ [[Link]] ne sera pas respecté (Reverting the lazy setting). La raison
de ce comportement est obscure : [since weaving was not enabled or did not occur].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
90/489
10. <dependencies>
11. ...
12. </dependencies>
13. <properties>
14. ...
15. </properties>
16.
17. <!-- plugins EclipseLink -->
18. <build>
19. <plugins>
20. <!--[[Link] -->
21. <!-- This plugin ensures the EclipseLink static weaving -->
22. <plugin>
23. <artifactId>staticweave-maven-plugin</artifactId>
24. <groupId>[Link]</groupId>
25. <version>1.0.0</version>
26. <executions>
27. <execution>
28. <goals>
29. <goal>weave</goal>
30. </goals>
31. <phase>process-classes</phase>
32. <configuration>
33. <logLevel>ALL</logLevel>
34. <includeProjectClasspath>true</includeProjectClasspath>
35. </configuration>
36. </execution>
37. </executions>
38. <dependencies>
39. <dependency>
40. <groupId>[Link]</groupId>
41. <artifactId>eclipselink</artifactId>
42. <version>2.6.3</version>
43. </dependency>
44. </dependencies>
45. </plugin>
46. </plugins>
47. <pluginManagement>
48. <plugins>
49. <!-- This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build
itself. -->
50. <plugin>
51. <groupId>[Link].m2e</groupId>
52. <artifactId>lifecycle-mapping</artifactId>
53. <version>1.0.0</version>
54. <configuration>
55. <lifecycleMappingMetadata>
56. <pluginExecutions>
57. <pluginExecution>
58. <pluginExecutionFilter>
59. <groupId>
60. [Link]
61. </groupId>
62. <artifactId>
63. staticweave-maven-plugin
64. </artifactId>
65. <versionRange>
66. [1.0.0,)
67. </versionRange>
68. <goals>
69. <goal>weave</goal>
70. </goals>
71. </pluginExecutionFilter>
72. <action>
73. <execute>
74. <runOnIncremental>true</runOnIncremental>
75. </execute>
76. </action>
77. </pluginExecution>
78. </pluginExecutions>
79. </lifecycleMappingMetadata>
80. </configuration>
81. </plugin>
82. </plugins>
83. </pluginManagement>
84. </build>
85. </project>
On ajoute les lignes 17-84 au fichier [[Link]]. Ceci fait, on construit le projet (Clean and Build). Parmi les logs on trouve les lignes
suivantes :
1. --- staticweave-maven-plugin:1.0.0:weave (default) @ mv-pam-jpa-eclipselink-lazy ---
2. Start EclipseLink static weaving...
3. [EL Fine]: moxy: 2016-09-01 [Link].438--Thread(Thread[main,5,main])--SAXParserFactory instance:
[Link]@25e49cb2
4. [EL Finest]: jpa: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])--Begin predeploying
Persistence Unit mv-pam-jpa-eclipselink-lazyPU; session mv-pam-jpa-eclipselink-lazyPU; state Initial; factoryCount 0
5. [EL Finest]: properties: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])--
property=[Link]; default value=true
6. [EL Finest]: properties: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])--
property=[Link]; default value=true
7. [EL Finest]: properties: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])--
property=[Link]; default value=false
8. [EL Finest]: properties: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])--
property=[Link]; default value=true
9. [EL Finest]: properties: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])--
property=[Link]; default value=true
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
91/489
10. [EL Finest]: properties: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])--
property=[Link]; default value=true
11. [EL Finest]: properties: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])--
property=[Link]-share-emf; default value=true
12. [EL Finest]: properties: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])--
property=[Link]-share-cache; default value=false
13. [EL Finer]: metadata: 2016-09-01 [Link].625--Thread(Thread[main,5,main])--Searching for mapping file: [META-INF/[Link]]
at root URL: [file:/D:/data/istia-1617/netbeans/support-td/chap-04/mv-pam-jpa-eclipselink-lazy/target/classes/].
14. [EL Finer]: metadata: 2016-09-01 [Link].625--Thread(Thread[main,5,main])--Searching for mapping file:
[META-INF/[Link]] at root URL: [file:/D:/data/istia-1617/netbeans/support-td/chap-04/mv-pam-jpa-eclipselink-
lazy/target/classes/].
15. [EL Config]: metadata: 2016-09-01 [Link].719--ServerSession(297031141)--Thread(Thread[main,5,main])--The access type for
the persistent class [class [Link]] is set to [FIELD].
16. [EL Config]: metadata: 2016-09-01 [Link].735--ServerSession(297031141)--Thread(Thread[main,5,main])--The access type for
the persistent class [class [Link]] is set to [FIELD].
17. [EL Config]: metadata: 2016-09-01 [Link].735--ServerSession(297031141)--Thread(Thread[main,5,main])--The access type for
the persistent class [class [Link]] is set to [FIELD].
18. [EL Config]: metadata: 2016-09-01 [Link].75--ServerSession(297031141)--Thread(Thread[main,5,main])--The target entity
(reference) class for the many to one mapping element [field indemniteId] is being defaulted to: class [Link].
19. [EL Config]: metadata: 2016-09-01 [Link].75--ServerSession(297031141)--Thread(Thread[main,5,main])--The alias name for
the entity class [class [Link]] is being defaulted to: Cotisation.
20. [EL Config]: metadata: 2016-09-01 [Link].766--ServerSession(297031141)--Thread(Thread[main,5,main])--The alias name for
the entity class [class [Link]] is being defaulted to: Indemnite.
21. [EL Config]: metadata: 2016-09-01 [Link].766--ServerSession(297031141)--Thread(Thread[main,5,main])--The alias name for
the entity class [class [Link]] is being defaulted to: Employe.
22. [EL Finer]: weaver: 2016-09-01 [Link].782--ServerSession(297031141)--Thread(Thread[main,5,main])--Class [[Link]]
registered to be processed by weaver.
23. [EL Finer]: weaver: 2016-09-01 [Link].782--ServerSession(297031141)--Thread(Thread[main,5,main])--Class [[Link]]
registered to be processed by weaver.
24. [EL Finer]: weaver: 2016-09-01 [Link].782--ServerSession(297031141)--Thread(Thread[main,5,main])--Class [[Link]]
registered to be processed by weaver.
25. [EL Finest]: jpa: 2016-09-01 [Link].797--ServerSession(297031141)--Thread(Thread[main,5,main])--End predeploying
Persistence Unit mv-pam-jpa-eclipselink-lazyPU; session mv-pam-jpa-eclipselink-lazyPU; state Predeployed; factoryCount 0
26. [EL Finest]: weaver: 2016-09-01 [Link].797--Thread(Thread[main,5,main])--Begin weaver class transformer processing class
[jpa/Cotisation].
27. [EL Finest]: weaver: 2016-09-01 [Link].797--Thread(Thread[main,5,main])--Initializing compute class writer for
[jpa/Cotisation]. Class loaders: context [5e5073ab] argument [730f9695].
28. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Visiting the end of the class [jpa/Cotisation].
29. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Adding _persistence_get() method into
[jpa/Cotisation].
30. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Adding _persistence_set() method into
[jpa/Cotisation].
31. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Weaved persistence (PersistenceEntity)
[jpa/Cotisation].
32. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Weaved change tracking (ChangeTracker)
[jpa/Cotisation].
33. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Weaved fetch groups (FetchGroupTracker)
[jpa/Cotisation].
34. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Weaved REST [jpa/Cotisation].
35. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--End weaver class transformer processing class
[jpa/Cotisation].
36. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Begin weaver class transformer processing class
[jpa/Employe].
37. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Initializing compute class writer for
[jpa/Employe]. Class loaders: context [5e5073ab] argument [730f9695].
38. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Visiting the end of the class [jpa/Employe].
39. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Adding _persistence_get() method into
[jpa/Employe].
40. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Adding _persistence_set() method into
[jpa/Employe].
41. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Weaved persistence (PersistenceEntity)
[jpa/Employe].
42. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Weaved change tracking (ChangeTracker)
[jpa/Employe].
43. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Weaved lazy (ValueHolder indirection)
[jpa/Employe].
44. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Weaved fetch groups (FetchGroupTracker)
[jpa/Employe].
45. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Weaved REST [jpa/Employe].
46. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--End weaver class transformer processing class
[jpa/Employe].
47. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Begin weaver class transformer processing class
[jpa/Indemnite].
48. [EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Initializing compute class writer for
[jpa/Indemnite]. Class loaders: context [5e5073ab] argument [730f9695].
49. [EL Finest]: weaver: 2016-09-01 [Link].828--Thread(Thread[main,5,main])--Visiting the end of the class [jpa/Indemnite].
50. [EL Finest]: weaver: 2016-09-01 [Link].828--Thread(Thread[main,5,main])--Adding _persistence_get() method into
[jpa/Indemnite].
51. [EL Finest]: weaver: 2016-09-01 [Link].828--Thread(Thread[main,5,main])--Adding _persistence_set() method into
[jpa/Indemnite].
52. [EL Finest]: weaver: 2016-09-01 [Link].828--Thread(Thread[main,5,main])--Weaved persistence (PersistenceEntity)
[jpa/Indemnite].
53. [EL Finest]: weaver: 2016-09-01 [Link].828--Thread(Thread[main,5,main])--Weaved change tracking (ChangeTracker)
[jpa/Indemnite].
54. [EL Finest]: weaver: 2016-09-01 [Link].828--Thread(Thread[main,5,main])--Weaved fetch groups (FetchGroupTracker)
[jpa/Indemnite].
55. [EL Finest]: weaver: 2016-09-01 [Link].828--Thread(Thread[main,5,main])--Weaved REST [jpa/Indemnite].
56. [EL Finest]: weaver: 2016-09-01 [Link].828--Thread(Thread[main,5,main])--End weaver class transformer processing class
[jpa/Indemnite].
57. [EL Finest]: weaver: 2016-09-01 [Link].828--Thread(Thread[main,5,main])--Missing class details for [main/Main].
58. [EL Finest]: weaver: 2016-09-01 [Link].828--Thread(Thread[main,5,main])--Using existing class bytes for [main/Main].
59. Finished EclipseLink static weaving.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
92/489
Ce weaving a enrichi les entités JPA de façon à ce que le mode [LAZY] des champs [@ManyToOne] et [@OneToMany] soit
respecté. On appelle statique ce weaving parce qu'il se fait à la compilation du projet. On appelle dynamique un weaving qui se
fait en cours d'exécution du projet. Ce n'est pas le cas ici.
• lignes 15-17 : on déclare qu'un weaving statique est fait à la compilation. Sans ces lignes et sous certaines conditions
(configuration Spring / EclipseLink), un weaving dynamique est tenté et amène des erreurs. Dans ce projet, on ne verra
aucune différence selon que ces lignes sont ou non présentes ;
A la ligne 18, on veut inspecter l'objet [Employe] obtenu ligne 17 pour voir si son champ [Indemnite indemniteId] a été initialisé ou
non. Puis on exécute l'application en mode débogage [2-3]. A la ligne d'arrêt, on obtient les résultats suivants :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
93/489
On voit en [4] que [indemniteId] vaut null indiquant par là que le mode [LAZY] a bien fonctionné.
Il était important de les enlever pour la raison suivante : nous allons découvrir qu'EclipseLink :
• respecte bien le mode [LAZY] du champ [Indemnite [Link]] comme nous venons de le voir ;
• est malgré tout capable de récupérer l'indemnité dès que le code fait référence au champ [Indemnite
[Link]]. C'est à dire que ce qui n'était pas possible avec Hibernate et provoquait une exception est possible
avec EclipseLink. La méthode [toString] fait référence au champ [indemniteId] (ligne 4). Donc le simple fait d'utiliser la
méthode [[Link]] va forcer la récupération de l'indemnité de l'employé. Or le débogueur, pour afficher un
objet, utilise la méthode [toString] de celui-ci. Donc avec la méthode [toString] décommentée ci-dessus, le débogueur
montre un objet [Employe] avec son indemnité et on a alors l'impression que le mode [LAZY] n'a pas fonctionné. C'est
pourquoi il a été nécessaire de modifier la méthode [[Link]] ;
Avec cette configuration, les résultats de l'exécution du projet sont les suivants :
1. ...
2. [EL Info]: connection: 2016-09-01 [Link].241--ServerSession(226710952)--Thread(Thread[main,5,main])--/file:/D:/data/
istia-1617/netbeans/support-td/chap-04/mv-pam-jpa-eclipselink-lazy/target/classes/_mv-pam-jpa-eclipselink-lazyPU login
successful
3. [EL Fine]: sql: 2016-09-01 [Link].522--ServerSession(226710952)--Connection(737897289)--Thread(Thread[main,5,main])--
SELECT ID, ADRESSE, CP, NOM, PRENOM, SS, VERSION, VILLE, INDEMNITE_ID FROM employes WHERE (NOM = ?)
4. bind => [Jouveinal]
5. [EL Fine]: sql: 2016-09-01 [Link].569--ServerSession(226710952)--Connection(737897289)--Thread(Thread[main,5,main])--
SELECT ID, BASE_HEURE, ENTRETIEN_JOUR, INDEMNITES_CP, INDICE, REPAS_JOUR, VERSION FROM indemnites WHERE (ID = ?)
6. bind => [2]
7. Employé=Employé=[id=1, version=1, prénom=Marie, nom=Jouveinal, adresse=5 rue des oiseaux, ville=St Corentin, code
postal=49203, indemnite=Indemnite[id=2, version=1, indice=2, base heure=2.1, entretien jour=2.1, repas jour=3.1, indemnités
CP=15.0]
8. [EL Fine]: sql: 2016-09-01 [Link].584--ServerSession(226710952)--Connection(737897289)--Thread(Thread[main,5,main])--
SELECT [Link], [Link], [Link], [Link], [Link], [Link], [Link], [Link], t1.INDEMNITE_ID, [Link], t0.BASE_HEURE,
t0.ENTRETIEN_JOUR, t0.INDEMNITES_CP, [Link], t0.REPAS_JOUR, [Link] FROM employes t1 LEFT OUTER JOIN indemnites t0 ON
([Link] = t1.INDEMNITE_ID) WHERE ([Link] = ?)
9. bind => [Jouveinal]
10. Employé=Employé=[id=1, version=1, prénom=Marie, nom=Jouveinal, adresse=5 rue des oiseaux, ville=St Corentin, code
postal=49203, indemnite=Indemnite[id=2, version=1, indice=2, base heure=2.1, entretien jour=2.1, repas jour=3.1, indemnités
CP=15.0]
11. [EL Config]: connection: 2016-09-01 [Link].647--ServerSession(226710952)--Connection(737897289)--
Thread(Thread[main,5,main])--disconnect
12. ...
• ligne 7 : le 1er affichage. On pourrait croire, à le voir, que le mode [LAZY] n'a pas fonctionné. Le débogueur nous a
montré que ce n'était pas le cas. On voit sur ces logs que :
◦ lignes 3-4 : une requête a été faite pour obtenir l'employé ;
◦ lignes 5-6 : une seconde requête a été faite pour satisfaire la méthode [[Link]] qui référence le champ
[[Link]]. L'indemnité a alors été cherchée en base ;
• ligne 10 : le second affichage. On voit lignes 8-9 que cette fois EclipseLink a fait une jointure pour ramener à la fois
l'employé et son indemnité ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
94/489
1.4.7 Le projet Maven [mv-pam-spring-hibernate]
Note : le prochain travail pratique est au paragraphe 1.4.8, page 108. D'ici là, il faut lire le cours.
7 Spring
Pour implémenter cette architecture, nous utiliserons le projet suivant présent dans le support du TD :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
95/489
7. <groupId>[Link]</groupId>
8. <artifactId>mv-pam-spring-hibernate</artifactId>
9. <version>1.0-SNAPSHOT</version>
10. <packaging>jar</packaging>
11. <name>mv-pam-spring-hibernate</name>
12.
13. <dependencies>
14. <!-- hibernate -->
15. <dependency>
16. <groupId>[Link]</groupId>
17. <artifactId>hibernate-entitymanager</artifactId>
18. <version>[Link]</version>
19. </dependency>
20. <!-- mysql -->
21. <dependency>
22. <groupId>mysql</groupId>
23. <artifactId>mysql-connector-java</artifactId>
24. <version>5.1.39</version>
25. </dependency>
26. <!-- JUnit -->
27. <dependency>
28. <groupId>junit</groupId>
29. <artifactId>junit</artifactId>
30. <version>4.12</version>
31. <scope>test</scope>
32. </dependency>
33. <!-- pool de connexions DBCP -->
34. <dependency>
35. <groupId>commons-dbcp</groupId>
36. <artifactId>commons-dbcp</artifactId>
37. <version>1.4</version>
38. </dependency>
39. <dependency>
40. <groupId>commons-pool</groupId>
41. <artifactId>commons-pool</artifactId>
42. <version>1.6</version>
43. </dependency>
44. <!-- Spring framework -->
45. <dependency>
46. <groupId>[Link]</groupId>
47. <artifactId>spring-tx</artifactId>
48. <version>[Link]</version>
49. </dependency>
50. <dependency>
51. <groupId>[Link]</groupId>
52. <artifactId>spring-context</artifactId>
53. <version>[Link]</version>
54. </dependency>
55. <dependency>
56. <groupId>[Link]</groupId>
57. <artifactId>spring-orm</artifactId>
58. <version>[Link]</version>
59. </dependency>
60. <!-- interfaces swing -->
61. <dependency>
62. <groupId>[Link]</groupId>
63. <artifactId>swing-layout</artifactId>
64. <version>1.0.3</version>
65. </dependency>
66. </dependencies>
67.
68. <properties>
69. <[Link]>UTF-8</[Link]>
70. <[Link]>1.8</[Link]>
71. <[Link]>1.8</[Link]>
72. </properties>
73. </project>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
96/489
La couche [JPA] utilisera les entités JPA construites au paraphaphe [Link], page 68 avec le mode LAZY présenté au paragraphe
[Link], page 85. Ces entités sont les suivantes :
L'entité [Employe]
1. package jpa;
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
12. import [Link];
13. import [Link];
14.
15. @Entity
16. @Table(name = "employes")
17. public class Employe implements Serializable {
18.
19. private static final long serialVersionUID = 1L;
20.
21. // clé primaire
22. @Id
23. @GeneratedValue(strategy = [Link])
24. @Column(name = "ID")
25. private Long id;
26.
27. // prénom
28. @Column(name = "PRENOM", length = 30, nullable = false)
29. private String prenom;
30.
31. // nom
32. @Column(name = "NOM", length = 30, nullable = false)
33. private String nom;
34.
35. // n° de sécu
36. @Column(name = "SS", length = 15, nullable = false, unique = true)
37. private String ss;
38.
39. // adresse
40. @Column(name = "ADRESSE", length = 50, nullable = false)
41. private String adresse;
42.
43. // code postal
44. @Column(name = "CP", length = 5, nullable = false)
45. private String cp;
46.
47. // ville
48. @Column(name = "VILLE", length = 20, nullable = false)
49. private String ville;
50.
51. // n° de version
52. @Column(name = "VERSION", nullable = false)
53. @Version
54. private int version;
55.
56. // indemnités
57. @JoinColumn(name = "INDEMNITE_ID", referencedColumnName = "ID")
58. @ManyToOne(optional = false, fetch = [Link])
59. private Indemnite indemnite;
60.
61. // clé étrangère
62. @Column(name = "INDEMNITE_ID", updatable = false, insertable = false, nullable = false)
63. private Long indemniteId;
64.
65. // constructeurs
66. public Employe() {
67. }
68.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
97/489
69. public Employe(Long id) {
70. [Link] = id;
71. }
72.
73. public Employe(Long id, int version, String prenom, String nom, String ss, String adresse, String ville, String cp,
Indemnite indemnite) {
74. [Link] = id;
75. [Link] = prenom;
76. [Link] = ss;
77. [Link] = adresse;
78. [Link] = cp;
79. [Link] = ville;
80. [Link] = nom;
81. [Link] = version;
82. [Link] = indemnite;
83. }
84.
85. // toString
86. @Override
87. public String toString() {
88. return [Link]("Employé=[id=%s, version=%s, prénom=%s, nom=%s, adresse=%s, ville=%s, code postal=%s, indemniteId=
%s]",
89. id, version, prenom, nom, adresse, ville, cp, indemniteId);
90. }
91.
92. // getters et setters ---------
93. ...
94.
95. // égalité d'instances ------------
96. @Override
97. public int hashCode() {
98. int hash = 0;
99. hash += (id != null ? [Link]() : 0);
100. return hash;
101. }
102.
103. @Override
104. public boolean equals(Object object) {
105. // TODO: Warning - this method won't work in the case the id fields are not set
106. if (!(object instanceof Employe)) {
107. return false;
108. }
109. Employe other = (Employe) object;
110. return !(([Link] == null && [Link] != null) || ([Link] != null && ));
111. }
112.
113. }
• ligne 58, on notera que l'entité [Indemnite] de l'employé est cherchée en mode LAZY ;
• lignes 62-63 : nous avons rajouté un champ [indemniteId] pour récupérer la valeur de la colonne [INDEMNITE_ID] de la
table [EMPLOYES] qui est une clé étrangère sur la colonne [[Link]]. Ligne 71, l'annotation [@Column] a des
attributs pas encore rencontrés : updatable et insertable. Si on ne les met pas, on a une erreur à l'exécution qui dit qu'il y a un
conflit entre les champs [indemniteId] ligne 63 et [indemnite] ligne 59. Avant l'insertion du nouveau champ [indemniteId],
lorsqu'on persistait une entité [Employe] dans la table [EMPLOYES], la colonne [INDEMNITES_ID] de cette table était
initialisée avec la valeur du champ [[Link]]. Avec l'insertion du nouveau champ [indemniteId], la colonne
[INDEMNITES_ID] peut aussi être mise à jour par la valeur de ce champ. Il y a donc un conflit. On le lève en indiquant
ligne 62 par [insertable=false] que la colonne [INDEMNITES_ID] ne doit pas être mise à jour par la valeur du champ
[indemniteId] lors d'une opération INSERT. Le même raisonnement s'applique lorsqu'il y a modification d'une entité
[Employe] qui va se traduire par une opération SQL UPDATE sur la table [EMPLOYES]. On indique ligne 71 par
[updatable=false] que la colonne [INDEMNITES_ID] ne doit pas être mise à jour par la valeur du champ [indemniteId]
lors d'une opération UPDATE.
L'ajout d'un champ pour la clé étrangère [INDEMNITES_ID] est rendue nécessaire par le mode LAZY appliqué au
champ [Indemnite indemnite]. En effet, lorsqu'on récupère des employés par une opération JPQL [select e from Employe
e], nous récupérons des employés sans leurs indemnités. Avec le champ [long indemniteId] qui représente la clé primaire
de l'indemnité de l'employé, il devient possible de faire une requête pour obtenir l'indemnité complète d'un employé
lorsqu'on en a besoin ;
• lignes 86-90 : la méthode [toString] d'un employé. On remarquera ligne 89 qu'on n'affiche pas le champ [Indemnite
indemnite] mais seulement le chap [long indemniteId]. Ceci parce qu'on est toujour sûr d'avoir ce dernier champ, ce qui
n'est pas vrai pour le premier à cause de son mode LAZY ;
L'entité [Indemnite]
1. package jpa;
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
98/489
11.
12. @Entity
13. @Table(name = "indemnites")
14. public class Indemnite implements Serializable {
15.
16. private static final long serialVersionUID = 1L;
17.
18. // clé primaire
19. @Id
20. @GeneratedValue(strategy = [Link])
21.
22. @Column(name = "ID", nullable = false)
23. private Long id;
24.
25. // n° de version
26. @Column(name = "VERSION", nullable = false)
27. @Version
28. private int version;
29.
30. // indemnité journalière d'entretien
31. @Column(name = "ENTRETIEN_JOUR", nullable = false)
32. private double entretienJour;
33.
34. // indemnité journalière de repas
35. @Column(name = "REPAS_JOUR", nullable = false)
36. private double repasJour;
37.
38. // indice d'indemnités
39. @Column(name = "INDICE", nullable = false, unique = true)
40. private int indice;
41.
42. // indemnités de congés payés
43. @Column(name = "INDEMNITES_CP", nullable = false)
44. private double indemnitesCp;
45.
46. // tarif horaire
47. @Column(name = "BASE_HEURE", nullable = false)
48. private double baseHeure;
49.
50. public Indemnite() {
51. }
52.
53. public Indemnite(Long id) {
54. [Link] = id;
55. }
56.
57. public Indemnite(Long id, int version, int indice, double baseHeure, double entretienJour, double repasJour, double
indemnitesCp) {
58. [Link] = id;
59. [Link] = entretienJour;
60. [Link] = repasJour;
61. [Link] = indice;
62. [Link] = indemnitesCp;
63. [Link] = baseHeure;
64. [Link] = version;
65. }
66.
67. @Override
68. public String toString() {
69. return [Link]("Indemnite[id=%s, version=%s, indice=%s, base heure=%s, entretien jour=%s, repas jour=%s,
indemnités CP=%s",
70. id, version, indice, baseHeure, entretienJour, repasJour, indemnitesCp);
71. }
72.
73. // getters et setters --------------
74. ...
75.
76. // égalité d'instances ------------
77. @Override
78. public int hashCode() {
79. int hash = 0;
80. hash += (id != null ? [Link]() : 0);
81. return hash;
82. }
83.
84. @Override
85. public boolean equals(Object object) {
86. // TODO: Warning - this method won't work in the case the id fields are not set
87. if (!(object instanceof Indemnite)) {
88. return false;
89. }
90. Indemnite other = (Indemnite) object;
91. return !(([Link] == null && [Link] != null) || ([Link] != null && ));
92. }
93.
94. }
L'entité [Cotisation]
1. package jpa;
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
99/489
10. import [Link];
11.
12. @Entity
13. @Table(name = "cotisations")
14. public class Cotisation implements Serializable {
15.
16. private static final long serialVersionUID = 1L;
17.
18. // clé primaire
19. @Id
20. @GeneratedValue(strategy = [Link])
21.
22. @Column(name = "ID", nullable = false)
23. private Long id;
24.
25. // n° de version
26. @Column(name = "VERSION", nullable = false)
27. @Version
28. private int version;
29.
30. // cotisations sécurité sociale
31. @Column(name = "SECU", nullable = false)
32. private double secu;
33.
34. // cotisations retraite
35. @Column(name = "RETRAITE", nullable = false)
36. private double retraite;
37.
38. // cotisations csgd
39. @Column(name = "CSGD", nullable = false)
40. private double csgd;
41.
42. // cotisations csgrds
43. @Column(name = "CSGRDS", nullable = false)
44. private double csgrds;
45.
46. // constructeurs
47. public Cotisation() {
48. }
49.
50. public Cotisation(Long id) {
51. [Link] = id;
52. }
53.
54. public Cotisation(Long id, int version, double secu, double retraite, double csgd, double csgrds) {
55. [Link] = id;
56. [Link] = secu;
57. [Link] = retraite;
58. [Link] = csgd;
59. [Link] = csgrds;
60. [Link] = version;
61. }
62.
63. // toString
64. @Override
65. public String toString() {
66. return [Link]("Cotisations=[id=%s, version=%s, csgrds=%s, csgd=%s, secu=%s, retraite=%s", id, version, csgrds,
csgd, secu, retraite);
67. }
68.
69. // getters et setters
70. ...
71.
72. // égalité de deux instances ------------
73. @Override
74. public int hashCode() {
75. int hash = 0;
76. hash += (id != null ? [Link]() : 0);
77. return hash;
78. }
79.
80. @Override
81. public boolean equals(Object object) {
82. // TODO: Warning - this method won't work in the case the id fields are not set
83. if (!(object instanceof Cotisation)) {
84. return false;
85. }
86. Cotisation other = (Cotisation) object;
87. return !(([Link] == null && [Link] != null) || ([Link] != null && ));
88. }
89. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
100/489
Revenons à l'architecture de l'application :
7 Spring
Dans l'architecture ci-dessus, quelle interface doit offrir la couche [DAO] à la couche [metier] et quelle interface doit offrir la
couche [metier] à la couche [ui] ? Une première approche pour définir les interfaces des différentes couches est d'examiner les
différents cas d'usage (use cases) de l'application. Ici nous en avons deux, selon l'interface utilisateur choisie : console ou formulaire
graphique.
A partir de ces information et d'autres enregistrées dans des fichiers de configuration, l'application affiche les informations suivantes
:
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
101/489
• lignes 8-10 : les informations liées à l'employé dont on a donné le n° de sécurité sociale ;
• lignes 12-14 : les taux des différentes cotisations sociales ;
• lignes 16-17 : les différentes indemnités versées à l'assistante maternelle ;
• lignes 19-24 : les éléments de la feuille de salaire de l'assistante maternelle ;
Certaines informations doivent être fournies par la couche [metier] à la couche [ui] :
1. les informations liées à une assistante maternelle identifiée par son n° de sécurité sociale. On trouve ces informations dans
la table [EMPLOYES]. Cela permet d'afficher les lignes 6-8 ;
2. les montants des divers taux de cotisations sociales à prélever sur le salaire brut. On trouve ces informations dans la table
[COTISATIONS]. Cela permet d'afficher les lignes 10-12 ;
3. les montants des diverses indemnités liées à la fonction d'assistante maternelle. On trouve ces informations dans la table
[INDEMNITES]. Cela permet d'afficher les lignes 14-15 ;
4. les éléments constitutifs du salaire affichés lignes 18-22 ;
De ceci, on pourrait décider d'une première écriture de l'interface [IMetier] présentée par la couche [metier] à la couche [ui] :
1. package metier;
2.
3. public interface IMetier {
4. // obtenir la feuille de salaire
5. FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés );
6. }
• ligne 1 : les éléments de la couche [metier] sont mis dans le paquetage [metier] ;
• ligne 5 : la méthode [ calculerFeuilleSalaire ] prend pour paramètres les trois informations acquises par la couche [ui] et
rend un objet de type [FeuilleSalaire] contenant les informations que la couche [ui] affichera sur la console ;
• ligne 9 : l'employé concerné par la feuille de salaire - information n° 1 affichée par la couche [ui] ;
• ligne 10 : les différents taux de cotisation - information n° 2 affichée par la couche [ui] ;
• ligne 11 : les différentes indemnités liées à l'indice de l'employé - information n° 3 affichée par la couche [ui] ;
• ligne 12 : les éléments constitutifs de son salaire - information n° 4 affichée par la couche [ui] ;
On voit ci-dessus, que la liste déroulante [1, 2] présente tous les employés. Cette liste doit être demandée à la couche [métier].
L'interface de celle-ci évolue alors de la façon suivante :
1. package metier;
2.
3. import [Link];
4. import [Link];
5.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
102/489
6. public interface IMetier {
7. // obtenir la feuille de salaire
8. FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés );
9. // liste des employés
10. List<Employe> findAllEmployes();
11. }
• ligne [10] : la méthode qui va permettre à la couche [ui] de demander la liste de tous les employés à la couche [métier] ;
La couche [metier] ne peut initialiser les champs [Employe, Cotisation, Indemnite] de l'objet [FeuilleSalaire] ci-dessus qu'en
questionnant la couche [DAO] car ces informations sont dans les tables de la base de données. Il en est de même pour obtenir la
liste de tous les employés. On peut créer une interface [DAO] unique gérant l'accès aux trois entités [Employe, Cotisation,
Indemnite]. Nous décidons plutôt ici de créer une interface [DAO] par entité.
L'interface [DAO] pour les accès aux entités [Cotisation] de la table [COTISATIONS] sera la suivante :
1. package dao;
2.
3. import [Link];
4. import [Link];
5.
6. public interface ICotisationDao {
7. // créer une nouvelle cotisation
8. Cotisation create(Cotisation cotisation);
9. // modifier une cotisation existante
10. Cotisation edit(Cotisation cotisation);
11. // supprimer une cotisation existante
12. void destroy(Cotisation cotisation);
13. // chercher une cotisation particulière
14. Cotisation find(Long id);
15. // obtenir tous les objets Cotisation
16. List<Cotisation> findAll();
17.
18. }
• ligne 6, l'interface [ICotisationDao] gère les accès à l'entité [Cotisation] et donc à la table [COTISATIONS] de la base de
données. Notre application n'a besoin que de la méthode [findAll] de la ligne 16 qui permet de retrouver tout le contenu
de la table [COTISATIONS]. On a voulu ici se mettre dans un cas plus général où toutes les opérations CRUD (Create,
Read, Update, Delete) sont effectuées sur l'entité ;
• ligne 8 : la méthode [create] crée une nouvelle entité [Cotisation] ;
• ligne 10 : la méthode [edit] modifie une entité [Cotisation] existante ;
• ligne 12 : la méthode [destroy] supprime une entité [Cotisation] existante ;
• ligne 14 : la méthode [find] permet de retrouver une entité [Cotisation] existante via son identifiant id ;
• ligne 16 : la méthode [findAll] rend dans une liste toutes les entités [Cotisation] existantes ;
La méthode create a un paramètre cotisation de type Cotisation. Le paramètre cotisation doit être persisté, c.a.d. ici mis dans la table
[COTISATIONS]. Avant cette persistance, le paramètre cotisation a un identifiant id sans valeur. Après la persistance, le champ id a
une valeur qui est la clé primaire de l'enregistrement ajouté à la table [COTISATIONS]. Le paramètre cotisation est donc un
paramètre d'entrée / sortie de la méthode create. Il ne semble pas nécessaire que méthode create rende le paramètre cotisation comme
résultat. La méthode appelante détenant une référence sur l'objet [Cotisation cotisation], si celui-ci est modifié, elle a accès à l'objet
modifié puisqu'elle a une référence dessus. Elle peut donc connaître la valeur que la méthode create a donné au champ id de l'objet
[Cotisation cotisation]. La signature de la méthode pourrait donc être plus simplement :
1. // créer une nouvelle cotisation
2. void create(Cotisation cotisation);
Lorsqu'on écrit une interface, il est bon de se rappeler qu'elle peut être utilisée dans deux contextes différents : local et distant. Dans
le contexte local, la méthode appelante et la méthode appelée sont exécutées dans la même JVM :
JVM
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
103/489
Si la couche [metier] fait appel à la méthode create de la couche [DAO], elle a bien une référence sur le paramètre [Cotisation
cotisation] qu'elle passe à la méthode.
Dans le contexte distant, la méthode appelante et la méthode appelée sont exécutées dans des JVM différentes :
Ci-dessus, la couche [metier] s'exécute dans la JVM 1 et la couche [DAO] dans la JVM 2 sur deux machines différentes. Les deux
couches ne communiquent pas directement. Entre-elles s'intercale une couche qu'on appellera couche de communication [1]. Celle-
ci est composée d'une couche d'émission [2] et d'une couche de réception [3]. Le développeur n'a en général pas à écrire ces
couches de communication. Elles sont générées automatiquement par des outils logiciels. La couche [metier] est écrite comme si
elle s'exécutait dans la même JVM que la couche [DAO]. Il n'y a donc aucune modification de code.
• la couche [metier] fait appel à la méthode create de la couche [DAO] en lui passant le paramètre [Cotisation cotisation1]
• ce paramètre est en fait passé à la couche d'émission [2]. Celle-ci va transmettre sur le réseau, la valeur du paramètre
cotisation1 et non sa référence. La forme exacte de cette valeur dépend du protocole de communication utilisé ;
• la couche de réception [3] va récupérer cette valeur et reconstruire à partir d'elle un objet [Cotisation cotisation2] image du
paramètre initial envoyé par la couche [metier]. On a maintenant deux objets identiques (au sens de contenu) dans deux
JVM différentes : cotisation1 et cotisation2 ;
• la couche de réception va passer l'objet cotisation2 à la méthode create de la couche [DAO] qui va le persister en base de
données. Après cette opération, le champ id de l'objet cotisation2 a été initialisé par la clé primaire de l'enregistrement ajouté
à la table [COTISATIONS]. Ce n'est pas le cas de l'objet cotisation1 sur lequel la couche [metier] a une référence. Si on veut
que la couche [metier] ait une référence sur l'objet cotisation2, il faut le lui envoyer. Aussi est-on amenés à changer la
signature de la méthode create de la couche [DAO] :
1. // créer une nouvelle cotisation
2. Cotisation create(Cotisation cotisation);
• avec cette nouvelle signature, la méthode create va rendre comme résultat l'objet persisté cotisation2. Ce résultat est rendu à
la couche de réception [3] qui avait appelé la couche [DAO]. Celle-ci va rendre la valeur (et non la référence) de cotisation2
à la couche d'émission [2] ;
• la couche d'émission [2] va récupérer cette valeur et reconstruire à partir d'elle un objet [Cotisation cotisation3] image du
résultat rendu par la méthode create de la couche [DAO] ;
• l'objet [Cotisation cotisation3] est rendu à la méthode de la couche [metier] dont l'appel à la méthode create de la couche
[DAO] avait initié tout ce mécanisme. La couche [metier] peut donc connaître la valeur de clé primaire donné à l'objet
[Cotisation cotisation1] dont elle avait demandé la persistance : c'est la valeur du champ id de cotisation3 ;
L'architecture précédente n'est pas la plus courante. On trouve plus fréquemment les couches [metier] et [DAO] dans la même JVM
:
Dans cette architecture, ce sont les méthodes de la couche [metier] qui doivent rendre des résultats et non celles de la couche
[DAO]. Néanmoins la signature suivante de la méthode create de la couche [DAO] :
1. // créer une nouvelle cotisation
2. Cotisation create(Cotisation cotisation);
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
104/489
nous permet de ne pas faire d'hypothèses sur l'architecture réellement mise en place. Utiliser des signatures qui fonctionneront
quelque soit l'architecture retenue, locale ou distante, implique que dans le cas où une méthode appelée modifie certains de ses
paramètres :
• ceux-ci doivent faire également partie du résultat de la méthode appelée ;
• la méthode appelante doit utiliser le résultat de la méthode appelée et non les références des paramètres modifiés qu'elle a
transmis à la méthode appelée ;
On se laisse ainsi la possibilité de passer une couche d'une architecture locale à une architecture distante sans modification de code.
Réexaminons, à cette lumière, l'interface [ICotisationDao] :
1. package dao;
2.
3. import [Link];
4. import [Link];
5.
6. public interface ICotisationDao {
7. // créer une nouvelle cotisation
8. Cotisation create(Cotisation cotisation);
9. // modifier une cotisation existante
10. Cotisation edit(Cotisation cotisation);
11. // supprimer une cotisation existante
12. void destroy(Cotisation cotisation);
13. // chercher une cotisation particulière
14. Cotisation find(Long id);
15. // obtenir tous les objets Cotisation
16. List<Cotisation> findAll();
17. }
Au final, seule la signature de la méthode create doit être adaptée pour être utilisable dans le cadre d'une architecture distante. Les
raisonnements précédents seront valables pour les autres interfaces [DAO]. Nous ne les répèterons pas et utiliserons directement
des signatures utilisables aussi bien dans le cadre d'une architecture distante que locale.
L'interface [DAO] pour les accès aux entités [Indemnite] de la table [INDEMNITES] sera la suivante :
1. package dao;
2.
3. import [Link];
4. import [Link];
5.
6. public interface IIndemniteDao {
7. // créer une entité Indemnite
8. Indemnite create(Indemnite indemnite);
9. // modifier une entité Indemnite
10. Indemnite edit(Indemnite indemnite);
11. // supprimer une entité Indemnite
12. void destroy(Indemnite indemnite);
13. // rechercher une entité Indemnite via son identifiant
14. Indemnite find(Long id);
15. // obtenir toutes les entités Indemnite
16. List<Indemnite> findAll();
17. }
• ligne 6, l'interface [IIndemniteDao] gère les accès à l'entité [Indemnite] et donc à la table [INDEMNITES] de la base de
données. Notre application n'a besoin que de la méthode [findAll] de la ligne 16 qui permet de retrouver tout le contenu
de la table [INDEMNITES]. On a voulu ici se mettre dans un cas plus général où toutes les opérations CRUD (Create,
Read, Update, Delete) sont effectuées sur l'entité.
• ligne 8 : la méthode [create] crée une nouvelle entité [Indemnite]
• ligne 10 : la méthode [edit] modifie une entité [Indemnite] existante
• ligne 12 : la méthode [destroy] supprime une entité [Indemnite] existante
• ligne 14 : la méthode [find] permet de retrouver une entité [Indemnite] existante via son identifiant id
• ligne 16 : la méthode [findAll] rend dans une liste toutes les entités [Indemnite] existantes
L'interface [DAO] pour les accès aux entités [Employe] de la table [EMPLOYES] sera la suivante :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
105/489
1. package dao;
2.
3. import [Link];
4. import [Link];
5.
6. public interface IEmployeDao {
7.
8. // créer une nouvelle entité Employe
9. Employe create(Employe employe);
10. // modifier une entité Employe existante
11. Employe edit(Employe employe);
12. // supprimer une entité Employe
13. void destroy(Employe employe);
14. // chercher une entité Employe via son identifiant id
15. Employe find(Long id);
16. // chercher une entité Employe via son n° SS
17. Employe find(String SS);
18. // obtenir toutes les entités Employe
19. List<Employe> findAll();
20. }
• ligne 6, l'interface [IEmployeDao] gère les accès à l'entité [Employe] et donc à la table [EMPLOYES] de la base de
données. Notre application n'a besoin que de la méthode [findAll] de la ligne 19 qui permet de retrouver tout le contenu
de la table [EMPLOYES]. On a voulu ici se mettre dans un cas plus général où toutes les opérations CRUD (Create, Read,
Update, Delete) sont effectuées sur l'entité ;
• ligne 9 : la méthode [create] crée une nouvelle entité [Employe] ;
• ligne 11 : la méthode [edit] modifie une entité [Employe] existante ;
• ligne 13 : la méthode [destroy] supprime une entité [Employe] existante ;
• ligne 15 : la méthode [find] permet de retrouver une entité [Employe] existante via son identifiant id ;
• ligne 17 : la méthode [find(String SS)] permet de retrouver une entité [Employe] existante via son n° SS. Nous avons vu
que cette méthode était nécessaire à l'application console ;
• ligne 19 : la méthode [findAll] rend dans une liste toutes les entités [Employe] existantes. Nous avons vu que cette
méthode était nécessaire à l'application graphique.
La couche [DAO] va travailler avec l'API JDBC de Java. Cette API lance des exceptions contrôlées de type [SQLException] qui
présentent deux inconvénients :
• elles alourdissent le code qui doit obligatoirement gérer ces exceptions avec des try / catch ;
• elles doivent être déclarées dans la signature des méthodes de l'interface [IDao] par un "throws SQLException". Ceci a
pour conséquence d'empêcher l'implémentation de cette interface par des classes qui lanceraient une exception contrôlée
d'un type différent de [SQLException] ;
Pour remédier à ce problème, la couche [DAO] ne "remontera" que des exceptions non contrôlées de type [PamException].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
106/489
[JPA]Exception SQLException
PamException
Spring
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
107/489
36.
37. public void setCode(int code) {
38. [Link] = code;
39. }
40.
41. }
• ligne 6 : [PamException] dérive de [RuntimeException]. C'est donc un type d'exceptions que le compilateur ne nous oblige
pas à gérer par un try / catch ou à mettre dans la signature des méthodes. C'est pour cette raison, que [PamException]
n'est pas dans la signature des méthodes de l'interface [IDao]. Cela permet à cette interface d'être implémentée par une
classe lançant un autre type d'exceptions, pourvu que celui-ci dérive également de [RuntimeException] ;
• pour différencier les erreurs qui peuvent se produire, on utilise le code erreur de la ligne 9. Les trois constructeurs des
lignes 16, 21 et 26 sont ceux de la classe parente [RuntimeException] auxquels on a rajouté un paramètre : celui du code
d'erreur qu'on veut donner à l'exception ;
• la couche [DAO] encapsulera toute exception rencontrée, dans une exception de type [PamException], et relancera cette
dernière pour la couche [métier] ;
• la couche [métier] laissera remonter les exceptions lancées par la couche [DAO]. Elle encapsulera toute exception
survenant dans la couche [métier], dans une exception de type [PamException] et relancera cette dernière pour la couche
[ui] ;
• la couche [ui] intercepte toutes les exceptions qui remontent des couches [métier] et [DAO]. Elle se contentera d'afficher
l'exception sur la console ou l'interface graphique ;
7 Spring
[Link] Implémentation
Lectures conseillées : paragraphe 3.1.3 de [ref1], API JPA au paragraphe 1.3.3, page 32.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
108/489
Travail à faire : En utilisant l'intégration Spring / JPA, écrire les classes [CotisationDao, IndemniteDao, EmployeDao]
d'implémentation des interfaces [ICotisationDao, IIndemniteDao, IEmployeDao]. Chaque méthode de classe interceptera une
éventuelle exception et l'encapsulera dans une exception de type [PamException] avec un code d'erreur propre à l'exception
interceptée.
• ligne 6 : l'annotation Spring @Transactional. Elle indique à Spring que chaque méthode de la classe soit se dérouler dans
une transaction ;
• ligne 9 : l'annotation Spring @PersistenceContext injecte dans le champ em de la ligne 10, le gestionnaire du contexte de
persistance de la couche JPA ;
• lignes 17-24 : à l'intérieur d'une méthode de la couche [DAO], on utilise l'EntityManager de la ligne 10. Grâce à lui, on
fait des opérations de persistance (persist, merge, remove, createQuery). On relira le paragraphe 1.3.3, page 32, qui
présente l'API d'une couche JPA ;
• parce que la méthode se déroule dans une transaction, on est assuré qu'à la fin de la méthode, les modifications apportées
au contexte de persistance seront synchronisées avec la base de données ;
• on fera en sorte que le code de la méthode soit entouré d'un try / catch arrêtant toute éventuelle exception. On
encapsulera celle-ci dans un type [PamException] (ligne 22).
L'intégration DAO / JPA est configurée par le fichier Spring [[Link]] et le fichier JPA [[Link]] :
Travail à faire : écrire le contenu de ces deux fichiers. On supposera que la base de données utilisée est la base MySQL5
[dbpam_hibernate] générée par le script SQL [[Link]]. Le fichier Spring définira les trois beans suivants :
employeDao de type EmployeDao, indemniteDao de type IndemniteDao, cotisationDao de type CotisationDao. Par ailleurs, l'implémentation
JPA utilisée sera Hibernate.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
109/489
Note 1 : suivre le paragraphe 3.1.5 de [ref1]
[Link] Tests
Lectures conseillées : paragraphes 3.1.6 et 3.1.7 de [ref1]
Maintenant que la couche [DAO] est écrite et configurée, nous pouvons la tester. L'architecture des tests sera la suivante :
7 Spring
[Link].1 JUnitInitDB
Nous allons créer deux programmes de tests de la couche [DAO]. Ceux-ci seront placés dans le paquetage [dao] [2] de la branche
[Test Packages] [1] du projet Netbeans. Cette branche n'est pas incluse dans le projet généré par l'option [Build project], ce qui nous
assure que les programmes de tests que nous y plaçons ne seront pas inclus dans le .jar final du projet.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
110/489
Les classes placées dans la branche [Test Packages] ont connaissance des classes présentes dans la branche [Source Packages] ainsi
que des bibliothèques de classes du projet. Si les tests ont besoin de bibliothèques autres que celles du projet, celles-ci doivent être
déclarées dans la branche [Test Libraries] [2].
• ligne 20 : la méthode [init] est exécutée avant le début de la série des tests (annotation @BeforeClass). Elle instancie la
couche [DAO]. Elle doit être statique (ligne 20) ce qui entraîne que les champs qu'elle utilise doivent être également
statiques (lignes 15-17) ;
• ligne 38 : la méthode [clean] est exécutée avant chaque test (annotation @Before). Elle vide la base de données ;
• ligne 30 : la méthode [initDB] est un test (annotation @Test). C'est le seul. Un test doit contenir des instructions
d'assertion [Link]. Ici il n'y en aura aucune. La méthode est donc un faux test. Elle a pour rôle de
remplir la base de données avec quelques lignes puis d'afficher le contenu de la base sur la console. Ce sont les méthodes
create et findAll des couches [DAO] qui sont ici utilisées.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
111/489
Travail à faire : compléter le code de la classe [JUnitInitDB]. On s'aidera de l'exemple du paragraphe 3.1.6 de [ ref1]. Le code
génèrera le contenu présenté page 63.
Note : on utilisera les méthodes [create] des classes [DAO]. Parce que l'entité [Employe] embarque l'entité [Indemnite], il faut
d'abord créer ces dernières avant de créer les employés qui vont les référencer. Par ailleurs, pour persister une entité en base, il faut
qu'elle ait son id égal à null. En effet, c'est à cette caractéristique que la méthode [persist] de l'interface [EntityManager] sait qu'elle
doit persister ou non une entité en base. Par ailleurs, la version de l'entité créée sera mise à 1.
• puis exécutez le script SQL [dbpam_hibernate-[Link]] que vous trouverez dans le support du TD [5] : il crée la
base [dbpam_hibernate] avec des tables vides ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
112/489
• les classes [1], les fichiers de configuration [2] et les classes de test de la couche [DAO] [3] sont mis en place ;
La fenêtre [Output] contient les logs de l'exécution, ceux de Spring et ceux du test lui-même. Les affichages faits par la classe
[JUnitInitDB] sont les suivants :
1. Employés ----------------------
2. Employé=[id=496, version=1, prénom=Marie, nom=Jouveinal, adresse=5 rue des oiseaux, ville=St Corentin, code postal=49203]
3. Employé=[id=497, version=1, prénom=Justine, nom=Laverti, adresse=La brûlerie, ville=St Marcel, code postal=49014]
4. Indemnités ----------------------
5. Indemnite[id=642, version=1, indice=1, base heure=1.93, entretien jour=2.0, repas jour=3.0, indemnités CP=12.0
6. Indemnite[id=643, version=1, indice=2, base heure=2.1, entretien jour=2.1, repas jour=3.1, indemnités CP=15.0
7. Cotisations ----------------------
8. Cotisations=[id=74, version=1, csgrds=3.49, csgd=6.15, secu=9.39, retraite=7.88
Les tables [EMPLOYES, INDEMNITES, COTISATIONS] ont été remplies. On peut le vérifier avec une connexion Netbeans à la
base [dbpam_hibernate].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
113/489
• en [1-4], dans l'onglet [services], on visualise les données de la table [employes] de la connexion [dbpam_hibernate] [2] ;
• en [3] le résultat ;
[Link].3 JUnitDao
Note : la classe [JUnitDao] peut être trouvée dans le support de cours.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
114/489
25. // log
26. log("init");
27. // configuration de l'application
28. ApplicationContext ctx = new ClassPathXmlApplicationContext("[Link]");
29. // couches dao
30. employeDao = (IEmployeDao) [Link]("employeDao");
31. indemniteDao = (IIndemniteDao) [Link]("indemniteDao");
32. cotisationDao = (ICotisationDao) [Link]("cotisationDao");
33. }
34.
35. @AfterClass
36. public static void terminate() {
37. }
38.
39. @Before()
40. public void clean() {
41. // on vide la base
42. [Link]().stream().forEach((employe) -> {
43. [Link](employe);
44. });
45. [Link]().stream().forEach((cotisation) -> {
46. [Link](cotisation);
47. });
48. [Link]().stream().forEach((indemnite) -> {
49. [Link](indemnite);
50. });
51.
52. }
53.
54. // logs
55. private static void log(String message) {
56. [Link]("----------- " + message);
57. }
58.
59. // tests
60. @Test
61. public void test01() {
62. log("test01");
63. // liste des cotisations
64. List<Cotisation> cotisations = [Link]();
65. int nbCotisations = [Link]();
66. // on ajoute une cotisation
67. Cotisation cotisation = [Link](new Cotisation(null, 1, 9.39, 7.88, 6.15, 3.49));
68. // on la demande
69. cotisation = [Link]([Link]());
70. // vérification
71. [Link](cotisation);
72. [Link](3.49, [Link](), 1e-6);
73. [Link](6.15, [Link](), 1e-6);
74. [Link](9.39, [Link](), 1e-6);
75. [Link](7.88, [Link](), 1e-6);
76. // on la modifie
77. [Link](-1);
78. [Link](-1);
79. [Link](-1);
80. [Link](-1);
81. Cotisation cotisation2 = [Link](cotisation);
82. // vérifications
83. [Link]([Link]() + 1, [Link]());
84. [Link](-1, [Link](), 1e-6);
85. [Link](-1, [Link](), 1e-6);
86. [Link](-1, [Link](), 1e-6);
87. [Link](-1, [Link](), 1e-6);
88. // on demande l'élément modifié
89. Cotisation cotisation3 = [Link]([Link]());
90. // vérifications
91. [Link]([Link](), [Link]());
92. [Link](-1, [Link](), 1e-6);
93. [Link](-1, [Link](), 1e-6);
94. [Link](-1, [Link](), 1e-6);
95. [Link](-1, [Link](), 1e-6);
96. // on supprime l'élément
97. [Link](cotisation3);
98. // vérifications
99. Cotisation cotisation4 = [Link]([Link]());
100. [Link](cotisation4);
101. cotisations = [Link]();
102. [Link](nbCotisations, [Link]());
103. }
104.
105. @Test
106. public void test02() {
107. log("test02");
108. // on demande la liste des indemnités
109.
110. // on ajoute une Indemnite indemnite
111.
112. // on va chercher indemnite en base via sa clé primaire – on récupère indemnite1
113.
114. // on vérifie que indemnite1 = indemnite
115.
116. // on modifie l'indemnité obtenue et on persiste la modification en BD. On obtient indemnite2
117.
118. // on vérifie la version de indemnite2
119.
120. // on va chercher indemnite2 en base – on obtient indemnite3
121.
122. // on vérifie que indemnite3 = indemnite2
123.
124. // on supprime en base l'image de indemnite3
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
115/489
125.
126. // on va chercher indemnite3 en base
127. // on vérifie qu'on obtient une référence null
128.
129. }
130.
131. @Test
132. public void test03() {
133. log("test03");
134. // on répète un test analogue aux précédents pour Employe
135.
136. }
137.
138. @Test
139. public void test04() {
140. log("test04");
141. // on teste la méthode [IEmployeDao].find(String SS)
142. // d'abord avec un employé existant
143. // puis avec un employé inexistant
144.
145. }
146.
147. @Test
148. public void test05() {
149. log("test05");
150. // on crée deux indemnités avec le même indice
151. // enfreint la contrainte d'unicité de l'indice
152. // on vérifie qu'une exception de type PamException se produit
153. // et qu'elle a le n° d'erreur attendu
154.
155. }
156.
157. @Test
158. public void test06() {
159. log("test06");
160. // on crée deux employés avec le même n° SS
161. // enfreint la contrainte d'unicité sur le n° SS
162. // on vérifie qu'une exception de type PamException se produit
163. // et qu'elle a le n° d'erreur attendu
164.
165. }
166.
167. @Test
168. public void test07() {
169. log("test07");
170. // on crée deux employés avec le même n° SS, le 1er avec create, le 2ème avec edit
171. // enfreint la contrainte d'unicité sur le n° SS
172. // on vérifie qu'une exception de type PamException se produit
173. // et qu'elle a le n° d'erreur attendu
174.
175. }
176.
177. @Test
178. public void test08() {
179. [Link]("test08");
180. log("test08");
181. // avec Hibernate (pas EclipseLink) supprimer un employé qui n'existe pas ne provoque pas d'exception
182. // il est ajouté puis détruit – on le vérifie
183.
184. }
185.
186. @Test
187. public void test09() {
188. log("test09");
189. // modifier un employé sans avoir la bonne version doit provoquer une exception
190. // on le vérifie
191.
192. }
193.
194. @Test
195. public void test10() {
196. log("test10");
197. // supprimer un employé sans avoir la bonne version doit provoquer une exception
198. // on le vérifie
199.
200. }
201.
202. @Test
203. public void test11() {
204. // sert à tester la méthode clean
205. }
206. }
Dans la classe de tests précédente, la base est vidée avant chaque test.
En procédant de la même façon que pour la classe de tests [JUnitInitDB], on obtient les résultats suivants :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
116/489
• en [1], on exécute la classe de tests ;
• en [2], les résultats des tests dans la fenêtre [Test Results] ;
Provoquons une erreur pour voir comment cela est signalé dans la page des résultats :
1. @Test
2. public void test01() {
3. log("test01");
4. // liste des cotisations
5. List<Cotisation> cotisations = [Link]();
6. int nbCotisations = [Link]();
7. // on ajoute une cotisation
8. Cotisation cotisation = [Link](new Cotisation(3.49, 6.15, 9.39, 7.88));
9. // on la demande
10. cotisation = [Link]([Link]());
11. // vérification
12. [Link](cotisation);
13. [Link](0, [Link](), 1e-6);
14. [Link](6.15, [Link](), 1e-6);
15. [Link](9.39, [Link](), 1e-6);
16. [Link](7.88, [Link](), 1e-6);
17. // on la modifie
18. ....
19. }
Ligne 13, l'assertion va provoquer une erreur, la valeur de [Csgrds] étant 3.49 (ligne 8). L'exécution de la classe de tests donne les
résultats suivants :
• la page des résultats [1] montre maintenant qu'il y a eu des tests non réussis ;
• en [2], un résumé de l'exception qui a fait échouer le test. On y trouve le n° de la ligne du code Java où s'est produite
l'exception ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
117/489
1.4.9 La couche [métier] de l'application [PAM]
Maintenant que la couche [DAO] a été écrite, nous passons à l'étude de la couche métier [2] :
7 Spring
Le paquetage [metier] comprendra, outre l'interface [IMetier] et son implémentation [Metier], deux autres classes [FeuilleSalaire] et
[ElementsSalaire]. La classe [FeuilleSalaire] a été brièvement présentée page 102. Nous revenons dessus maintenant.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
118/489
23.
24. // toString
25. @Override
26. public String toString() {
27. return [Link]("FeuilleSalaire[%s, %s, ,%s]", employe, cotisation, elementsSalaire);
28. }
29.
30. // accesseurs
31. ...
32. }
• ligne 7 : la classe implémente l'interface Serializable parce que ses instances sont susceptibles d'être échangées sur le réseau ;
• ligne 9 : l'employé concerné par la feuille de salaire ;
• ligne 10 : les différents taux de cotisation ;
• ligne 11 : les différentes indemnités liées à l'indice de l'employé ;
• ligne 12 : les éléments constitutifs de son salaire ;
• lignes 14-22 : les deux constructeurs de la classe ;
• lignes 25-27 : méthode [toString] identifiant un objet [FeuilleSalaire] particulier ;
• lignes 30 et au-delà : les accesseurs publics aux champs privés de la classe ;
La classe [ElementsSalaire] référencée ligne 11 de la classe [FeuilleSalaire] ci-dessus, rassemble les éléments constituant une fiche de
paie. Sa définition est la suivante :
1. package metier;
2.
3. import [Link];
4.
5. public class ElementsSalaire implements Serializable {
6.
7. // champs privés
8. private double salaireBase;
9. private double cotisationsSociales;
10. private double indemnitesEntretien;
11. private double indemnitesRepas;
12. private double salaireNet;
13.
14. // constructeurs
15. public ElementsSalaire() {
16.
17. }
18.
19. public ElementsSalaire(double salaireBase, double cotisationsSociales, double indemnitesEntretien, double
indemnitesRepas, double salaireNet) {
20. [Link] = salaireBase;
21. [Link] = cotisationsSociales;
22. [Link] = indemnitesEntretien;
23. [Link] = indemnitesRepas;
24. [Link] = salaireNet;
25. }
26.
27. // toString
28. @Override
29. public String toString() {
30. return [Link]("[salaire base=%s, cotisations sociales=%s, indemnités d'entretien=%s, indemnités de repas=
%s,salaire net=%s]",
31. salaireBase, cotisationsSociales, indemnitesEntretien, indemnitesRepas, salaireNet);
32. }
33.
34. // getters et setters
35. ...
36.
37. }
• ligne 5 : la classe implémente l'interface Serializable parce qu'elle est un composant de la classe FeuilleSalaire qui doit être
sérialisable.
• ligne 8 : le salaire de base
• ligne 9 : les cotisations sociales payées sur ce salaire de base
• ligne 10 : les indemnités journalières d'entretien de l'enfant
• ligne 11 : les indemnités journalières de repas de l'enfant
• ligne 12 : le salaire net à payer à l'assistante maternelle
• lignes 15-25 : les constructeurs de la classe
• lignes 28-32 : méthode [toString] identifiant un objet [ElementsSalaire] particulier
• lignes 34 et au-delà : les accesseurs publics aux champs privés de la classe
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
119/489
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11.
12. @Transactional
13. public class Metier implements IMetier {
14.
15. // références sur la couche [dao]
16. private ICotisationDao cotisationDao = null;
17. private IEmployeDao employeDao = null;
18.
19. // obtenir la feuille de salaire
20. @Override
21. public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés) {
22. ...
23. }
24.
25. // liste des employés
26. @Override
27. public List<Employe> findAllEmployes() {
28. ...
29. }
30.
31. // getters et setters
32. ...
33.
34. }
• ligne 12 : l'annotation Spring @Transactional fait que chaque méthode de la classe se déroulera au sein d'une transaction.
On avait déjà mis cette annotation sur les classes de la couche [DAO]. Lorsqu'une méthode de la couche [métier] appelle
une méthode de la couche [DAO], il n'y pas de nouvelle transaction créée. La méthode de la couche [DAO] utilise celle
créée par la couche [métier]. Au final, la durée de vie du contexte de persistance JPA est celle de la transaction de la
couche [métier] et non plus celle de la transaction de la couche [DAO] ;
• lignes 16-17 : les références sur les couches [DAO] des entités [Cotisation, Employe] ;
• lignes 20-23 : la méthode [calculerFeuilleSalaire] ;
• lignes 26-29 : la méthode [findAllEmployes] ;
• ligne 31 et au-delà : les accesseurs publics des champs privés de la classe.
Les classes de tests sont créées dans un paquetage [metier] [2] de la branche [Test Packages] [1] du projet.
Pour faire les tests, on mettra en commentaires dans le fichier [[Link]] les lignes qui font que les tables de la
base de données sont recréées à chaque nouvelle exécution du projet :
1. <!-- configuration JPA -->
2. <bean id="entityManagerFactory" class="[Link]">
3. <property name="dataSource" ref="dataSource"/>
4. <property name="jpaVendorAdapter">
5. <bean class="[Link]">
6. <property name="databasePlatform" value="[Link].MySQL5InnoDBDialect"/>
7. </bean>
8. </property>
9. <!-- <property name="jpaProperties">
10. <props>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
120/489
11. <prop key="[Link]">drop-and-create</prop>
12. <prop key="hibernate.show_sql">TRUE</prop>
13. <prop key="hibernate.format_sql">TRUE</prop>
14. <prop key="use_sql_comments">TRUE</prop>
15. </props>
16. </property> -->
17. <property name="loadTimeWeaver">
18. <bean class="[Link]"/>
19. </property>
20. </bean>
Il n'y a pas d'assertion [Link] dans la classe. On cherche simplement à calculer quelques salaires afin de les
vérifier ensuite à la main. L'affichage écran obtenu par l'exécution de la classe précédente est le suivant :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
121/489
1. FeuilleSalaire[Employé=[id=552, version=1, prénom=Justine, nom=Laverti, adresse=La brûlerie, ville=St Marcel, code
postal=49014, indemniteId=717], Cotisations=[id=88, version=1, csgrds=3.49, csgd=6.15, secu=9.39, retraite=7.88, ,[salaire
base=64.85, cotisations sociales=17.45, indemnités d'entretien=10.0, indemnités de repas=15.0,salaire net=72.4]]
2. FeuilleSalaire[Employé=[id=551, version=1, prénom=Marie, nom=Jouveinal, adresse=5 rue des oiseaux, ville=St Corentin, code
postal=49203, indemniteId=718], Cotisations=[id=88, version=1, csgrds=3.49, csgd=6.15, secu=9.39, retraite=7.88, ,[salaire
base=362.25, cotisations sociales=97.48, indemnités d'entretien=42.0, indemnités de repas=62.0,salaire net=368.77]]
3. PamException[Code=101, message=L'employé de n°[xx] est introuvable]
Travail à faire : la ligne 27 de [JUnitMetier_1] utilise le bean Spring nommé metier. Donner la définition de ce bean dans le fichier
[[Link]]. Ce fichier est identique au fichier Spring utilisé pour tester la couche [DAO]. On y ajoute
simplement la définition du bean de la couche [métier].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
122/489
La classe [JUnitMetier_2] est une copie de la classe [JUnitMetier_1] où cette fois, des assertions ont été placées dans la méthode
test01.
Lors de l'exécution de la classe [JUnitMetier_2], on obtient les résultats suivants si tout va bien :
7 Spring
Nous créerons deux implémentations différentes de la couche [ui] : une version console et une version graphique swing :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
123/489
21. // le second paramètre doit être un nombre réel >0
22.
23. // erreur ?
24. if (argumentErroné) {
25. [Link]([Link]("Le nombre d'heures travaillées [%s] est erroné", args[1]));
26. }
27. // le troisième paramètre doit être un nombre entier >0
28.
29. // erreur ?
30. if (argumentErroné) {
31. [Link]([Link]("Le nombre de jours travaillés [%s] est erroné", args[2]));
32. }
33. // des erreurs ?
34.
35. // c'est bon - on peut demander la feuille de salaire
36. FeuilleSalaire feuilleSalaire;
37. try {
38. // instanciation couche [metier]
39.
40. // calcul de la feuille de salaire
41.
42. } catch (PamException ex) {
43. [Link]([Link]("L'erreur suivante s'est produite : %s", [Link]()));
44. return;
45. } catch (Exception ex) {
46. [Link]([Link]("L'erreur suivante s'est produite : %s", ex));
47. return;
48. }
49. // affichage détaillé
50. String output = "Valeurs saisies :\n";
51. output += ajouteInfo("N° de sécurité sociale de l'employé", args[0]);
52. output += ajouteInfo("Nombre d'heures travaillées", args[1]);
53. output += ajouteInfo("Nombre de jours travaillés", args[2]);
54. output += ajouteInfo("\nInformations Employé", "");
55. output += ajouteInfo("Nom", [Link]().getNom());
56. output += ajouteInfo("Prénom", [Link]().getPrenom());
57. output += ajouteInfo("Adresse", [Link]().getAdresse());
58. output += ajouteInfo("Ville", [Link]().getVille());
59. output += ajouteInfo("Code Postal", [Link]().getCp());
60. output += ajouteInfo("Indice", "" + [Link]().getIndemnite().getIndice());
61. output += ajouteInfo("\nInformations Cotisations", "");
62. output += ajouteInfo("CSGRDS", "" + [Link]().getCsgrds() + " %");
63. output += ajouteInfo("CSGD", "" + [Link]().getCsgd() + " %");
64. output += ajouteInfo("Retraite", "" + [Link]().getRetraite() + " %");
65. output += ajouteInfo("Sécurité sociale", "" + [Link]().getSecu() + " %");
66. output += ajouteInfo("\nInformations Indemnités", "");
67. output += ajouteInfo("Salaire horaire", "" + [Link]().getIndemnite().getBaseHeure() + " euro");
68. output += ajouteInfo("Entretien/jour", "" + [Link]().getIndemnite().getEntretienJour() + " euro");
69. output += ajouteInfo("Repas/jour", "" + [Link]().getIndemnite().getRepasJour() + " euro");
70. output += ajouteInfo("Congés Payés", "" + [Link]().getIndemnite().getIndemnitesCp() + " %");
71. output += ajouteInfo("\nInformations Salaire", "");
72. output += ajouteInfo("Salaire de base", "" + [Link]().getSalaireBase() + " euro");
73. output += ajouteInfo("Cotisations sociales", "" + [Link]().getCotisationsSociales() + "
euro");
74. output += ajouteInfo("Indemnités d'entretien", "" + [Link]().getIndemnitesEntretien() + "
euro");
75. output += ajouteInfo("Indemnités de repas", "" + [Link]().getIndemnitesRepas() + " euro");
76. output += ajouteInfo("Salaire net", "" + [Link]().getSalaireNet() + " euro");
77.
78. [Link](output);
79. }
80.
81. static String ajouteInfo(String message, String valeur) {
82. return [Link]("%s : %s\n", message, valeur);
83. }
84. }
[Link] Exécution
Pour exécuter la classe [[Link]], on procèdera de la façon suivante :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
124/489
• en [1], sélectionner les propriétés du projet,
• en [2], sélectionner la propriété [Run] du projet,
• utiliser le bouton [3] pour désigner la classe (dite classe principale) à exécuter,
• celle-ci a besoin de trois arguments pour s'exécuter (n° SS, nombre d'heures travaillées, nombre de jours travaillés). Ces
arguments sont placés en [5],
• ceci fait, on peut exécuter le projet [6]. La configuration précédente fait que c'est la classe [[Link]] qui va être
exécutée.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
125/489
1.4.11 La couche [ui] de l'application [PAM] – version graphique
Nous implémentons maintenant la couche [ui] avec une interface graphique :
7 Spring
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
126/489
• en [1], la classe [PamJFrame] de l'interface graphique ;
• en[2] : l'interface graphique ;
• [11] : la liste des composants Swing disponibles pour un formulaire est trouvée dans la fenêtre [Palette] ;
• [12] : la fenêtre [Inspector] présente l'arborescence des composants du formulaire. Les composants ayant une
représentation visuelle se retrouveront dans la branche [JFrame], les autres dans la branche [Other Components] ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
127/489
• en [13], nous sélectionnons un composant [JLabel] par un clic simple ;
• en [14], nous le déposons sur le formulaire en mode [Design] ;
• en [15], nous définissons les propriétés du JLabel (text, font) ;
• en [20] et [21] : dans la perspective [Source] du formulaire, du code Java a été ajouté pour gérer le JLabel ajouté ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
128/489
Un tutoriel sur la construction de formulaires avec Netbeans est disponible à l'url
[[Link]
JLabel1
JPanel1
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
129/489
JPanel2
JPanel3
JPanel4
JPanel5
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
130/489
Nous gèrerons le clic sur le bouton [jButtonSalaire]. Pour créer la méthode de gestion de cet événement, on pourra procéder
comme suit en mode [Design] de l'interface graphique [1-4]:
Le code Java qui associe la méthode précédente au clic sur le bouton [JButtonSalaire] est lui aussi généré :
1. [Link]("Salaire");
2. [Link](new [Link]() {
3. public void actionPerformed([Link] evt) {
4. jButtonSalaireActionPerformed(evt);
5. }
6. });
Ce sont les lignes 2-5 qui indiquent que le clic (evt de type ActionPerformed) sur le bouton [jButtonSalaire] (ligne 2) doit être géré par
la méthode [jButtonSalaireActionPerformed] (ligne 4).
Nous gèrerons également, l'événement [caretUpdate] (déplacement du curseur de saisie) sur le champ de saisie [jTextFieldHT]. Pour
créer le gestionnaire de cet événement, nous procédons comme précédemment :
Le code Java qui associe la méthode précédente à l'événement [caretUpdate] sur le champ de saisie [jTextFieldHT] est lui aussi
généré :
1. [Link](new [Link]() {
2. public void caretUpdate([Link] evt) {
3. jTextFieldHTCaretUpdate(evt);
4. }
5. });
Les lignes 1-4 indiquent que l'événement [caretUpdate] (ligne 2) sur le bouton [jTextFieldHT] (ligne 1) doit être géré par la méthode
[ jTextFieldHTCaretUpdate] (ligne 3).
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
131/489
Couche Couche Couche Objets image
Interface Implémentation Couche
[ui] [metier] [DAO] de la BD
[JPA] [Hibernate] [JDBC]
7 Spring
La couche [ui] a besoin d'une référence sur la couche [metier]. Rappelons comment cette référence avait été obtenue dans
l'application console :
1. // instanciation couche [metier]
2. ApplicationContext ctx = new ClassPathXmlApplicationContext("[Link]");
3. IMetier metier = (IMetier) [Link]("metier");
La méthode est la même dans l'application graphique. Il faut que lorsque celle-ci s'initialise, la référence [IMetier metier] de la ligne
3 ci-dessus soit également initialisée. Le code généré pour l'interface graphique est pour l'instant le suivant :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
12.
13. public class PamJFrame extends [Link] {
14.
15. /** Creates new form PamJFrame */
16. public PamJFrame() {
17. initComponents();
18. }
19.
20. /** This method is called from within the constructor to
21. * initialize the form.
22. * WARNING: Do NOT modify this code. The content of this method is
23. * always regenerated by the Form Editor.
24. */
25. // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
26. private void initComponents() {
27. ...
28. }// </editor-fold>//GEN-END:initComponents
29.
30. private void jTextFieldHTCaretUpdate([Link] evt) {//GEN-FIRST:event_jTextFieldHTCaretUpdate
31. ...
32.
33. }//GEN-LAST:event_jTextFieldHTCaretUpdate
34.
35. private void jButtonSalaireActionPerformed([Link] evt) {//GEN-
FIRST:event_jButtonSalaireActionPerformed
36. ...
37. }//GEN-LAST:event_jButtonSalaireActionPerformed
38.
39. /**
40. * @param args the command line arguments
41. */
42. public static void main(String args[]) {
43. [Link](() -> {
44. new PamJFrame().setVisible(true);
45. });
46. }
47.
48. // Variables declaration - do not modify//GEN-BEGIN:variables
49. private [Link] jButtonSalaire;
50. ...
51. // End of variables declaration//GEN-END:variables
52.
53. // variables d'instance'
54. private IMetier metier=null;
55. private List<Employe> employes=null;
56. private String[] employesCombo=null;
57. private double heuresTravaillées=0;
58. ..
59. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
132/489
• ligne 30 : la méthode qui va gérer le déplacement du curseur de saisie dans le champ [jTextFieldHT] ;
• ligne 35 : la méthode qui va gérer le clic sur le bouton [jButtonSalaire] ;
Pour ajouter au code précédent nos propres initialisations, nous pouvons procéder comme suit :
1. /** Creates new form PamJFrame */
2. public PamJFrame() {
3. initComponents();
4. doMyInit();
5. }
6. ...
7.
8. // variables d'instance
9. private IMetier metier=null;
10. private List<Employe> employes=null;
11. private String[] employesCombo=null;
12. private double heuresTravaillées=0;
13.
14. // initialisations propriétaires
15. public final void doMyInit(){
16. // init contexte
17. try{
18. // instanciation couche [metier]
19.
20. // on récupère la liste des employés
21. }catch (PamException ex){
22. [Link]([Link]());
23. return;
24. }
25. // composants
26. // bouton salaire inhibé
27.
28. // textAreaStatus visible
29.
30. // spinner jours travaillés initialisé
31. [Link](new SpinnerNumberModel(0,0,31,1));
32. // combobox employés rempli
33. employesCombo=new String[[Link]()];
34. int i=0;
35. for(Employe employe : employes){
36. employesCombo[i++]=[Link]()+" "+[Link]();
37. }
38. [Link](new DefaultComboBoxModel(employesCombo));
39. }
• ligne 4 : on appelle une méthode propriétaire pour faire nos propres initialisations. Celles-ci sont définies par le code des
lignes 15-39 ;
Travail à faire : en vous aidant des commentaires, compléter le code de la procédure [doMyInit].
Travail à faire : écrire la méthode [jButtonSalaireActionPerformed] qui doit afficher la feuille de salaire de l'employé sélectionné
dans [jComboBoxEmployes].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
133/489
Le projet doit être complet avec ses fichiers de configuration ([Link], [Link]) et la classe de
l'interface graphique. On lancera Le SGBD cible avant d'exécuter le projet.
7 Spring
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
134/489
Le projet [5] doit être modifié en deux points pour l'adapter à la nouvelle couche JPA / EclipseLink :
1. en [11], les fichiers de configuration de Spring doivent être modifiés. On y trouve en effet la configuration de la couche
JPA ;
2. en [12], les bibliothèques du projet doivent être modifiées : celles d'Hibernate doivent être remplacées par celles de
EclipseLink.
Commençons par ce dernier point. Le fichier [[Link]] pour le nouveau projet sera celui-ci :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <project xmlns="[Link] xmlns:xsi="[Link]
3. xsi:schemaLocation="[Link] [Link]
4.
5. <!-- artifact -->
6. <modelVersion>4.0.0</modelVersion>
7. <groupId>[Link]</groupId>
8. <artifactId>mv-pam-spring-eclipselink</artifactId>
9. <version>1.0-SNAPSHOT</version>
10. <packaging>jar</packaging>
11. <name>mv-pam-spring-eclipselink</name>
12.
13. <dependencies>
14. <!-- eclipselink -->
15. <dependency>
16. <groupId>[Link]</groupId>
17. <artifactId>eclipselink</artifactId>
18. <version>2.6.3</version>
19. </dependency>
20. <!-- mysql -->
21. ...
22. <!-- JUnit -->
23. ...
24. <!-- pool de connexions DBCP -->
25. ...
26. <!-- Spring framework -->
27. ...
28. <!-- interfaces swing -->
29. ...
30. </dependencies>
31.
32. <!-- plugins EclipseLink -->
33. <build>
34. <plugins>
35. <!--[[Link] -->
36. <!-- This plugin ensures the EclipseLink static weaving -->
37. <plugin>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
135/489
38. <artifactId>staticweave-maven-plugin</artifactId>
39. <groupId>[Link]</groupId>
40. <version>1.0.0</version>
41. <executions>
42. <execution>
43. <goals>
44. <goal>weave</goal>
45. </goals>
46. <phase>process-classes</phase>
47. <configuration>
48. <logLevel>ALL</logLevel>
49. <includeProjectClasspath>true</includeProjectClasspath>
50. </configuration>
51. </execution>
52. </executions>
53. <dependencies>
54. <dependency>
55. <groupId>[Link]</groupId>
56. <artifactId>eclipselink</artifactId>
57. <version>2.6.3</version>
58. </dependency>
59. </dependencies>
60. </plugin>
61. </plugins>
62. <pluginManagement>
63. <plugins>
64. <!-- This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build
itself. -->
65. <plugin>
66. <groupId>[Link].m2e</groupId>
67. <artifactId>lifecycle-mapping</artifactId>
68. <version>1.0.0</version>
69. <configuration>
70. <lifecycleMappingMetadata>
71. <pluginExecutions>
72. <pluginExecution>
73. <pluginExecutionFilter>
74. <groupId>
75. [Link]
76. </groupId>
77. <artifactId>
78. staticweave-maven-plugin
79. </artifactId>
80. <versionRange>
81. [1.0.0,)
82. </versionRange>
83. <goals>
84. <goal>weave</goal>
85. </goals>
86. </pluginExecutionFilter>
87. <action>
88. <execute>
89. <runOnIncremental>true</runOnIncremental>
90. </execute>
91. </action>
92. </pluginExecution>
93. </pluginExecutions>
94. </lifecycleMappingMetadata>
95. </configuration>
96. </plugin>
97. </plugins>
98. </pluginManagement>
99. </build>
100.
101. <properties>
102. <[Link]>UTF-8</[Link]>
103. <[Link]>1.8</[Link]>
104. <[Link]>1.8</[Link]>
105. </properties>
106. </project>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
136/489
17. <property name="loadTimeWeaver">
18. <bean class="[Link]"/>
19. </property>
20. </bean>
Les lignes 1-20 configurent la couche JPA. L'implémentation JPA utilisée est Hibernate (ligne 5). Pour passer à une implémentation
JPA / EclipseLink, les lignes ci-dessus sont remplacées par les lignes ci-dessous :
1. <!-- configuration JPA -->
2. <bean id="entityManagerFactory" class="[Link]">
3. <property name="dataSource" ref="dataSource"/>
4. <property name="jpaVendorAdapter">
5. <bean class="[Link]">
6. <property name="databasePlatform" value="[Link]" />
7. </bean>
8. </property>
9. <property name="jpaProperties">
10. <props>
11. <prop key="[Link]">drop-and-create</prop>
12. <prop key="[Link]">FINE</prop>
13. <prop key="[Link]">static</prop>
14. <prop key="[Link]">true</prop>
15. <prop key="[Link]">true</prop>
16. </props>
17. </property>
18. </bean>
Nous construisons le projet (Clean and Build) puis nous exécutons le test [JUnitInitDB] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
137/489
On peut remarquer, lignes 2 et 3 qu'on n'a pas récupéré la clé étrangère [indemniteId] des deux employés alors qu'avec Hibernate
on les avait. On retrouve de nouveau une différence de résultats selon l'implémentation JPA. Ici, le fait qu'on n'ait pas pu obtenir la
clé étrangère [indemniteId] des employés n'a pas d'importance. Au paragraphe [Link], page 89, nous avons vu qu'EclipseLink
retrouvait l'entité [Indemnite [Link]] dès que ce champ de l'entité [Employe] était référencé dans le code. Si nous
changeons la méthode [[Link]] de la façon suivante :
1. // toString
2. @Override
3. public String toString() {
4. return [Link]("Employé=[id=%s, version=%s, prénom=%s, nom=%s, adresse=%s, ville=%s, code postal=%s,
[Link]=%s]",
5. id, version, prenom, nom, adresse, ville, cp, [Link]());
6. }
[Link].1 JUnitDao
Note : la classe [JUnitDao] est disponible dans le support de cours.
Le changement d'implémentation JPA n'influe en rien sur les couches [metier] et [ui] et donc si ces couches fonctionnaient avec
Hibernate, elles fonctionneront avec EclipseLink à quelques précautions près : il ne faut pas que la couche [DAO] lance des
exceptions propres à l'implémentation JPA utilisée. Celles-ci doivent être encapsulées dans des exceptions propres à l'application.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
138/489
1.5 Version 2 : Architecture OpenEJB / JPA
Nous présentons ici les principes qui vont gouverner le portage d'une application JPA / Spring / Hibernate vers une application
JPA / OpenEJB / EclipseLink. On attendra le paragraphe 1.5.2, page 143 pour créer les projets Maven.
71 Spring
1 OpenEjb
Dans cette implémentation, la couche [ui] sera un client local de la couche [métier].
1 OpenEjb
Dans cette implémentation, la couche [ui] sera un client distant de la couche [métier].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
139/489
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.1"
3. xmlns="[Link]
4. xmlns:xsi="[Link]
5. xsi:schemaLocation="[Link]
[Link]
6. <persistence-unit name="mv-pam-jpa-eclipselinkPU" transaction-type="JTA">
7. <provider>[Link]</provider>
8. <class>[Link]</class>
9. <class>[Link]</class>
10. <class>[Link]</class>
11. <properties>
12. <property name="[Link]" value="drop-and-create"/>
13. <property name="[Link]" value="FINE"/>
14. </properties>
15. </persistence-unit>
16. </persistence>
• ligne 5 : les transactions dans un conteneur EJB sont de type JTA (Java Transaction API). Elles étaient de type
RESOURCE_LOCAL avec Spring ;
• ligne 7 : l'implémentation JPA utilisée est EclipseLink
• lignes 8-10 : les entités gérées par la couche JPA
• lignes 11-14 : propriétés du provider EclipseLink
• ligne 12 : à chaque exécution, les tables seront créées
Les caractéristiques JDBC de la source de données JTA utilisée par le conteneur OpenEJB seront précisées par le fichier
de configuration [conf/[Link]] suivant :
1. <?xml version="1.0"?>
2. <openejb>
3. <Resource id="Default JDBC Database">
4. JdbcDriver [Link]
5. JdbcUrl jdbc:mysql://localhost:3306/dbpam_hibernate
6. UserName root
7. </Resource>
8. </openejb>
• ligne 3 : on utilise l'id Default JDBC Database lorsqu'on travaille avec un conteneur OpenEJB embarqué (embedded)
dans l'application elle-même ;
• ligne 5 : nous utilisons le base MySQL [dbpam_hibernate] ;
L'EJB va implémenter cette même interface sous deux formes différentes : une locale et une distante. L'interface locale
peut être utilisée par un client s'exécutant dans la même JVM, l'interface distante par un client s'exécutant dans une autre
JVM.
L'interface locale :
1. package dao;
2.
3. import [Link];
4.
5. @Local
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
140/489
6. public interface ICotisationDaoLocal extends ICotisationDao {
7. }
• ligne 6 : l'interface [ICotisationDaoLocal] hérite de l'interface [ICotisationDao] pour en reprendre toutes les méthodes.
Elle n'en ajoute pas de nouvelles ;
• ligne 5 : l'annotation @Local en fait une interface locale pour l'EJB qui l'implémentera ;
L'interface distante :
1. package dao;
2.
3. import [Link];
4.
5. @Remote
6. public interface ICotisationDaoRemote extends ICotisationDao {
7. }
• ligne 6 : l'interface [ICotisationDaoRemote] hérite de l'interface [ICotisationDao] pour en reprendre toutes les méthodes.
Elle n'en ajoute pas de nouvelles.
• ligne 5 : l'annotation @Remote en fait une interface distante pour l'EJB qui l'implémentera.
La couche [DAO] est implémentée par un EJB implémentant les deux interfaces (ce n'est pas obligatoire) :
1. @Stateless
2. @TransactionAttribute([Link])
3. public class CotisationDao implements ICotisationDaoLocal, ICotisationDaoRemote {
4.
5. @PersistenceContext
6. private EntityManager em;
Lorsque l'interface locale de la couche [DAO] est utilisée, le client de cette interface s'exécute dans la même JVM.
JVM
Ci-dessus, les couches [metier] et [DAO] échangent des objets par référence. Lorsqu'une couche change l'objet partagé,
l'autre couche voit ce changement.
Lorsque l'interface distante de la couche [DAO] est utilisée, le client de cette interface s'exécute habituellement dans
une autre JVM.
Ci-dessus, les couches [metier] et [DAO] échangent des objets par valeur (sérialisation / désérialisation de l'objet
échangé). Lorsqu'une couche change un objet partagé, l'autre couche ne voit ce changement que si l'objet modifié lui
est renvoyé.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
141/489
[Link] Implémentation de la couche [metier] par un EJB
La classe implémentant la couche [metier] devient elle aussi un EJB implémentant une interface locale et distante. L'interface
initiale [IMetier] était la suivante :
1. package metier;
2.
3. import [Link];
4. import [Link];
5.
6. public interface IMetier {
7. // obtenir la feuille de salaire
8. FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés );
9. // liste des employés
10. List<Employe> findAllEmployes();
11. }
On crée une interface locale et une interface distante à partir de l'interface précédente :
1. package metier;
2.
3. import [Link];
4.
5. @Local
6. public interface IMetierLocal extends IMetier{
7.
8. }
1. package metier;
2.
3. import [Link];
4.
5. @Remote
6. public interface IMetierRemote extends IMetier{
7.
8. }
• lignes 1-2 : définissent un EJB dont chaque méthode s'exécute dans une transaction ;
• ligne 7 : une référence sur l'interface locale de l'EJB [CotisationDao] ;
• ligne 6 : l'annotation @EJB demande à ce que le conteneur EJB injecte une référence sur l'interface locale de l'EJB
[CotisationDao] ;
• lignes 8-9 : on refait la même chose pour l' interface locale de l'EJB [EmployeDao] ;
Au final, lorsque l'EJB [Metier] sera instancié, les champs des lignes 7 et 9 seront initialisés avec des références sur les
interfaces locales des deux EJB de la couche [DAO]. On fait donc ici l'hypothèse que les couches [metier] et [DAO]
s'exécuteront dans la même JVM.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
142/489
1
Dans le schéma ci-dessus, pour communiquer avec la couche [metier], la couche [ui] doit obtenir une référence sur l'interface
distante de l'EJB de la couche [metier].
JVM
Dans le schéma ci-dessus, pour communiquer avec la couche [metier], la couche [ui] doit obtenir une référence sur l'interface locale
de l'EJB de la couche [metier]. La méthode pour obtenir ces références diffère d'un conteneur à l'autre. Pour le conteneur
OpenEJB, on pourra procéder comme suit :
Le code précédent récupère des références sur les interfaces locales des EJB via leurs noms JNDI. Nous avons dit précédemment
que celles-ci pouvaient également être obtenues via l'annotation @EJB. On pourrait donc vouloir écrire :
@EJB
private IMetierLocal metier ;
L'annotation @EJB n'est honorée que si elle appartient à une classe chargée par le conteneur EJB. Ce sera le cas de la classe
[Metier] par exemple. Le code ci-dessus lui, appartiendra à une classe console qui ne sera pas chargée par le conteneur EJB. On est
donc obligé d'utiliser les noms JNDI des EJB.
Ci-dessous, le code pour avoir une référence sur l'interface distante de l'EJB [Metier] :
1. // on configure le conteneur Open EJB embarqué
2. Properties properties = new Properties();
3. [Link](Context.INITIAL_CONTEXT_FACTORY,
"[Link]");
4. // initialisation du contexte JNDI avec les propriétés précédentes
5. InitialContext initialContext = new InitialContext(properties);
6. // instanciation couche métier distante
7. IMetierRemote metier = (IMetierRemote) [Link]("MetierRemote");
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
143/489
L'implémentation actuelle avec Spring / Hibernate
Spring
OpenEjb
• dans l'onglet [Files] [2], créer un dossier [conf] [3] sous la racine du projet ;
• placer dans ce dossier, le fichier [[Link]] [4-10] suivant :
1. <?xml version="1.0"?>
2. <openejb>
3. <Resource id="Default JDBC Database">
4. JdbcDriver [Link]
5. JdbcUrl jdbc:mysql://localhost:3306/dbpam_hibernate
6. UserName root
7. </Resource>
8. </openejb>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
144/489
• créer le dossier [src / main/ resources/ META-INF] [11] (attention, il s'agit du mot anglais resources avec un seul s) ;
• y mettre le fichier [[Link]] [11] suivant :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.1"
3. xmlns="[Link]
4. xmlns:xsi="[Link]
5. xsi:schemaLocation="[Link]
[Link]
6. <persistence-unit name="mv-pam-jpa-eclipselinkPU" transaction-type="JTA">
7. <provider>[Link]</provider>
8. <class>[Link]</class>
9. <class>[Link]</class>
10. <class>[Link]</class>
11. <properties>
12. <property name="[Link]" value="drop-and-create"/>
13. <property name="[Link]" value="FINE"/>
14. <!-- weaving -->
15. <property name="[Link]" value="static"/>
16. <property name="[Link]" value="true"/>
17. <property name="[Link]" value="true"/>
18. </properties>
19. </persistence-unit>
20. </persistence>
• ligne 3 : on fera attention à l'URL de cette ligne. Si on reprend celle présente dans les fichiers [[Link]] que nous
avons utilisés jusqu'à maintenant et qui avaient été générés par Netbeans, on a une erreur signalée par OpenEJB et difficile
à déboguer ;
• ligne 13 : on demande des logs fins à EclipseLink ;
• ligne 12 : les tables seront créées à l'instanciation de la couche JPA ;
• ligne 13 : niveau de logs d'EclipseLink ;
• lignes 15-17 : pour l'enrichissement statique des entité JPA / EclipseLink ;
• ajouter les bibliothèques OpenEJB, EclipseLink ainsi que le pilote JDBC de MySQL au fichier [[Link]] du projet :
1. <?xml version="1.0" encoding="UTF-8"?>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
145/489
2. <project xmlns="[Link] xmlns:xsi="[Link]
3. xsi:schemaLocation="[Link] [Link]
4.
5. <!-- artifact -->
6. <modelVersion>4.0.0</modelVersion>
7. <groupId>[Link]</groupId>
8. <artifactId>mv-pam-openejb-eclipselink</artifactId>
9. <version>1.0-SNAPSHOT</version>
10. <packaging>jar</packaging>
11. <name>mv-pam-openejb-eclipselink</name>
12.
13. <dependencies>
14. <!-- eclipselink -->
15. <dependency>
16. <groupId>[Link]</groupId>
17. <artifactId>[Link]</artifactId>
18. <version>2.1.1</version>
19. </dependency>
20. <dependency>
21. <groupId>[Link]</groupId>
22. <artifactId>eclipselink</artifactId>
23. <version>2.6.3</version>
24. </dependency>
25. <!-- mysql -->
26. <dependency>
27. <groupId>mysql</groupId>
28. <artifactId>mysql-connector-java</artifactId>
29. <version>5.1.39</version>
30. </dependency>
31. <!-- JUnit -->
32. <dependency>
33. <groupId>junit</groupId>
34. <artifactId>junit</artifactId>
35. <version>4.12</version>
36. <scope>test</scope>
37. </dependency>
38. <!-- OpenEJB -->
39. <dependency>
40. <groupId>[Link]</groupId>
41. <artifactId>openejb-core</artifactId>
42. <version>4.7.4</version>
43. </dependency>
44. <!-- interfaces swing -->
45. <dependency>
46. <groupId>[Link]</groupId>
47. <artifactId>swing-layout</artifactId>
48. <version>1.0.3</version>
49. </dependency>
50. </dependencies>
51.
52. <properties>
53. <[Link]>UTF-8</[Link]>
54. <[Link]>1.8</[Link]>
55. <[Link]>1.8</[Link]>
56. </properties>
57. </project>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
146/489
Les erreurs signalées ci-dessus viennent du fait que la couche [DAO] copiée utilise Spring et que les bibliothèques Spring ne font
plus partie du projet.
Pour avoir les bons import de paquetages, faire [clic droit sur le code / Fix Imports].
L'import que faisait cette classe sur le framework Spring disparaît. Faire un [Clean and Build] du projet :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
147/489
Ceci fait, il n'y a plus d'erreurs dans le projet [2].
La ligne 1 est ajoutée. Pour avoir les bons import, faire [clic droit+Fix imports].
Pour comprendre l'annotation de la ligne 1, il faut se rappeler que chaque méthode des EJB de notre couche [DAO] :
• s'exécute dans une transaction démarrée et terminée par le conteneur EJB ;
• lance une exception de type [PamException] dès que quelque chose se passe mal ;
Lorsque la couche [metier] appelle une méthode M de la couche [DAO], cet appel est intercepté par le conteneur EJB. Tout se
passe comme s'il y avait une classe intermédiaire entre la couche [metier] et la couche [DAO], appelée ici[Proxy EJB], interceptant
tous les appels vers la couche [DAO]. Lorsque l'appel à la méthode M de la couche [DAO] est interceptée, le Proxy EJB démarre
une transaction puis passe la main à la méthode M de la couche [DAO] qui s'exécute alors dans cette transaction. La méthode M se
termine avec ou sans exception.
• si la méthode M se termine sans exception, l'exécution revient au proxy EJB qui termine la transaction en la validant par
un commit. Le flux d'exécution est ensuite rendu à la méthode appelante de la couche [metier] ;
• si la méthode M se termine avec exception, l'exécution revient au proxy EJB qui termine la transaction en l'invalidant par
un rollback. De plus il encapsule cette exception dans un type EJBException. Le flux d'exécution est ensuite rendu à la
méthode appelante de la couche [metier] qui reçoit donc une EJBException. L'annotation de la ligne 5 ci-dessus
empêche cette encapsulation. La couche [metier] recevra donc une PamException. De plus, l'attribut rollback=true
indique au proxy EJB que lorsqu'il reçoit une PamException, il doit invalider la transaction ;
1 OpenEjb
Les couches [ui] et [metier] vont s'échanger des objets. Dans la pratique, ces deux couches sont dans deux JVM différentes. Si la
couche [ui] veut passer un objet à la couche [métier], elle ne peut pas passer la référence de cet objet. La couche [métier] ne peut en
effet référencer des objets qui ne sont pas dans sa JVM. La couche [ui] va alors passer la valeur de l'objet et non sa référence. On
appelle cela la sérialisation de l'objet. La couche [métier] va recevoir cette valeur et va créer un nouvel objet à partir d'elle. On
appelle cela la désérialisation de l'objet. La couche [ui] et la couche [métier] ont alors deux objets identiques mais chacun dans sa
JVM.
Dans notre exemple, les types suivants peuvent être échangés entre les couches [ui] et [metier] : [Employe, Cotisation, Indemnite,
FeuilleSalaire, ElementsSalaire, PamException]. Ces classes doivent pouvoir être sérialisées. Cela est obtenu par la simple
déclaration :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
148/489
1. public [Classe] extends ... implements Serializable{
2. ..
3. }
On fera donc en sorte que les classes citées implémentent l'interface [Serializable]. Il n'y a rien d'autre à faire pour qu'une classe
puisse être sérialisée.
Nous ne conservons que le test [JUnitInitDB] qui initialise la base avec quelques données [2]. Nous renommons la classe
[ JUnitInitDbLocal] [3]. La classe [JUnitInitDBLocal] utilisera l'interface locale des EJB de la couche [DAO].
• lignes 3-5 : des références sur les interfaces locales des EJB de la couche [DAO] ;
• ligne 7 : @BeforeClass annote la méthode exécutée au démarrage du test JUnit ;
• lignes 10-13 : initialisation du conteneur OpenEJB. Cette initialisation est propriétaire et change avec chaque conteneur
EJB ;
• ligne 13 : on a un contexte JNDI (Java Naming and Directory Interface) qui permet d'accéder aux EJB via des noms. Avec
OpenEJB, l'interface locale d'un EJB E est désignée par ELocal et l'interface distante par ERemote ;
• lignes 15-17 : on demande au contexte JNDI, une référence sur les interfaces locales des EJB [EmployeDao,
CotisationDao, IndemniteDao].
Avant d'exécuter le test [JUnitDBLocal], avec la connexion Netbeans [dbpam_hibernate], supprimez les tables de la base pour
vérifier qu'elles vont bien être recréées puis exécutez le test :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
149/489
On construit le projet (Clean and Build), on lance le serveur MySQL si besoin est, on exécute le test JUnitInitDBLocal [2-3]. On
rappelle que le fichier [[Link]] a été configuré pour recréer les tables à chaque exécution.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
150/489
9. INFOS - [Link] = D:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejb-eclipselink
10. INFOS - [Link] = D:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejb-eclipselink
11. INFOS - Created new singletonService [Link]@668bc3d5
12. INFOS - Succeeded in installing singleton service
13. INFOS - openejb configuration file is 'D:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejb-eclipselink\conf\
[Link]'
14. INFOS - Configuring Service(id=Default Security Service, type=SecurityService, provider-id=Default Security Service)
15. INFOS - Configuring Service(id=Default Transaction Manager, type=TransactionManager, provider-id=Default Transaction
Manager)
16. INFOS - Configuring Service(id=Default JDBC Database, type=Resource, provider-id=Default JDBC Database)
17. INFOS - Found EjbModule in classpath: d:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejb-eclipselink\target\
classes
18. INFOS - Beginning load: d:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejb-eclipselink\target\classes
19. INFOS - Configuring enterprise application: D:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejb-eclipselink\
[Link]
20. INFOS - Auto-deploying ejb EmployeDao: EjbDeployment(deployment-id=EmployeDao)
21. INFOS - Auto-deploying ejb Metier: EjbDeployment(deployment-id=Metier)
22. INFOS - Auto-deploying ejb CotisationDao: EjbDeployment(deployment-id=CotisationDao)
23. INFOS - Auto-deploying ejb IndemniteDao: EjbDeployment(deployment-id=IndemniteDao)
24. INFOS - Configuring Service(id=Default Stateless Container, type=Container, provider-id=Default Stateless Container)
25. INFOS - Auto-creating a container for bean EmployeDao: Container(type=STATELESS, id=Default Stateless Container)
26. INFOS - Configuring PersistenceUnit(name=mv-pam-jpa-eclipselinkPU,
provider=[Link])
27. INFOS - Auto-creating a Resource with id 'Default JDBC DatabaseNonJta' of type 'DataSource for 'mv-pam-jpa-eclipselinkPU'.
28. INFOS - Configuring Service(id=Default JDBC DatabaseNonJta, type=Resource, provider-id=Default JDBC Database)
29. INFOS - Adjusting PersistenceUnit mv-pam-jpa-eclipselinkPU <jta-data-source> to Resource ID 'Default JDBC Database' from
'null'
30. INFOS - Adjusting PersistenceUnit mv-pam-jpa-eclipselinkPU <non-jta-data-source> to Resource ID 'Default JDBC
DatabaseNonJta' from 'null'
31. INFOS - Enterprise application "D:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejb-eclipselink\[Link]"
loaded.
32. INFOS - Creating TransactionManager(id=Default Transaction Manager)
33. INFOS - Creating SecurityService(id=Default Security Service)
34. INFOS - Creating Resource(id=Default JDBC Database)
35. INFOS - Creating Resource(id=Default JDBC DatabaseNonJta)
36. INFOS - Creating Container(id=Default Stateless Container)
37. INFOS - Assembling app: D:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejb-eclipselink\[Link]
38. [EL Fine]: 2016-09-02 [Link].84--Thread(Thread[main,5,main])--Configured server platform:
[Link]
39. [EL Config]: 2016-09-02 [Link].027--ServerSession(1544078442)--Thread(Thread[main,5,main])--The access type for the
persistent class [class [Link]] is set to [FIELD].
40. [EL Config]: 2016-09-02 [Link].043--ServerSession(1544078442)--Thread(Thread[main,5,main])--The access type for the
persistent class [class [Link]] is set to [FIELD].
41. [EL Config]: 2016-09-02 [Link].059--ServerSession(1544078442)--Thread(Thread[main,5,main])--The access type for the
persistent class [class [Link]] is set to [FIELD].
42. [EL Config]: 2016-09-02 [Link].059--ServerSession(1544078442)--Thread(Thread[main,5,main])--The target entity (reference)
class for the many to one mapping element [field indemnite] is being defaulted to: class [Link].
43. [EL Config]: 2016-09-02 [Link].059--ServerSession(1544078442)--Thread(Thread[main,5,main])--The alias name for the entity
class [class [Link]] is being defaulted to: Cotisation.
44. [EL Config]: 2016-09-02 [Link].074--ServerSession(1544078442)--Thread(Thread[main,5,main])--The alias name for the entity
class [class [Link]] is being defaulted to: Indemnite.
45. [EL Config]: 2016-09-02 [Link].09--ServerSession(1544078442)--Thread(Thread[main,5,main])--The alias name for the entity
class [class [Link]] is being defaulted to: Employe.
46. AVERTISSEMENT - JAVA AGENT NOT INSTALLED. The JPA Persistence Provider requested installation of a ClassFileTransformer
which requires a JavaAgent. See [Link]
47. [EL Info]: 2016-09-02 [Link].152--ServerSession(1544078442)--Thread(Thread[main,5,main])--EclipseLink, version: Eclipse
Persistence Services - 2.6.3.v20160428-59c81c5
48. [EL Fine]: 2016-09-02 [Link].481--Thread(Thread[main,5,main])--Detected database platform:
[Link]
49. [EL Config]: 2016-09-02 [Link].496--ServerSession(1544078442)--Connection(1850597787)--Thread(Thread[main,5,main])--
connecting(DatabaseLogin(
50. platform=>DatabasePlatform
51. user name=> ""
52. connector=>JNDIConnector datasource name=>null
53. ))
54. [EL Config]: 2016-09-02 [Link].496--ServerSession(1544078442)--Connection(407997647)--Thread(Thread[main,5,main])--
Connected: jdbc:mysql://localhost:3306/dbpam_hibernate
55. User: root@localhost
56. Database: MySQL Version: 5.6.25-log
57. Driver: MySQL Connector Java Version: mysql-connector-java-5.1.39 ( Revision: 3289a357af6d09ecc1a10fd3c26e95183e5790ad
)
58. [EL Config]: 2016-09-02 [Link].496--ServerSession(1544078442)--Connection(428996455)--Thread(Thread[main,5,main])--
connecting(DatabaseLogin(
59. platform=>MySQLPlatform
60. user name=> ""
61. connector=>JNDIConnector datasource name=>null
62. ))
63. [EL Config]: 2016-09-02 [Link].527--ServerSession(1544078442)--Connection(1829460911)--Thread(Thread[main,5,main])--
Connected: jdbc:mysql://localhost:3306/dbpam_hibernate
64. User: root@localhost
65. Database: MySQL Version: 5.6.25-log
66. Driver: MySQL Connector Java Version: mysql-connector-java-5.1.39 ( Revision: 3289a357af6d09ecc1a10fd3c26e95183e5790ad
)
67. [EL Info]: 2016-09-02 [Link].606--ServerSession(1544078442)--Thread(Thread[main,5,main])--/file:/d:/data/istia-1617/
netbeans/support-td/chap-05/mv-pam-openejb-eclipselink/target/classes/_mv-pam-jpa-eclipselinkPU login successful
68. [EL Warning]: 2016-09-02 [Link].606--ServerSession(1544078442)--Thread(Thread[main,5,main])--Problem while registering
MBean: [Link]
69. [EL Fine]: 2016-09-02 [Link].637--ServerSession(1544078442)--Connection(156199931)--Thread(Thread[main,5,main])--ALTER
TABLE employes DROP FOREIGN KEY FK_employes_INDEMNITE_ID
70. [EL Fine]: 2016-09-02 [Link].699--ServerSession(1544078442)--Connection(509559152)--Thread(Thread[main,5,main])--DROP
TABLE cotisations
71. [EL Fine]: 2016-09-02 [Link].715--ServerSession(1544078442)--Connection(1709578324)--Thread(Thread[main,5,main])--DROP
TABLE employes
72. [EL Fine]: 2016-09-02 [Link].731--ServerSession(1544078442)--Connection(1870723838)--Thread(Thread[main,5,main])--DROP
TABLE indemnites
73. [EL Fine]: 2016-09-02 [Link].762--ServerSession(1544078442)--Connection(961859592)--Thread(Thread[main,5,main])--CREATE
TABLE cotisations (ID BIGINT AUTO_INCREMENT NOT NULL, CSGD DOUBLE NOT NULL, CSGRDS DOUBLE NOT NULL, RETRAITE DOUBLE NOT
NULL, SECU DOUBLE NOT NULL, VERSION INTEGER NOT NULL, PRIMARY KEY (ID))
74. [EL Fine]: 2016-09-02 [Link].809--ServerSession(1544078442)--Connection(1562764987)--Thread(Thread[main,5,main])--CREATE
TABLE employes (ID BIGINT AUTO_INCREMENT NOT NULL, ADRESSE VARCHAR(50) NOT NULL, CP VARCHAR(5) NOT NULL, INDEMNITE_ID
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
151/489
BIGINT NOT NULL, NOM VARCHAR(30) NOT NULL, PRENOM VARCHAR(30) NOT NULL, SS VARCHAR(15) NOT NULL UNIQUE, VERSION INTEGER NOT
NULL, VILLE VARCHAR(20) NOT NULL, PRIMARY KEY (ID))
75. [EL Fine]: 2016-09-02 [Link].871--ServerSession(1544078442)--Connection(2080643905)--Thread(Thread[main,5,main])--CREATE
TABLE indemnites (ID BIGINT AUTO_INCREMENT NOT NULL, BASE_HEURE DOUBLE NOT NULL, ENTRETIEN_JOUR DOUBLE NOT NULL,
INDEMNITES_CP DOUBLE NOT NULL, INDICE INTEGER NOT NULL UNIQUE, REPAS_JOUR DOUBLE NOT NULL, VERSION INTEGER NOT NULL,
PRIMARY KEY (ID))
76. [EL Fine]: 2016-09-02 [Link].918--ServerSession(1544078442)--Connection(481553464)--Thread(Thread[main,5,main])--ALTER
TABLE employes ADD CONSTRAINT FK_employes_INDEMNITE_ID FOREIGN KEY (INDEMNITE_ID) REFERENCES indemnites (ID)
77. INFOS - PersistenceUnit(name=mv-pam-jpa-eclipselinkPU, provider=[Link]) - provider
time 1340ms
78. INFOS - Jndi(name=EmployeDaoLocal) --> Ejb(deployment-id=EmployeDao)
79. INFOS - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/EmployeDao![Link]) --> Ejb(deployment-
id=EmployeDao)
80. INFOS - Jndi(name=EmployeDaoRemote) --> Ejb(deployment-id=EmployeDao)
81. INFOS - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/EmployeDao![Link]) --> Ejb(deployment-
id=EmployeDao)
82. INFOS - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/EmployeDao) --> Ejb(deployment-id=EmployeDao)
83. INFOS - Jndi(name=MetierLocal) --> Ejb(deployment-id=Metier)
84. INFOS - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/Metier![Link]) --> Ejb(deployment-id=Metier)
85. INFOS - Jndi(name=MetierRemote) --> Ejb(deployment-id=Metier)
86. INFOS - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/Metier![Link]) --> Ejb(deployment-
id=Metier)
87. INFOS - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/Metier) --> Ejb(deployment-id=Metier)
88. INFOS - Jndi(name=CotisationDaoLocal) --> Ejb(deployment-id=CotisationDao)
89. INFOS - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/CotisationDao![Link]) -->
Ejb(deployment-id=CotisationDao)
90. INFOS - Jndi(name=CotisationDaoRemote) --> Ejb(deployment-id=CotisationDao)
91. INFOS - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/CotisationDao![Link]) -->
Ejb(deployment-id=CotisationDao)
92. INFOS - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/CotisationDao) --> Ejb(deployment-id=CotisationDao)
93. INFOS - Jndi(name=IndemniteDaoLocal) --> Ejb(deployment-id=IndemniteDao)
94. INFOS - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/IndemniteDao![Link]) --> Ejb(deployment-
id=IndemniteDao)
95. INFOS - Jndi(name=IndemniteDaoRemote) --> Ejb(deployment-id=IndemniteDao)
96. INFOS - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/IndemniteDao![Link]) --> Ejb(deployment-
id=IndemniteDao)
97. INFOS - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/IndemniteDao) --> Ejb(deployment-id=IndemniteDao)
98. INFOS - Existing thread singleton service in SystemInstance(): [Link]@668bc3d5
99. INFOS - OpenWebBeans Container is starting...
100. INFOS - Adding OpenWebBeansPlugin : [CdiPlugin]
101. INFOS - All injection points were validated successfully.
102. INFOS - OpenWebBeans Container has started, it took 78 ms.
103. INFOS - Created Ejb(deployment-id=Metier, ejb-name=Metier, container=Default Stateless Container)
104. INFOS - Created Ejb(deployment-id=IndemniteDao, ejb-name=IndemniteDao, container=Default Stateless Container)
105. INFOS - Created Ejb(deployment-id=EmployeDao, ejb-name=EmployeDao, container=Default Stateless Container)
106. INFOS - Created Ejb(deployment-id=CotisationDao, ejb-name=CotisationDao, container=Default Stateless Container)
107. INFOS - Started Ejb(deployment-id=Metier, ejb-name=Metier, container=Default Stateless Container)
108. INFOS - Started Ejb(deployment-id=IndemniteDao, ejb-name=IndemniteDao, container=Default Stateless Container)
109. INFOS - Started Ejb(deployment-id=EmployeDao, ejb-name=EmployeDao, container=Default Stateless Container)
110. INFOS - Started Ejb(deployment-id=CotisationDao, ejb-name=CotisationDao, container=Default Stateless Container)
111. INFOS - Deployed Application(path=D:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejb-eclipselink\[Link])
112. [EL Fine]: 2016-09-02 [Link].668--ServerSession(1544078442)--Connection(231351829)--Thread(Thread[main,5,main])--SELECT
ID, ADRESSE, CP, INDEMNITE_ID, NOM, PRENOM, SS, VERSION, VILLE FROM employes
113. [EL Fine]: 2016-09-02 [Link].746--ServerSession(1544078442)--Connection(1020632821)--Thread(Thread[main,5,main])--SELECT
ID, CSGD, CSGRDS, RETRAITE, SECU, VERSION FROM cotisations
114. [EL Fine]: 2016-09-02 [Link].762--ServerSession(1544078442)--Connection(897801829)--Thread(Thread[main,5,main])--SELECT
ID, BASE_HEURE, ENTRETIEN_JOUR, INDEMNITES_CP, INDICE, REPAS_JOUR, VERSION FROM indemnites
115. [EL Fine]: 2016-09-02 [Link].793--ClientSession(195253450)--Connection(1280263013)--Thread(Thread[main,5,main])--INSERT
INTO indemnites (BASE_HEURE, ENTRETIEN_JOUR, INDEMNITES_CP, INDICE, REPAS_JOUR, VERSION) VALUES (?, ?, ?, ?, ?, ?)
116. bind => [1.93, 2.0, 12.0, 1, 3.0, 1]
117. [EL Fine]: 2016-09-02 [Link].793--ClientSession(195253450)--Connection(1280263013)--Thread(Thread[main,5,main])--SELECT
LAST_INSERT_ID()
118. [EL Fine]: 2016-09-02 [Link].809--ClientSession(2096268257)--Connection(551994588)--Thread(Thread[main,5,main])--INSERT
INTO indemnites (BASE_HEURE, ENTRETIEN_JOUR, INDEMNITES_CP, INDICE, REPAS_JOUR, VERSION) VALUES (?, ?, ?, ?, ?, ?)
119. bind => [2.1, 2.1, 15.0, 2, 3.1, 1]
120. [EL Fine]: 2016-09-02 [Link].824--ClientSession(2096268257)--Connection(551994588)--Thread(Thread[main,5,main])--SELECT
LAST_INSERT_ID()
121. [EL Fine]: 2016-09-02 [Link].84--ClientSession(1250816994)--Connection(1033425208)--Thread(Thread[main,5,main])--INSERT
INTO employes (ADRESSE, CP, NOM, PRENOM, SS, VERSION, VILLE, INDEMNITE_ID) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
122. bind => [5 rue des oiseaux, 49203, Jouveinal, Marie, 254104940426058, 1, St Corentin, 2]
123. [EL Fine]: 2016-09-02 [Link].84--ClientSession(1250816994)--Connection(1033425208)--Thread(Thread[main,5,main])--SELECT
LAST_INSERT_ID()
124. [EL Fine]: 2016-09-02 [Link].84--ClientSession(954936400)--Connection(146799499)--Thread(Thread[main,5,main])--INSERT
INTO employes (ADRESSE, CP, NOM, PRENOM, SS, VERSION, VILLE, INDEMNITE_ID) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
125. bind => [La brûlerie, 49014, Laverti, Justine, 260124402111742, 1, St Marcel, 1]
126. [EL Fine]: 2016-09-02 [Link].84--ClientSession(954936400)--Connection(146799499)--Thread(Thread[main,5,main])--SELECT
LAST_INSERT_ID()
127. [EL Fine]: 2016-09-02 [Link].856--ClientSession(956365425)--Connection(645015141)--Thread(Thread[main,5,main])--INSERT
INTO cotisations (CSGD, CSGRDS, RETRAITE, SECU, VERSION) VALUES (?, ?, ?, ?, ?)
128. bind => [6.15, 3.49, 7.88, 9.39, 1]
129. [EL Fine]: 2016-09-02 [Link].856--ClientSession(956365425)--Connection(645015141)--Thread(Thread[main,5,main])--SELECT
LAST_INSERT_ID()
130. Employés ----------------------
131. [EL Fine]: 2016-09-02 [Link].871--ServerSession(1544078442)--Connection(1311884268)--Thread(Thread[main,5,main])--SELECT
ID, ADRESSE, CP, INDEMNITE_ID, NOM, PRENOM, SS, VERSION, VILLE FROM employes
132. Employé=[id=1, version=1, prénom=Marie, nom=Jouveinal, adresse=5 rue des oiseaux, ville=St Corentin, code postal=49203,
[Link]=null]
133. Employé=[id=2, version=1, prénom=Justine, nom=Laverti, adresse=La brûlerie, ville=St Marcel, code postal=49014,
[Link]=null]
134. Indemnités ----------------------
135. [EL Fine]: 2016-09-02 [Link].871--ServerSession(1544078442)--Connection(1740708279)--Thread(Thread[main,5,main])--SELECT
ID, BASE_HEURE, ENTRETIEN_JOUR, INDEMNITES_CP, INDICE, REPAS_JOUR, VERSION FROM indemnites
136. Indemnite[id=1, version=1, indice=1, base heure=1.93, entretien jour=2.0, repas jour=3.0, indemnités CP=12.0]
137. Indemnite[id=2, version=1, indice=2, base heure=2.1, entretien jour=2.1, repas jour=3.1, indemnités CP=15.0]
138. Cotisations ----------------------
139. [EL Fine]: 2016-09-02 [Link].871--ServerSession(1544078442)--Connection(1872417052)--Thread(Thread[main,5,main])--SELECT
ID, CSGD, CSGRDS, RETRAITE, SECU, VERSION FROM cotisations
140. Cotisations=[id=1, version=1, csgrds=3.49, csgd=6.15, secu=9.39, retraite=7.88]
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
152/489
• ligne 16 : la base de données est découverte ;
• lignes 20-23 : les EJB sont découverts ;
• lignes 26-30 : exploitation du fichier de configuration [[Link]] ;
• ligne 32 : création du gestionnaire de transactions ;
• lignes 39-45 : exploitation des entités JPA par EclipseLink ;
• lignes 48-67 : les noms JNDI des EJB de l'application. Il est indispensable de connaître ces noms qui ne sont pas standard
d'un conteneur EJB à un autre ;
• lignes 48-67 : EclipseLink se connecte à la base de données [dbpam_hibernate] ;
• lignes 69-76 : EclipseLink exécute la propriété [drop-and-create] trouvée dans [[Link]] ;
• lignes 78-97 : les noms JNDI des EJB ;
• lignes 83-84 : les deux noms JNDI de la version locale de l'EJB [Metier]. Dans le code, nous utiliserons celui de la ligne
85 ;
• lignes 85-86 : les deux noms JNDI de la version distante de l'EJB [Metier]. Dans le code, nous utiliserons celui de la ligne
87 ;
• lignes 103-110 : création et démarrage des EJB ;
• ligne 111 : l'application est déployée. Elle va désormais être exécutée ;
• lignes 112-139 : logs d'EclipseLink au cours de l'exécution de [JUnitInitDBLocal] et affichages faits par l'application elle-
même ;
Lignes 2 et 3, on remarquera que le champ [indemniteId] de l'employé ramené en mode LAZY vaut null.
Nous refaisons le même test, en utilisant cette fois-ci l'interface distante des EJB.
En [1], la classe [JUnitInitDBLocal] a été dupliquée (copy / paste) dans [JUnitInitDBRemote]. Dans cette classe, nous remplaçons
les interfaces locales par les interfaces distantes :
1. public class JUnitInitDBRemote {
2.
3. static private IEmployeDaoRemote employeDao = null;
4. static private ICotisationDaoRemote cotisationDao = null;
5. static private IIndemniteDaoRemote indemniteDao = null;
6.
7. @BeforeClass
8. public static void init() throws Exception {
9. // on configure le conteneur Open EJB embarqué
10. Properties properties = new Properties();
11. [Link](Context.INITIAL_CONTEXT_FACTORY, "[Link]");
12. // initialisation du contexte JNDI avec les propriétés précédentes
13. InitialContext initialContext = new InitialContext(properties);
14. // instanciation couches DAO distantes
15. employeDao = (IEmployeDaoRemote) [Link]("EmployeDaoRemote");
16. cotisationDao = (ICotisationDaoRemote) [Link]("CotisationDaoRemote");
17. indemniteDao = (IIndemniteDaoRemote) [Link]("IndemniteDaoRemote");
18. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
153/489
Ceci fait, la nouvelle classe de test peut être exécutée. Auparavant, avec la connexion Netbeans [dbpam_hibernate], supprimez les
tables de la base pour vérifier qu'elles vont bien être recréées.
Exécutez le test puis avec la connexion Netbeans [dbpam_hibernate], vérifiez que la base a été remplie.
Travail à faire : portez le test [JUnitDao] du projet [mv-pam-spring-hibernate] dans le projet [mv-pam-openejb-eclipselink] sous la
forme locale [JUnitDaoLocal] et distante [JUnitDaoRemote]. Vérifiez que ces tests passent.
Les erreurs signalées ci-dessus [1] viennent du fait que la couche [metier] copiée utilise Spring et que les bibliothèques Spring ne
font plus partie du projet.
1. package metier;
2.
3. import [Link];
4.
5. @Remote
6. public interface IMetierRemote extends IMetier{
7.
8. }
Ceci fait, en [3] nous modifions la classe [Metier] afin qu'elle devienne un EJB :
1. @Stateless
2. @TransactionAttribute([Link])
3. public class Metier implements IMetierLocal, IMetierRemote {
4.
5. // références sur la couche [dao]
6. @EJB
7. private ICotisationDaoLocal cotisationDao = null;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
154/489
8. @EJB
9. private IEmployeDaoLocal employeDao = null;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
155/489
En [2], on duplique [JUnitMetierLocal] en [JUnitMetierRemote] pour tester cette fois-ci l'interface distante de l'EJB [Metier]. Le
code de [JUnitMetierRemote] est modifié pour utiliser cette interface distante. Le reste ne change pas.
1. public class JUnitMetierRemote {
2.
3. // couche métier
4. static private IMetierRemote metier;
5.
6. @BeforeClass
7. public static void init() throws NamingException {
8. // log
9. log("init");
10. // on configure le conteneur Open EJB embarqué
11. Properties properties = new Properties();
12. [Link](Context.INITIAL_CONTEXT_FACTORY, "[Link]");
13. // initialisation du contexte JNDI avec les propriétés précédentes
14. InitialContext initialContext = new InitialContext(properties);
15. // instanciation couches DAO locales
16. IEmployeDaoLocal employeDao = (IEmployeDaoLocal) [Link]("EmployeDaoLocal");
17. ICotisationDaoLocal cotisationDao = (ICotisationDaoLocal) [Link]("CotisationDaoLocal");
18. IIndemniteDaoLocal indemniteDao = (IIndemniteDaoLocal) [Link]("IndemniteDaoLocal");
19. // instanciation couche métier distante
20. metier = (IMetierRemote) [Link]("MetierRemote");
21. // on vide la base
22. ...
Les erreurs signalées ci-dessus [1] viennent du fait que la couche [metier] copiée utilise Spring et que les bibliothèques Spring ne
font plus partie du projet. En [2], la classe [Main] est renommée [MainLocal]. Elle utilisera l'interface locale de l'EJB [Metier].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
156/489
Le code de la classe [MainLocal] évolue de la façon suivante :
1. public static void main(String[] args) {
2. // données locales
3. final String syntaxe = "pg num_securite_sociale nb_heures_travaillées nb_jours_travaillés";
4. // on vérifie le nombre de paramètres
5. if ([Link] != 3) {
6. [Link]("Syntaxe : " + syntaxe);
7. return;
8. }
9. // liste des erreurs
10. List<String> erreurs = new ArrayList<>();
11. ...
12. // des erreurs ?
13. if (![Link]()) {
14. for (int i = 0; i < [Link](); i++) {
15. [Link]([Link](i));
16. }
17. return;
18. }
19. // c'est bon - on peut demander la feuille de salaire
20. FeuilleSalaire feuilleSalaire;
21. try {
22. // on configure le conteneur Open EJB embarqué
23. Properties properties = new Properties();
24. [Link](Context.INITIAL_CONTEXT_FACTORY, "[Link]");
25. // initialisation du contexte JNDI avec les propriétés précédentes
26. InitialContext initialContext = new InitialContext(properties);
27. // instanciation couche métier locale
28. IMetierLocal metier = (IMetierLocal) [Link]("MetierLocal");
29. // calcul de la feuille de salaire
30. feuilleSalaire = [Link](args[0], nbHeuresTravaillées, nbJoursTravaillés);
31. } catch (PamException ex) {
32. [Link]([Link]("L'erreur suivante s'est produite : %s", [Link]()));
33. return;
34. } catch (Exception ex) {
35. [Link]([Link]("L'erreur suivante s'est produite : %s", ex));
36. return;
37. }
38. ...
Les modifications se situent dans les lignes 22-28. C'est la façon d'avoir une référence sur la couche [metier] qui change (ligne 28).
Nous n'expliquons pas le nouveau code qui a déjà été vu dans des exemples précédents. Une fois ces modifications faites, le projet
ne présente plus d'erreurs (cf [3]).
Nous configurons le projet pour qu'il soit exécuté avec des arguments [1-3] :
Pour que l'application console s'exécute normalement, il faut qu'il y ait des données dans la base. Pour cela, il faut modifier le fichier
[META-INF/[Link]] :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.1"
3. xmlns="[Link]
4. xmlns:xsi="[Link]
5. xsi:schemaLocation="[Link]
[Link]
6. <persistence-unit name="mv-pam-jpa-eclipselinkPU" transaction-type="JTA">
7. <provider>[Link]</provider>
8. <class>[Link]</class>
9. <class>[Link]</class>
10. <class>[Link]</class>
11. <properties>
12. <!-- <property name="[Link]" value="drop-and-create"/>-->
13. <property name="[Link]" value="FINE"/>
14. </properties>
15. </persistence-unit>
16. </persistence>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
157/489
La ligne 12 qui faisait que les tables de la base de données étaient recréées à chaque exécution est mise en commentaires. Le projet
doit être reconstruit (Clean and Build) pour que cette modification soit prise en compte. Si besoin est, exécutez le test [JUnitInitDB]
du projet [mv-pam-openejb-eclipselink] pour remplir les tables. Ceci fait, on peut exécuter le programme. Si tout va bien, on obtient
un affichage console analogue au suivant :
1. .......
2. INFO - Created EJB(deployment-id=IndemniteDao, ejb-name=IndemniteDao, container=Default Stateless Container)
3. INFO - Deployed Application(path=[Link])
4. [EL Info]: 2009-09-30 [Link].109--ServerSession(16658781)--EclipseLink, version: Eclipse Persistence Services -
1.1.2.v20090612-r4475
5. [EL Info]: 2009-09-30 [Link].937--ServerSession(16658781)--file:/C:/temp/09-09-28/pam-console-metier-dao-openejb-
eclipselink-0910/build/classes/-JPA login successful
6. Valeurs saisies :
7. N° de sécurité sociale de l'employé : 254104940426058
8. Nombre d'heures travaillées : 150
9. Nombre de jours travaillés : 20
10.
11. Informations Employé :
12. Nom : Jouveinal
13. Prénom : Marie
14. Adresse : 5 rue des oiseaux
15. Ville : St Corentin
16. Code Postal : 49203
17. Indice : 2
18.
19. Informations Cotisations :
20. CSGRDS : 3.49 %
21. CSGD : 6.15 %
22. Retraite : 7.88 %
23. Sécurité sociale : 9.39 %
24.
25. Informations Indemnités :
26. Salaire horaire : 2.1 euro
27. Entretien/jour : 2.1 euro
28. Repas/jour : 3.1 euro
29. Congés Payés : 15.0 %
30.
31. Informations Salaire :
32. Salaire de base : 362.25 euro
33. Cotisations sociales : 97.48 euro
34. Indemnités d'entretien : 42.0 euro
35. Indemnités de repas : 62.0 euro
36. Salaire net : 368.77 euro
37.
38. BUILD SUCCESSFUL (total time: 4 seconds)
Nous avons utilisé ici l'interface locale de la couche [metier]. Nous utilisons maintenant son interface distante dans une seconde
classe console :
En [1], la classe [MainLocal] a été dupliquée dans [MainRemote]. Le code de [MainRemote] est modifié pour utiliser l'interface
distante de la couche [metier] :
1. // c'est bon - on peut demander la feuille de salaire
2. FeuilleSalaire feuilleSalaire;
3. try {
4. // on configure le conteneur Open EJB embarqué
5. Properties properties = new Properties();
6. [Link](Context.INITIAL_CONTEXT_FACTORY, "[Link]");
7. // initialisation du contexte JNDI avec les propriétés précédentes
8. InitialContext initialContext = new InitialContext(properties);
9. // instanciation couche métier distante
10. IMetierRemote metier = (IMetierRemote) [Link]("MetierRemote");
11. // calcul de la feuille de salaire
12. feuilleSalaire = [Link](args[0], nbHeuresTravaillées, nbJoursTravaillés);
13. } catch (PamException ex) {
14. [Link]([Link]("L'erreur suivante s'est produite : %s", [Link]()));
15. return;
16. } catch (Exception ex) {
17. [Link]([Link]("L'erreur suivante s'est produite : %s", ex));
18. return;
19. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
158/489
La modification est faite ligne 10. Le projet est configuré [2] pour exécuter la classe [MainRemote]. Son exécution donne les mêmes
résultats que précédemment.
1.5.3 Conclusion
Nous avons montré comment porter une architecture Spring / Hibernate vers une architecture OpenEJB / EclipseLink.
Spring
Objets image
Couche Couche Couche Interface Implémentation Couche
de la BD
[ui] [metier] [DAO] [JPA] [EclipseLink] [JDBC]
OpenEjb
Le portage a pu se faire sans trop de difficultés parce que l'application initiale avait été structurée en couches. Ce point est
important à comprendre.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
159/489
1.6 Version 3 : Portage de l'application PAM sur un serveur d'applications
Glassfish
On se propose de placer les EJB des couches [metier] et [DAO] de l'architecture OpenEJB / EclipseLink dans le conteneur d'un
serveur d'applications Glassfish.
OpenEjb
Nous avons testé deux contextes d'exécution : local et distant. Dans ce dernier mode, la couche [ui] était cliente de la couche [metier],
couche implémentée par des EJB. Pour fonctionner en mode client / serveur, dans lequel le client et le serveur s'exécutent dans
deux JVM différentes, nous allons placer les couches [metier, DAO, JPA] sur le serveur Java EE Glassfish. Ce serveur est livré avec
Netbeans.
Couche C
Couche Couche Couche Couche
[ui] [metier] [DAO] [JPA / [JDBC]
EclipseLink]
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
160/489
Conteneur
Client Jpa / Eclipselink
Ejb3 Données
Java SE
serveur Java EE
Il s'agit de faire un portage vers le serveur Glassfish de ce qui a déjà été fait et testé avec le conteneur OpenEJB. C'est là l'intérêt de
OpenEJB et en général des conteneurs EJB embarqués : ils nous permettent de tester l'application dans un environnement
d'exécution simplifié. Lorsque l'application a été testée, il ne reste plus qu'à la porter sur un serveur cible, ici le serveur Glassfish.
• avec le bouton [4], choisir le dossier parent du dossier du projet ou taper son nom directement en [5] ;
• en [6], donner un nom au projet ;
• en [7], fixer le [groupId] de l'artifact Maven du projet ;
• en [8], passer à l'étape suivante ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
161/489
• en [9], choisir le serveur d'application sur lequel il sera exécuté. Celui choisi ici est l'un de ceux visibles dans l'onglet
[Runtime / Servers] ;
• en [10], choisir la version de Java EE ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
162/489
53. <execution>
54. <phase>validate</phase>
55. <goals>
56. <goal>copy</goal>
57. </goals>
58. <configuration>
59. <outputDirectory>${[Link]}</outputDirectory>
60. <silent>true</silent>
61. <artifactItems>
62. <artifactItem>
63. <groupId>javax</groupId>
64. <artifactId>javaee-endorsed-api</artifactId>
65. <version>7.0</version>
66. <type>jar</type>
67. </artifactItem>
68. </artifactItems>
69. </configuration>
70. </execution>
71. </executions>
72. </plugin>
73. </plugins>
74. </build>
75.
76. </project>
• lignes 18-23 : la seule dépendance dont le projet a besoin est celle de l'artifact [java-ee]. On notera l'attribut [provided] de
la ligne 22 : cela signifie que la dépendance [java-ee] ne sera pas incluse dans l'archive jar du projet. A l'exécution, elle sera
trouvée dans les archives du serveur Glassfish ;
• lignes 26-74 : divers plugins Maven nécessaires à la génération de l'archive du projet EJB ;
La configuration Maven précédente n'inclut pas les plugins nécessaires au weaving des entités JPA / EclipseLink. Nous les
rajoutons ;
1. <build>
2. <plugins>
3. <!-- généré par Netbeans-->
4. <plugin>
5. ...
6. </plugin>
7. <plugin>
8. ...
9. </plugin>
10. <plugin>
11. ....
12. </plugin>
13.
14. <!-- weaving EclipseLink -->
15. <!--[[Link] -->
16. <!-- This plugin ensures the EclipseLink static weaving -->
17. <plugin>
18. <artifactId>staticweave-maven-plugin</artifactId>
19. <groupId>[Link]</groupId>
20. <version>1.0.0</version>
21. <executions>
22. <execution>
23. <goals>
24. <goal>weave</goal>
25. </goals>
26. <phase>process-classes</phase>
27. <configuration>
28. <logLevel>ALL</logLevel>
29. <includeProjectClasspath>true</includeProjectClasspath>
30. </configuration>
31. </execution>
32. </executions>
33. <dependencies>
34. <dependency>
35. <groupId>[Link]</groupId>
36. <artifactId>eclipselink</artifactId>
37. <version>2.6.3</version>
38. </dependency>
39. </dependencies>
40. </plugin>
41. </plugins>
42.
43. <!-- weaving EclipseLink -->
44. <pluginManagement>
45. <plugins>
46. <!-- This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven
build itself. -->
47. <plugin>
48. <groupId>[Link].m2e</groupId>
49. <artifactId>lifecycle-mapping</artifactId>
50. <version>1.0.0</version>
51. <configuration>
52. <lifecycleMappingMetadata>
53. <pluginExecutions>
54. <pluginExecution>
55. <pluginExecutionFilter>
56. <groupId>
57. [Link]
58. </groupId>
59. <artifactId>
60. staticweave-maven-plugin
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
163/489
61. </artifactId>
62. <versionRange>
63. [1.0.0,)
64. </versionRange>
65. <goals>
66. <goal>weave</goal>
67. </goals>
68. </pluginExecutionFilter>
69. <action>
70. <execute>
71. <runOnIncremental>true</runOnIncremental>
72. </execute>
73. </action>
74. </pluginExecution>
75. </pluginExecutions>
76. </lifecycleMappingMetadata>
77. </configuration>
78. </plugin>
79. </plugins>
80. </pluginManagement>
81. </build>
Couche C
Couche Couche Couche Couche
[ui] [metier] [DAO] [JPA / [JDBC] SGBD BD
Eclipselink]
On pourra procéder comme suit. Tout d'abord, dans l'onglet [Runtime / Databases], on créera [1] une connexion sur la base
MySQL5 [dbpam_hibernate] :
Ceci fait, on peut passer à la création de la ressource JDBC utilisée par le module EJB :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
164/489
• en [1], créer un nouveau fichier – on s'assurera que le projet EJB est sélectionné avant de faire cette opération ;
• en [2], le projet EJB ;
• en [3], on sélectionne la catégorie [Glassfish] ;
• en [4], on veut créer une ressource JDBC ;
• en [5], indiquer que la ressource JDBC va utiliser un nouveau pool de connexions. On rappelle qu'un pool de connexions
est un pool de connexions ouvertes qui sert à accélérer les échanges de l'application avec la base de données ;
• en [6], donner un nom JNDI à la ressource JDBC créée. Ce nom peut être quelconque mais il a souvent la forme
jdbc/nom ; Ce nom JNDI sera utilisé dans le fichier [[Link]] pour désigner la source de données que
l'implémentation JPA doit utiliser ;
• en [7], donner un nom qui peut être quelconque au pool de connexions qui va être créé ;
• dans la liste déroulante [8], choisir la connexion JDBC créée précédemment sur la base MySQL / dbpam_hibernate ;
• en [9], un récapitulatif des propriétés du pool de connexions - on ne touche à rien ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
165/489
• en [10], on peut préciser plusieurs des propriétés du pool de connexions - on laisse ici les valeurs par défaut ;
• en [11], à l'issue de l'assistant de création d'une ressource JDBC pour le module EJB, un fichier [[Link]] a
été créé dans la branche [Other Sources]. C'est ici une erreur de Netbeans. Ce fichier devrait être dans le dossier [setup].
On l'y déplace [13] ;
Le fichier [[Link]] est un fichier XML qui reprend toutes les données collectées par l'assistant. Il va être utilisé par
Netbeans pour, lors du déploiement du module EJB sur le serveur Glassfish, demander la création de la ressource JDBC dont a
besoin ce module.
On peut désormais créer le fichier [[Link]] qui va configurer la couche JPA du module EJB :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
166/489
• en [1], créer une nouvelle ressource ;
• en [2], le projet EJB ;
• en [4], on sélectionne la catégorie [Persistence] ;
• en [5], on veut créer une unité de persistance ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
167/489
• ligne 5 : les entités JPA ne sont pas précisées. Elles seront cherchées dans le Classpath du module EJB ;
• le nom de l'implémentation JPA (Hibernate, EclipseLink, ...) utilisée n'est pas indiquée. Dans ce cas, Glassfish utilise par
défaut EclipseLink ;
Ces trois couches sont identiques à ce qu'elles étaient avec OpenEJB. On peut procéder à un simple copier / coller entre les deux
projets. C'est ce que nous faisons maintenant :
• en [1], le résultat de la copie des paquetages [JPA, dao, metier, exception] du projet [mv-pam-openejb-eclipselink] dans le
module EJB [mv-pam-ejb-metier-dao-jpa-eclipselink]. Il ne doit pas y avoir d'erreurs ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
168/489
• en [1-3], nous lançons le serveur Glassfish ;
Le déploiement a également créé la ressource JNDI [jdbc / dbpam_hibernate] [8] ainsi que le pool de connexions
[dbpamHibernateConnectionPool] [9]. On rappelle que ces informations sont présentes dans le fichier [setup / glassfish-
[Link]] ;
Lors du déploiement, le serveur Glassfish logue dans la console des informations intéressantes :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
169/489
1. ...
2. Infos: [Link] actually got transformed
3. Infos: [Link] actually got transformed
4. Infos: [Link] actually got transformed
5. Infos: EclipseLink, version: Eclipse Persistence Services - 2.6.1.v20150605-31e8258
6. Infos: /file:/D:/data/istia-1617/netbeans/support-td/chap-06/mv-pam-ejb-metier-dao-eclipselink/target/classes/_mv-pam-
ejb-metier-dao-eclipselinkPU login successful
7. Infos: Portable JNDI names for EJB IndemniteDao: [java:global/mv-pam-ejb-metier-dao-eclipselink/IndemniteDao!
[Link], java:global/mv-pam-ejb-metier-dao-eclipselink/IndemniteDao![Link]]
8. Infos: Glassfish-specific (Non-portable) JNDI names for EJB IndemniteDao:
[[Link]#[Link], [Link]]
9. Infos: Portable JNDI names for EJB Metier: [java:global/mv-pam-ejb-metier-dao-eclipselink/Metier![Link],
java:global/mv-pam-ejb-metier-dao-eclipselink/Metier![Link]]
10. Infos: Glassfish-specific (Non-portable) JNDI names for EJB Metier: [[Link],
[Link]#[Link]]
11. Infos: Portable JNDI names for EJB CotisationDao: [java:global/mv-pam-ejb-metier-dao-eclipselink/CotisationDao!
[Link], java:global/mv-pam-ejb-metier-dao-eclipselink/CotisationDao![Link]]
12. Infos: Glassfish-specific (Non-portable) JNDI names for EJB CotisationDao: [[Link],
[Link]#[Link]]
13. Infos: Portable JNDI names for EJB EmployeDao: [java:global/mv-pam-ejb-metier-dao-eclipselink/EmployeDao!
[Link], java:global/mv-pam-ejb-metier-dao-eclipselink/EmployeDao![Link]]
14. Infos: Glassfish-specific (Non-portable) JNDI names for EJB EmployeDao: [[Link]#[Link],
[Link]]
15. ...
Ces noms seront utiles à l'application console que nous allons écrire pour utiliser le module EJB déployé.
Nous créons un nouveau projet Maven de type [Java Application] nommé [mv-pam-client-ejb-metier-dao-eclipselink] :
Pour qu'il ait accès à la définition de ces éléments, nous créons une dépendance vers le projet EJB :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
170/489
4
• lignes 10-14 : la dépendance sur le projet Maven du module EJB. Nous voulons récupérer ici les définitions des entités
JPA et celles des différentes interfaces ainsi que celle de la classe d'exception [PamException] ;
Si nous compilons Clean and Build) le projet client, nous obtenons des erreurs :
1. COMPILATION ERROR :
2. -------------------------------------------------------------
3. ui/console/[Link]:[76,25] cannot access [Link]
4. class file for [Link] not found
5. ui/console/[Link]:[77,25] cannot access [Link]
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
171/489
6. class file for [Link] not found
7. ui/console/[Link]:[78,25] cannot access [Link]
8. class file for [Link] not found
9. ui/console/[Link]:[79,25] cannot access [Link]
10. class file for [Link] not found
11. ...
Les messages d'erreur sont peu clairs mais on comprend qu'ils concernent EclipseLink. On rajoute dans le [[Link]] la dépendance
sur EclipseLink, lignes 9-13 ci-dessous :
1. <dependencies>
2. <!-- projet EJB -->
3. <dependency>
4. <groupId>${[Link]}</groupId>
5. <artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
6. <version>${[Link]}</version>
7. </dependency>
8. <!-- eclipselink -->
9. <dependency>
10. <groupId>[Link]</groupId>
11. <artifactId>eclipselink</artifactId>
12. <version>2.6.3</version>
13. </dependency>
14. </dependencies>
La classe [MainRemote] doit obtenir une référence sur l'EJB de la couche [metier]. Le code de la classe [MainRemote] évolue de la
façon suivante :
1. ...
2. // c'est bon - on peut demander la feuille de salaire
3. FeuilleSalaire feuilleSalaire;
4. try {
5. // contexte JNDI du serveur Glassfish
6. InitialContext initialContext = new InitialContext();
7. // instanciation couche métier
8. IMetierRemote metier = (IMetierRemote) [Link]("java:global/mv-pam-ejb-metier-dao-eclipselink/Metier!
[Link]");
9. // calcul de la feuille de salaire
10. feuilleSalaire = [Link](args[0], nbHeuresTravaillées, nbJoursTravaillés);
11. } catch (PamException ex) {
12. [Link]([Link]("L'erreur suivante s'est produite : %s", [Link]()));
13. return;
14. } catch (Exception ex) {
15. [Link]([Link]("L'erreur suivante s'est produite : %s", ex));
16. return;
17. }
18.
19. ..
Ligne 1, le nom JNDI utilisable avec tout serveur d'applications JAVA EE 6. Ligne 2, le nom JNDI spécifique à Glassfish.
Dans le code, ligne 8, nous utilisons le nom JNDI portable ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
172/489
1
En [3], on configure le projet pour qu'il exécute la classe [MainRemote] avec les arguments [4].
Pour savoir où se produit cette erreur, il faut laisser l'exception se produire sans l'intercepter. Nous modifions le code de
[MainRemote] de la façon suivante :
1. public static void main(String[] args) throws NamingException {
2. ..
3. // c'est bon - on peut demander la feuille de salaire
4. FeuilleSalaire feuilleSalaire;
5. // try {
6. // contexte JNDI du serveur Glassfish
7. InitialContext initialContext = new InitialContext();
8. // instanciation couche métier
9. IMetierRemote metier = (IMetierRemote) [Link]("java:global/mv-pam-ejb-metier-dao-eclipselink/Metier!
[Link]");
10. // calcul de la feuille de salaire
11. feuilleSalaire = [Link](args[0], nbHeuresTravaillées, nbJoursTravaillés);
12. // } catch (PamException ex) {
13. // [Link]([Link]("L'erreur suivante s'est produite : %s", [Link]()));
14. // return;
15. // } catch (Exception ex) {
16. // [Link]([Link]("L'erreur suivante s'est produite : %s", ex));
17. // return;
18. // }
19. // affichage détaillé
20. ...
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
173/489
L'exécution du projet donne alors le résultat suivant :
1. Exception in thread "main" [Link]: Need to specify class name in environment or system
property, or as an applet parameter, or in an application resource file: [Link]
2. at [Link]([Link])
3. at [Link]([Link])
4. at [Link]([Link])
5. at [Link]([Link])
6. at [Link]([Link])
C'est le contexte JNDI de l'application EJB qui n'a pu être obtenu. C'est au travers de ce contexte qu'on a accès aux EJB via leurs
noms JNDI. Pour obtenir ce contexte JNDI, il faut ajouter une dépendance nécessaire à tous les clients Glassfish. Ceci est fait dans
le [[Link]] (lignes 15-19 ci-dessous) :
1. <dependencies>
2. <!-- projet EJB -->
3. <dependency>
4. <groupId>${[Link]}</groupId>
5. <artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
6. <version>${[Link]}</version>
7. </dependency>
8. <!-- eclipselink -->
9. <dependency>
10. <groupId>[Link]</groupId>
11. <artifactId>eclipselink</artifactId>
12. <version>2.6.3</version>
13. </dependency>
14. <!-- client glassfish -->
15. <dependency>
16. <groupId>[Link]</groupId>
17. <artifactId>gf-client</artifactId>
18. <version>3.1.1</version>
19. </dependency>
20. </dependencies>
On exécute le projet de nouveau. Cette fois-ci, l'ajout de la dépendance qui vient d'être fait a créé une erreur :
1. Failed to execute goal on project mv-pam-client-ejb-metier-dao-eclipselink: Could not resolve dependencies for project
[Link]:mv-pam-client-ejb-metier-dao-eclipselink:jar:1.0-SNAPSHOT: Failed to collect dependencies at
[Link]:gf-client:jar:3.1.1 -> [Link]:gf-client-module:jar:3.1.1 -> [Link]:ejb-
container:jar:3.1.1 -> [Link]:security:jar:3.1.1 -> [Link]:dol:jar:3.1.1 ->
[Link]:[Link]:jar:2.0.3: Failed to read artifact descriptor for
[Link]:[Link]:jar:2.0.3: Could not transfer artifact
[Link]:[Link]:pom:2.0.3 from/to glassfish-repo-archive
([Link] hostname in certificate didn't match: <[Link]> !=
<[Link]> OR <[Link]> -> [Help 1]
De nouveau, l'erreur est peu compréhensible. Son origine est étonnante : une dépendance EclipseLink nécessitée par la nouvelle
dépendance n'a pas été trouvée. Elle a été cherchée dans le dépôt [[Link] Il est
possible de spécifier explicitement les dépôts qui doivent être explorés lorsque Maven cherche des dépendances. C'est ce que nous
faisons maintenant. Le fichier [[Link]] évolue de la façon suivante :
1. ...
2. <dependencies>
3. <!-- projet EJB -->
4. <dependency>
5. <groupId>${[Link]}</groupId>
6. <artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
7. <version>${[Link]}</version>
8. </dependency>
9. <!-- eclipselink -->
10. <dependency>
11. <groupId>[Link]</groupId>
12. <artifactId>eclipselink</artifactId>
13. <version>2.6.3</version>
14. </dependency>
15. <!-- client glassfish -->
16. <dependency>
17. <groupId>[Link]</groupId>
18. <artifactId>gf-client</artifactId>
19. <version>3.1.1</version>
20. </dependency>
21. </dependencies>
22.
23. <!-- repositories -->
24. <repositories>
25. <repository>
26. <url>[Link]
27. <id>eclipselink</id>
28. <layout>default</layout>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
174/489
29. <name>Repository for library Library[eclipselink]</name>
30. </repository>
31. </repositories>
32. ...
Aux lignes 25-30, nous avons désigné un dépôt supplémentaire où Maven doit chercher les dépendances. Le dépôt ajouté est celui
d'EclipseLink. Cette méthode est à utiliser lorsqu'on veut par exemple télécharger des versions beta de dépendances. Celles-ci ne se
trouvent pas sur le dépôt central de Maven mais dans des dépôts spécialisés.
Cette fois-ci, l'exécution du projet ne plante pas (mais le temps de réponse du serveur EJB peut être particulièrement long) et donne
le résultats suivant :
1. run:
2. Valeurs saisies :
3. N° de sécurité sociale de l'employé : 254104940426058
4. Nombre d'heures travaillées : 150
5. Nombre de jours travaillés : 20
6.
7. Informations Employé :
8. Nom : Jouveinal
9. Prénom : Marie
10. Adresse : 5 rue des oiseaux
11. Ville : St Corentin
12. Code Postal : 49203
13. Indice : 2
14.
15. Informations Cotisations :
16. CSGRDS : 3.49 %
17. CSGD : 6.15 %
18. Retraite : 7.88 %
19. Sécurité sociale : 9.39 %
20.
21. Informations Indemnités :
22. Salaire horaire : 2.1 euro
23. Entretien/jour : 2.1 euro
24. Repas/jour : 3.1 euro
25. Congés Payés : 15.0 %
26.
27. Informations Salaire :
28. Salaire de base : 362.25 euro
29. Cotisations sociales : 97.48 euro
30. Indemnités d'entretien : 42.0 euro
31. Indemnités de repas : 62.0 euro
32. Salaire net : 368.77 euro
33.
34. BUILD SUCCESSFUL (total time: 2 seconds)
Si dans les propriétés, on met un n° de sécurité sociale incorrect, on obtient le résultat suivant :
1. run:
2. L'erreur suivante s'est produite : L'employé de n°[254104940426058x] est introuvable
3. BUILD SUCCESSFUL (total time: 2 seconds)
Note : si vous rencontrez une exception de type [unmarshalling / marshalling exception] cela signifie que le serveur a voulu
envoyer au client un objet qui n'a pas pu être sérialisé. Vérifiez les points suivants :
• les entités échangées entre le client et le serveur [Employe, Cotisation, Indemnite, FeuilleSalaire, ElementsSalaire,
PamException] doivent implémenter l'interface [Serializable] ;
• l'entité JPA [Indemnite] ne doit pas voir l'annotation [@OneToMany]. Si elle est présente, enlevez-la ainsi que le champ
qu'elle annote ;
• si les points précédents sont remplis, alors regardez les logs de Glassfish. La cause la plus probable est qu'une exception
non sérialisable s'est produite côté serveur. Lorsque le serveur veut l'envoyer au client, une exception de sérialisation se
produit alors.
Les lignes 7 et 8 désignent la machine du service JNDI et le port d'écoute de celui-ci. Ce fichier ne permet pas d'interroger un
serveur JNDI autre que localhost ou travaillant sur un port autre que le port 3700. Si on veut changer ces deux paramètres, on peut
construire son propre fichier [[Link]] ou utiliser une configuration Spring. Nous montrons cette deuxième technnique.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
175/489
Nous commençons par créer un nouveau projet à partir du projet [pam-client-metier-dao-jpa-eclipselink] initial.
Ce projet n'a pas la branche [Other Sources] qui sert à mettre des fichiers de configuration qui doivent être dans le Classpath du
projet. Or ici, on veut créer un fichier de configuration Spring qui doit être dans le Classpath du projet. Pour créer la branche
[Other Sources] on peut procéder de la façon suivante :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
176/489
• en [9], le fichier [[Link]] de configuration de Spring. Son contenu est le suivant :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="[Link]
3. xmlns:xsi="[Link]
4. xmlns:jee="[Link]
5.
6. xsi:schemaLocation="[Link] [Link]
[Link]
7. [Link] [Link]
8. ">
9.
10. </beans>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
177/489
17. [Link]=[Link]
18. [Link]=[Link]
19. [Link]=localhost
20. [Link]=3700
21. </jee:environment>
22. </jee:jndi-lookup>
23.
24. </beans>
Nous utilisons ici une balise <jee> (ligne 14) apparue avec Spring 2.0. L'usage de cette balise nécessite la définition du schéma
auquel elle appartient, lignes 4, 9 et 10.
• ligne 14 : la balise <jee:jndi-lookup> permet d'obtenir la référence d'un objet auprès d'un service JNDI. Ici, on associe le
bean nommé " metier " à la ressource JNDI associée à l'EJB [Metier]. Le nom JNDI utilisé ici est le nom portable (Java
EE 6) de l'EJB ;
• le contenu du fichier [[Link]] devient le contenu de la balise <jee:environment> (ligne 15) qui sert à définir les
paramètres de connexion au service JNDI ;
Pour que le fichier [[Link]] puisse être exploité, il faut ajouter la dépendance de Spring au projet Maven :
1. ...
2. <dependencies>
3. <!-- projet EJB -->
4. <dependency>
5. <groupId>${[Link]}</groupId>
6. <artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
7. <version>${[Link]}</version>
8. </dependency>
9. <!-- eclipselink -->
10. <dependency>
11. <groupId>[Link]</groupId>
12. <artifactId>eclipselink</artifactId>
13. <version>2.6.3</version>
14. </dependency>
15. <!-- client glassfish -->
16. <dependency>
17. <groupId>[Link]</groupId>
18. <artifactId>gf-client</artifactId>
19. <version>3.1.1</version>
20. </dependency>
21. <!-- Spring framework -->
22. <dependency>
23. <groupId>[Link]</groupId>
24. <artifactId>spring-context</artifactId>
25. <version>[Link]</version>
26. </dependency>
27. </dependencies>
28. ...
• lignes 22-26 : la dépendance sur [spring-context] qui ramène elle-même d'autres dépendances qui sont automatiquement
chargées ;
Lignes 6-7, la référence de type [IMetier] sur la couche [metier] est demandée à Spring. On notera que pour que notre code s'adapte
aussi bien à la version locale que distante de l'EJB, nous avons, ligne 7, donné le type [IMetier] à la référence sur la couche [metier].
L'interface [IMetier] est la classe parent des interfaces [IMetierLocal] et [IMetierRemote]. Cette solution amène de la souplesse dans
notre architecture. En effet, si l'EJB de la couche [metier] devenait local, c.a.d. exécuté dans la même JVM que notre client
[MainRemote], le code de celui-ci ne changerait pas. Seul le contenu du fichier [[Link]] changerait. On retrouve là
une configuration analogue à l'architecture Spring / JPA étudiée au paragraphe 1.4.10.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
178/489
[Link] Le client Swing
Nous construisons maintenant le client swing de notre application client / serveur EJB.
Ci-dessus, la classe [PamJFrame] avait été écrite initialement pour s'exécuter dans un environnement Spring / JPA :
7 Spring
Maintenant cette classe doit devenir le client distant d'un EJB déployé sur le serveur Glassfish.
Travail à faire : en suivant l'exemple du client console [[Link]] du projet, modifier la façon utilisée par la
méthode [doMyInit] (cf paragraphe [Link], page 131) de la classe [PamJFrame] pour acquérir une référence sur la couche [metier]
qui est maintenant distante.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
179/489
Les tests unitaires avait été écrits initialement pour s'exécuter dans un environnement Spring / JPA :
7 Spring
Maintenant les classes de test doivent devenir des clients distants des EJB déployés sur le serveur Glassfish.
Travail à faire : en suivant l'exemple du client console [[Link]] du projet, modifiez la façon utilisée par la
méthode [init] des différents tests pour acquérir une référence sur les couches [DAO] et [metier] qui sont maintenant distantes,
puis exécutez les tests.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
180/489
27. [Link]=localhost
28. [Link]=3700
29. </jee:environment>
30. </jee:jndi-lookup>
31.
32. <!-- employeDao -->
33. <jee:jndi-lookup id="employeDao" jndi-name="java:global/mv-pam-ejb-metier-dao-eclipselink/EmployeDao!
[Link]">
34. <jee:environment>
35. [Link]=[Link]
36. [Link]=[Link]
37. [Link]=[Link]
38. [Link]=localhost
39. [Link]=3700
40. </jee:environment>
41. </jee:jndi-lookup>
42.
43. <!-- indemniteDao -->
44. <jee:jndi-lookup id="indemniteDao" jndi-name="java:global/mv-pam-ejb-metier-dao-eclipselink/IndemniteDao!
[Link]">
45. <jee:environment>
46. [Link]=[Link]
47. [Link]=[Link]
48. [Link]=[Link]
49. [Link]=localhost
50. [Link]=3700
51. </jee:environment>
52. </jee:jndi-lookup>
53.
54. </beans>
Note 2 :
A l'exécution initiale de [JunitDaoRemote] tous les tests ont échoué. Il en a été de même pour les autres tests. L'erreur se passait
côté serveur. Il était indiqué que la colonne [EMPLOYES.INDEMNITE_ID] ne pouvait avoir la valeur NULL. Après avoir
tâtonné un bon moment à la recherche de la cause de cette erreur, il s'est avéré que l'erreur provenait de la définition suivante
trouvée dans l'entité JPA [Employe] :
1. // clé étrangère
2. @Column(name = "INDEMNITE_ID", updatable = false, insertable = false, nullable = false)
3. private Long indemniteId;
L'erreur est ligne 2 : il ne faut pas écrire [nullable=false]. Cette erreur est surprenante : en effet cet attribut n'avait pas posé
problème dans les environnements Spring / Hibernate et OpenEJB / Eclipselink. Dans l'environnement Glassfish / EclipseLink,
cet attribut cause donc une erreur. Elle est d'autant plus surprenante que dans la base de données on garde bien l'attribut [NOT
NULL] de la colonne [EMPLOYES.INDEMNITES_ID]. Il y a ici une incohérence entre l'attribut du champ [indemniteId]
(nullable=true par défaut) de l'entité [Employe] et celui (NOT NULL) de la colonne correspondante
[EMPLOYES.INDEMNITES_ID] dans la base de données.
On retrouve de nouveau la difficulté d'avoir des configurations à 100% portables d'un environnement à un autre. Cette erreur
corrigée, tous les tests passent.
Note 3 :
On peut se demander, si lorsqu'on demande au serveur la liste des employés, ceux-ci sont envoyés avec ou sans leurs indemnités.
Côté serveur, l'entité JPA [Employe] a un champ [Indemnite indemnite] avec l'attribut [LAZY]. Donc lorsqu'on demande les
employés, on ne devrait pas avoir leurs indemnités. On peut vérifier ce point, en exécutant le test [JUnitInitDB] en mode débogage :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
181/489
• on fixe un point d'arrêt en [1] ci-dessus afin de pouvoir examiner la variable [employes] de la ligne 43 [2] ;
• lorsqu'on examine dans la liste [employes], l'élément [Jouveinal] [3], on voit que son champ [Indemnite indemnite] vaut
null, ce qui montre que l'attribut [LAZY] de ce champ a été observé ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
182/489
1.7 Version 4 – client / serveur dans une architecture de service web SOAP
Dans cette nouvelle version, l'application [Pam] va s'exécuter en mode client / serveur dans une architecture de service web.
Revenons sur l'architecture de l'application précédente :
Ci-dessus, une couche de communication [C, RMI, S] permettait une communication transparente entre le client [ui] et la couche
distante [metier]. Nous allons utiliser une architecture analogue, où la couche de communication [C, RMI, S] sera remplacée par une
couche [C, HTTP / SOAP, S] :
Le protocole HTTP / SOAP a l'avantage sur le protocole RMI / EJB précédent d'être multi-plateformes. Ainsi le service web peut
être écrit en Java et déployé sur le serveur Glassfish. Le client lui, pourrait être un client .NET ou PHP.
Un service web peut être implémenté de diverses façons au sein d'un serveur Java EE :
• par une classe annotée @WebService qui s'exécute dans un conteneur web :
Client Conteneur
Conteneur web Jpa
du Ejb3 Données
service web tcp-ip serveur Java EE
Client Conteneur
Jpa
du Ejb3 Données
service web serveur Java EE
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
183/489
Nous commençons par cette dernière architecture.
la couche [metier] va être le service web contacté par la couche [ui]. Cette classe n'a pas besoin d'implémenter une interface. Ce sont
des annotations qui transforment un POJO (Plain Ordinary Java Object) en service web. La classe [Metier] qui implémente la
couche [metier] ci-dessus, est transformée de la façon suivante :
1. package metier;
2.
3. ...
4. @WebService
5. @Stateless()
6. @TransactionAttribute([Link])
7. public class Metier implements IMetierLocal,IMetierRemote {
8.
9. // références sur les couches [DAO]
10. @EJB
11. private ICotisationDaoLocal cotisationDao = null;
12. @EJB
13. private IEmployeDaoLocal employeDao=null;
14.
15. // obtenir la feuille de salaire
16. @WebMethod
17. public FeuilleSalaire calculerFeuilleSalaire(String SS,
18. ...
19. }
20.
21. // liste des employés
22. @WebMethod
23. public List<Employe> findAllEmployes() {
24. ...
25. }
26. // important - pas de getters et setters pour les EJB qui deviennent des services web
27. }
• ligne 4, l'annotation @WebService fait de la classe [Metier] un service web. Un service web expose des méthodes à ses
clients. Celles-ci peuvent être annotées par l'attribut @WebMethod, mais c'est facultatif. Par défaut, les méthodes
publiques d'une classe annotée par [@WebService] deviennent automatiquement des méthodes du service web ;
• lignes 16 et 22 : les deux méthodes de la classe [Metier] deviennent des méthodes du service web ;
• ligne 26 : il est important que les getters et setters soient supprimés sinon ils seront exposés dans le service web et cela
cause des erreurs de sécurité ;
L'ajout de ces annotations est détecté par Netbeans qui fait alors évoluer la nature du projet :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
184/489
En [1], une arborescence [Web Services] est apparue dans le projet. On y trouve le service web MetierService et ses deux
méthodes.
Note : Si à la compilation du projet web, vous obtenez une erreur qui parle de weaving EclipseLink des entités JPA :
L'application serveur peut être déployée [3]. Le serveur MySQL soit être lancé et sa base [dbpam_hibernate] exister et être remplie.
Il peut être nécessaire auparavant de supprimer [2] les EJB du projet client / serveur EJB étudié précédemment pour éviter des
conflits de noms. En effet, notre nouveau projet amène avec lui les mêmes EJB que ceux du projet précédent.
En [1], nous voyons notre application serveur déployée sur le serveur Glassfish. Une fois le service web déployé, il peut être testé :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
185/489
• en [1], dans le projet courant, nous testons le service web [MetierService] ;
• le service web est accessible via différentes URL. L'URL [2] permet de tester le service web ;
• en [3], un lien sur le fichier XML définissant le service web. Les clients du service web ont besoin de connaître l'URL de ce
fichier. C'est à partir de lui qu'est générée la couche cliente (stubs) du service web ;
• en [4,5], un formulaire permettant de tester les méthodes exposées par le service web. Celles-ci sont présentées avec leurs
paramètres que l'utilisateur peut définir ;
Par exemple, testons la méthode [findAllEmployes] qui n'a besoin d'aucun paramètre :
Ci-dessus, nous testons la méthode. Nous recevons alors la réponse ci-dessous (vue partielle). On remarque qu'on obtient les
employés avec leurs indemnités, ce qui montre que :
• soit l'attribut [LAZY] sur le champ [[Link]] n'a pas été observé par EclipseLink. Or il avait été observé avec
le projet purement EJB ;
• soit pour envoyer la réponse SOAP, le service web a accédé à la méthode [[Link]()], ce qui a forcé
EclipseLink à aller chercher l'indemnité de l'employé. On sait qu'EclipseLink sait faire cela alors qu'avec Hibernate, ce cas
d'utilisation provoque une exception ;
Le lecteur est invité à tester de la même façon la méthode [4] en lui passant les trois paramètres qu'elle attend.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
186/489
Note : si vous n'obtenez pas le résultat ci-dessus mais que vous n'avez pas d'exception, vérifiez que toutes les entités échangées
entre le client et le serveur [Employe, Cotisation, Indemnite, FeuilleSalaire, ElementsSalaire, PamException] ont des setters
publics.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
187/489
• en [2], nous sélectionnons le nouveau projet et activons le bouton [New File] ;
• en [3], nous indiquons que nous voulons créer un client de service web ;
• en [9], est affichée l'URL de définition du service web. Cette URL est utilisée par les outils logiciels qui génèrent la couche
cliente qui va s'interfacer avec le service web ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
188/489
Couche C S Couche Couche Couche Couche
[ui] [metier] [DAO] [JPA / [JDBC] SGBD BD
2 EclipseLink]
3 4
1 HTTP /
SOAP
Java SE Java EE - serveur Glassfish
• la couche cliente [C] [1] qui va être générée est constituée d'un ensemble de classes Java qui vont être mises dans un même
paquetage. Le nom de celui-ci est fixé en [10] ;
• une fois l'assistant de création du client du service web terminé avec le bouton [Finish], la couche [C] ci-dessus est créée ;
• en [12] ci-dessus, apparaît une arborescence [Generated Sources] qui contient les classes de la couche [C] qui permettent
au client [3] de communiquer avec le service web. Cette couche permet au client [3] de communiquer avec la couche
[metier] [4] comme si elle était locale et non distante ;
• en [11], apparaît une arborescence [Web Service References] qui liste les services web pour lesquels une couche cliente a
été générée ;
On notera que dans la couche [C] [12] générée, nous retrouvons des classes qui ont été déployées côté serveur : Indemnite,
Cotisation, Employe, FeuilleSalaire, ElementsSalaire, Metier. Metier est le service web et les autres classes sont des classes
nécessaires à ce service. On pourra avoir la curiosité de consulter leur code. On verra que la définition des classes qui, instanciées,
représentent des objets manipulés par le service, consiste en la définition des champs de la classe et de leurs accesseurs ainsi qu'à
l'ajout d'annotations permettant la sérialisation de la classe en flux XML. La classe Metier est devenue une interface avec dedans
les deux méthodes qui ont été annotées @WebMethod. Chacune de celles-ci donne naissance à deux classes, par exemple
[[Link]] et [[Link]], où l'une encapsule l'appel à la méthode et l'autre son résultat.
Enfin, la classe MetierService est la classe qui permet au client d'avoir une référence sur le service web Metier distant :
1. @WebEndpoint(name = "MetierPort")
2. public Metier getMetierPort() {
3. return [Link](new QName("[Link] "MetierPort"), [Link]);
4. }
La méthode getMetierPort de la ligne 2 permet d'obtenir une référence sur le service web Metier distant.
• en [1], la classe du client du service web. La classe [MainRemote] présente des erreurs. Pour les corriger, on commencera
par supprimer toutes les instructions [import] existantes dans la classe et on les régènerera par l'option [Fix Imports]. En
effet, certaines des classes utilisées par la classe [MainRemote] font désormais partie du package [client] généré ;
• en [2] ci-dessous, le morceau de code où la couche [metier] est instanciée. Elle l'est avec du code Spring pour obtenir une
référence sur un EJB distant ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
189/489
Nous faisons évoluer le code de la façon suivante :
• en [3], il nous reste à obtenir une référence sur la service web distant [Metier] afin de pouvoir appeler sa méthode
[calculerFeuilleSalaire] ;
• en [4], avec la souris, nous tirons (drag) la méthode [calculerFeuilleSalaire] du service web [Metier] pour la déposer (drop)
en [4]. Du code est généré [6]. Ce code générique peut être ensuite adapté par le développeur ;
• tout d'abord, le code généré en [5] est erroné parce que les classes générées dans une étape précédente sont dans le
package [client] et non [metier] (lignes 56, 57) ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
190/489
• ligne 63, on voit que [calculerFeuilleSalaire] est une méthode de la classe [[Link]().getMetierPort()] (lignes 56-
57). Maintenant que nous savons comment obtenir la couche [metier], le code précédent peut être réécrit de la façon
suivante :
1. // c'est bon - on peut demander la feuille de salaire
2. FeuilleSalaire feuilleSalaire;
3. // couche [metier]
4. [Link] metier = new [Link]().getMetierPort();
5. try {
6. // calcul de la feuille de salaire
7. feuilleSalaire = [Link](args[0], nbHeuresTravaillées, nbJoursTravaillés);
8. } catch (Exception ex) {
9. [Link]([Link]("L'erreur suivante s'est produite : %s", ex));
10. return;
11. }
La ligne 4 récupère une référence sur le client local du service web MetierService. Ceci fait, le code de la classe ne change pas.
• s'assurer que le SGBD MySQL5 est lancé, que la base dbpam_eclipselink est créée et initialisée ;
• s'assurer que le service web est déployé sur le serveur Glassfish ;
• construire le client (Clean and Build) ;
• configurer l'exécution du projet client ;
• exécuter le client ;
L'exécution du client est ici beaucoup plus rapide que ce qu'elle était pour le client du serveur EJB. Les résultats dans la console
sont les suivants :
1. ...
2. Valeurs saisies :
3. N° de sécurité sociale de l'employé : 254104940426058
4. Nombre d'heures travaillées : 150
5. Nombre de jours travaillés : 20
6.
7. Informations Employé :
8. Nom : Jouveinal
9. Prénom : Marie
10. Adresse : 5 rue des oiseaux
11. ...
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
191/489
Travail à faire : porter le client swing du projet [mv-pam-client-ejb-metier-dao-jpa-eclipselink-2] dans le nouveau projet afin que
lui aussi soit client du service web déployé sur le serveur Glassfish.
Client Conteneur
Conteneur web Jpa
du EJB3 Données
service web tcp-ip serveur Java EE
Le service web est assuré par une application web exécutée au sein du conteneur web du serveur Glassfish. Ce service web va
s'appuyer sur l'EJB [Metier] déployé lui dans le conteneur EJB3.
Sur le schéma ci-dessous, l'application web créée va s'exécuter dans le conteneur web. Elle va utiliser l'EJB [Metier] qui lui, sera
déployé dans le conteneur EJB du serveur.
Client Conteneur
Conteneur web Jpa
du EJB3 Données
service web tcp-ip serveur Java EE
Pour que l'application web créée ait accès aux classes associées à l'EJB [Metier], nous ajoutons aux bibliothèques de l'application
web [mv-pam-ws-ejb-metier-dao-eclipselink], la dépendance du serveur EJB [mv-pam-ejb-metier-dao-eclipselink] déjà étudié.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
192/489
• en [1], on ajoute un projet aux dépendances du projet web,
• en [2], on sélectionne le projet [mv-pam-ejb-metier-dao-eclipselink],
• en [3], la portée de la dépendance est provided, c'est à dire qu'elle sera fournie par l'environnement d'exécution,
• en [4], la dépendance a été ajoutée.
• lignes 6-9 : la classe importe des classes du module EJB [mv-pam-ejb-metier-dao-eclipselink] dont le projet Maven a été
ajouté aux dépendances du projet ;
• ligne 11 : la classe est un service web qui va par défaut exposer les méthodes des lignes 18 et 23 ;
• ligne 12 : elle implémente l'interface IMetier définie dans le module EJB ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
193/489
• lignes 14-15 : l'interface locale de l'EJB [Metier] est injectée dans le champ de la ligne 15. Nous utilisons l'interface locale
car l'application web et le module EJB s'exécutent dans la même JVM ;
• lignes 18 et 23 : les méthodes calculerFeuilleSalaire et findAllEmployes délèguent leur traitement aux méthodes de même nom
de l'EJB [Metier]. La classe ne sert donc qu'à exposer à des clients distants les méthodes de l'EJB [Metier] comme des
méthodes d'un service web.
Dans Netbeans, l'application web est reconnue comme exposant un service web :
Pour déployer le service web sur le serveur Glassfish, il nous faut à la fois déployer :
Pour cela, nous avons besoin de créer une application de type [Enterprise Application] qui va déployer les deux modules en même
temps. Pour ce faire, il faut que les deux projets soient chargés dans Netbeans [2].
• en [6], nous configurons le projet. La version de Java EE sera Java EE 7. Un projet d'entreprise peut être créé avec deux
modules : un module EJB et un module Web. Ici, le projet d'entreprise va encapsuler le module Web et le module EJB
déjà créés et chargés dans Netbeans. Donc nous ne demandons pas la création de nouveaux modules [7-8] ;
• en [9], le projet d'entreprise [mv-pam-webapp-ear] ainsi créé. Un autre projet Maven a été créé en même temps [mv-pam-
webapp]. Nous ne nous en occuperons pas ;
• en [10], nous ajoutons des dépendances au projet d'entreprise [mv-pam-webapp-ear] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
194/489
• en [11], on ajoute le projet web de type war ;
• en [12], on ajoute le projet EJB de type ejb ;
Nous construisons le projet d'entreprise par un Clean and Build. Nous sommes quasiment prêts à le déployer sur le serveur Glassfish.
Auparavant il peut être nécessaire de décharger les applications déjà chargées sur le serveur afin d'éviter d'éventuels conflits de
noms d'EJB [14] :
Le serveur MySQL doit être lancé et la base [dbpam_hibernate] disponible et remplie. Ceci fait, l'application d'entreprise peut être
déployée [15]. En [16], on peut voir qu'elle a bien été déployée sur le serveur Glassfish. Comme l'application déployée est une
application web, une page par défaut est affichée dans le navigateur par défaut du poste :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
195/489
Nous pouvons tester le service web qui vient d'être déployé :
Notes :
• parfois l'option [Test Web Service] est inactive. Le plus souvent, c'est parce que le projet web n'a pas été associé à un
serveur. Il faut alors aller dans les propriétés du projet [Properties / Run] du projet web et fixer le serveur qui va exécuter
le service web ;
Le service web est assuré par une application web exécutée au sein du conteneur web du serveur Tomcat. L'architecture de
l'application sera la suivante :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
196/489
Couche Couche Couche Interface Implémentation Couche
[web] [metier] [DAO] [JPA] [Hibernate] [JDBC]
Spring
7
Nous nous appuierons sur le projet [mv-pam-spring-hibernate] construit au paragraphe 1.4.10, page 123 :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
197/489
Nous complétons le fichier [[Link]] pour y inclure les nouvelles dépendances suivantes :
1. ...
2. <dependencies>
3. <!-- spring / hibernate -->
4. <dependency>
5. <groupId>${[Link]}</groupId>
6. <artifactId>mv-pam-spring-hibernate</artifactId>
7. <version>${[Link]}</version>
8. </dependency>
9. <!-- Apache CXF dependencies -->
10. <dependency>
11. <groupId>[Link]</groupId>
12. <artifactId>cxf-rt-frontend-jaxws</artifactId>
13. <version>3.1.7</version>
14. </dependency>
15. <dependency>
16. <groupId>[Link]</groupId>
17. <artifactId>cxf-rt-transports-http</artifactId>
18. <version>3.1.7</version>
19. </dependency>
20. <!-- Spring framework -->
21. <dependency>
22. <groupId>[Link]</groupId>
23. <artifactId>spring-web</artifactId>
24. <version>[Link]</version>
25. </dependency>
26.
27. </dependencies>
7 Spring
L'application web que nous allons construire est une application web standard du monde JEE. A ce titre, elle est configurée par un
fichier [WEB-INF / [Link]] que nous créons maintenant :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
198/489
Les appels au service web que nous allons construire sont gérés par une servlet du framework CXF. Cela se traduit dans le fichier
[WEB-INF / [Link]] de la façon suivante :
1. <?xml version="1.0" encoding="UTF-8"?>
2.
3. <web-app xmlns="[Link]
4. xmlns:xsi="[Link]
5. xsi:schemaLocation="[Link] [Link]
6. version="3.1">
7. <!-- listener Spring -->
8. <listener>
9. <listener-class>[Link]</listener-class>
10. </listener>
11. <!-- Configuration de CXF -->
12. <servlet>
13. <servlet-name>CXFServlet</servlet-name>
14. <servlet-class>[Link]</servlet-class>
15. <load-on-startup>1</load-on-startup>
16. </servlet>
17. <servlet-mapping>
18. <servlet-name>CXFServlet</servlet-name>
19. <url-pattern>/ws/*</url-pattern>
20. </servlet-mapping>
21. <!-- session -->
22. <session-config>
23. <session-timeout>
24. 30
25. </session-timeout>
26. </session-config>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
199/489
27. </web-app>
• le framework CXF a une dépendance sur Spring (les archives Spring nécessaires sont automatiquement téléchargées).
Lignes 8-10 : un listener est déclaré. La classe correspondante [ContextLoaderListener] va être chargée en même temps
que l'application web ;
• lignes 12-16 : la servlet CXF qui va gérer les appels au service web que nous allons créer ;
• lignes 17-20 : les URL traitées par la servlet CXF seront du type /ws/*. Les autres ne seront pas traitées par CXF ;
Pour définir le service web, nous définissons une interface et son implémentattion :
• ligne 7 : l'interface [IWsMetier] dérive de l'interface [IMetier] de la couche [métier] du projet [mv-pam-spring-hibernate] ;
• ligne 6 : l'interface [IWsMetier] est celle d'un service web ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
200/489
Le listener Spring du fichier [WEB-INF/[Link]] (lignes 8-10) ci-dessous :
1. <?xml version="1.0" encoding="UTF-8"?>
2.
3. <web-app xmlns="[Link]
4. xmlns:xsi="[Link]
5. xsi:schemaLocation="[Link] [Link]
6. version="3.1">
7. <!-- listener Spring -->
8. <listener>
9. <listener-class>[Link]</listener-class>
10. </listener>
11. <!-- Configuration de CXF -->
12. ...
13. </web-app>
exploite par défaut un fichier de configuration [WEB-INF/ [Link]]. Son contenu sera le suivant :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="[Link]
3. xmlns:xsi="[Link]
4. xmlns:jaxws="[Link]
5. xsi:schemaLocation="
6. [Link]
7. [Link]
8. [Link]
9. [Link]
10.
11. <!-- Apache CXF -->
12. <import resource="classpath:META-INF/cxf/[Link]" />
13.
14. <!-- couches basses -->
15. <import resource="classpath:[Link]" />
16.
17. <!-- service web -->
18. <bean id="wsMetier" class="[Link]">
19. <property name="metier" ref="metier"/>
20. </bean>
21. <jaxws:endpoint id="wsmetier"
22. implementor="#wsMetier"
23. address="/metier">
24. </jaxws:endpoint>
25. </beans>
• ligne 12 : on importe un fichier de configuration Apache CXF. Il sera trouvé dans l'une des archives jar amenées par les
dépendances Apache CXF ;
• lignes 4, 8-9 : des espaces de noms spécifiques à Apache CXF sont déclarés ;
• ligne 15 : on importe le fichier de configuration Spring du projet [mv-pam-spring-hibernate] ;
• lignes 18-24 : définissent le bean du service web avec sa dépendance sur la couche [métier] (ligne 19) ;
• lignes 21-24 : définissent le service web lui-même,
• ligne 21 : le bean Spring implémentant le service web est celui défini lignes 18-20 ;
• ligne 26 : définit l'URL à laquelle le service web sera disponible, ici /metier. Combinée à la forme que
doivent avoir les URL traitées par Apache CXF (cf fichier [Link]), cette URL devient /ws/metier.
Notre projet est prêt à être exécuté. Nous l'exécutons (Run). Nous obtenons la page web suivante :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
201/489
La page liste tous les services web déployés. Ici, il n'y en a qu'un. Nous suivons le lien WSDL :
Le texte affiché [1] est celui d'un fichier XML qui définit les fonctionnalités du service web, comment l'appeler et quelles réponses il
envoie. On notera l'URL [2] de ce fichier WSDL. Tous les clients du service web ont besoin de la connaître.
Note 1 : pour indiquer l'URL du fichier WSDL du service web, on suivra la procédure [3-5]. On mettra en [4], l'URL notée
précédemment en [2] ci-dessus.
Note 2 :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
202/489
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9.
10. public class JUnitMetierRemote {
11.
12. // couche métier
13. static private [Link] metier;
14.
15. @BeforeClass
16. public static void init() {
17. // couche [metier]
18. metier = new [Link]().getPamWsMetierPort();
19. }
20.
21. @AfterClass
22. static public void terminate() {
23. }
24.
25. // logs
26. static private void log(String message) {
27. [Link]("----------- " + message);
28. }
29.
30. // test
31. @Test
32. public void test01() {
33. [Link](72.4, [Link]("260124402111742", 30, 5).getElementsSalaire().getSalaireNet(),
1e-6);
34. [Link](368.77, [Link]("254104940426058", 150,
20).getElementsSalaire().getSalaireNet(), 1e-6);
35. boolean erreur = false;
36. try {
37. [Link]("xx", 150, 20);
38. } catch (Exception ex) {
39. erreur = true;
40. }
41. [Link](erreur);
42. }
43.
44. @Test
45. public void test02() {
46. // employés
47. List<Employe> employes = [Link]();
48. [Link](employes);
49. }
50. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
203/489
Lorsque côté client distant, on voit une erreur de type [Marshalling / Unmarshalling] [1], le plus probable est qu'il y a eu une erreur
côté serveur. Il faut donc aller voir dans les logs console du serveur, ici le serveur Tomcat [2-4] :
L'erreur [4] est une erreur Hibernate liée au mode [LAZY] du champ JPA [[Link]]. Comme il avait été remarqué au
paragraphe [Link], page 184, la méthode [getAllEmployes] du service SOAP renvoie les employés avec leurs indemnités. Ceci est
surprenant, puisque le champ JPA [[Link]] a l'attribut [LAZY] qui fait que lorsqu'on demande un employé, on n'a pas
son indemnité avec. Il avait été alors indiqué que le service SOAP faisait peut-être implicitement un appel à la méthode
[[Link]]. Hibernate et EclipseLink ne se comportent alors pas de la même façon :
• Hibernate : l'exécution de la méthode [[Link]] provoque l'exécution d'une requête JPA pour récupérer
l'indemnité de l'employé. Cette requête n'est possible pour Hibernate que s'il y a un contexte de persistance. Or pour
l'exécution de la méthode [metier].getAllEmployes, un contexte de persistance Hibernate est créé puis fermé. Lorsque le
service web SOAP fait appel à la méthode [[Link]], le contexte de persistance n'existe plus et Hibernate
lance alors une exception ;
• EclipseLink : dans les mêmes conditions, EclipseLink ne lance pas d'exception et ramène correctement l'indemnité ;
L'erreur [4] [no Session] indique qu'Hibernate n'a pas pu faire la requête JPA parce que le contexte de persistance, appelé session
par Hibernate, n'existait pas.
Pour que le test [test02] passe, il faut donc inhiber le mode [LAZY] du champ JPA [[Link]], ce qui est fâcheux.
Quoiqu'on fasse, lorsqu'on demandera les employés, on les aura avec leurs indemnités, ce qui parfois consommera de la bande
passante inutilement. De façon générale, lorsqu'on exploite une base de données, des relations de clé étrangère peuvent relier
plusieurs tables entre elles. Le service web SOAP, qui suit les liens de clé étrangère, ramènera des données dont on n'a pas
forcément besoin. Nous n'essaierons pas de résoudre ce problème avec le protocole
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
204/489
• en [2-4], on enlève la dépendance sur le projet [mv-pam-spring-hibernate] ;
• en [5-9] : on crée une dépendance sur le projet [mv-pam-spring-eclipselink] du paragraphe 1.4.12, page 134 (le projet doit
être chargé dans Netbeans pour apparaître en [9]) ;
A ce stade, le projet ne présente plus d'erreurs de configuration [10]. Nous modifions ensuite le fichier [META-INF / [Link]]
qui configure le contexte de l'application web :
Le fichier [META-INF / [Link]] du projet [mv-pam-sw-spring-tomcat-hibernate] avait été généré par l'IDE et était le suivant :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <Context path="/mv-pam-ws-spring-tomcat-hibernate"/>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
205/489
• ligne 2 : on donne un nom à l'application (attribut path du contexte). Ce nom indique comment appeler l'application depuis
un navigateur web. Les URL demandées sont de la forme [[Link] et donc ici on aura
[contexte=mv-pam-ws-spring-tomcat-hibernate]. On peut mettre n'importe quel nom. Netbeans génère par défaut un
contexte qui porte le nom du projet ;
On peut se demander d'où vient la page qui est affichée en [3-4]. Elle provient du fichier [[Link]] du projet [5-7] :
La page [[Link]] est la page affichée par défaut lorsqu'on demande la racine du contexte [/mv-pam-ws-spring-tomcat-
eclipselink]. Cela peut être configuré dans le fichier [WEB-INF / [Link]] du projet web [8].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
206/489
puis suivons le lien [2]. Nous obtenons la page suivante :
Cette page représente la définition du service web déployé. Notez l'URL [4]. Vous allez en avoir besoin pour générer un client du
service web. Nous créons maintenant celui-ci.
Ensuite nous régénérons le client du service web dans le nouveau projet. Pour cela, le service web [/mv-pam-ws-spring-tomcat-
eclipselink] doit être lancé :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
207/489
Mettez en [8], l'URL de définition du service web déployé. Vous avez noté cette URL précédemment. Dans cet exemple, c'est
l'URL [[Link] Mettez en [9], le package [client]. C'est celui
qui a été utilisé dans le précédent projet [mv-pam-ws-spring-tomcat-hibernate] et l'application console a été construite pour l'utiliser.
A l'issue de l'assistant, une référence sur le service web distant a été ajoutée au projet [9] et le code nécessaire pour communiquer
avec celui-ci a été généré en [10].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
208/489
Maintenant, exécutons le test [JUnitMetierRemote] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
209/489
32. public void test01() {
33. [Link](72.4, [Link]("260124402111742", 30, 5).getElementsSalaire().getSalaireNet(),
1e-6);
34. [Link](368.77, [Link]("254104940426058", 150,
20).getElementsSalaire().getSalaireNet(), 1e-6);
35. boolean erreur = false;
36. try {
37. [Link]("xx", 150, 20);
38. } catch (Exception ex) {
39. erreur = true;
40. }
41. [Link](erreur);
42. }
43.
44. @Test
45. public void test02() {
46. // employés
47. List<Employe> employes = [Link]();
48. [Link](employes);
49. }
50. }
Dans le test [test02] des lignes 44-49, le service web ramène les employés avec leurs indemnités alors que le champ JPA
[[Link]] a l'attribut [LAZY]. Ce champ est demandé alors que le contexte de persistance qui avait été ouvert pour
récupérer les employés a été fermé. Cela provoque une exception avec Hibernate mais pas avec EclipseLink.
1.7.5 Conclusion
Nous avons déployé des services web SOAP dans différents environnements :
Ils ont tous eu la particularité de ramener les employés avec leurs indemnités. Sans configuration supplémentaire, il n'est pas
possible de faire autrement. Nous ne creuserons pas davantage les services web SOAP. Dans les références [ ref4] [ref5], il est
montré comment créer des services web / jSON qui permettent de respecter l'éventuel mode [LAZY] présent sur les champs
[@ManyToOne] ou [@OneToMany] des entités JPA. Ce type de services web a tendance à supplanter les services web SOAP (sept
2016).
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
210/489
Introduction à Java EE
par l'exemple
- Partie 2 -
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
211/489
1.8 Introduction à Java Server Faces
On lira le document Introduction à Java Server Faces, Primefaces et Primefaces mobile à l'URL
[[Link] On suivra le tutoriel sur Java Server Faces (JSF).
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
212/489
1.9 Version 5 - Application PAM Web / JSF
7 Serveur Glassfish
Dans cette version, le serveur Glassfish hébergera la totalité des couches de l'application :
• la couche [web] est hébergée par le conteneur de servlets du serveur (1 ci-dessous)
• les autres couches [metier, DAO, JPA] sont hébergées par le conteneur EJB3 du serveur (2 ci-dessous)
Les éléments [metier, DAO] de l'application s'exécutant dans le conteneur EJB3 ont déjà été écrits dans l'application client / serveur
étudiée au paragraphe 1.6.1, page 160 et dont l'architecture était la suivante :
client serveur
Couche Couche Couche Couche Couche
[ui] é
[métier] [DAO] [JPA / [JDBC]
EclipseLink]
Les couches [metier, DAO] s'exécutaient dans le conteneur EJB3 du serveur Glassfish et la couche [ui] dans une application
console ou swing sur une autre machine :
client serveur
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
213/489
Conteneur web Conteneur EJB3 Jpa 3
Navigateur [web / JSF2] [métier, DAO] 2 EclipseLink SGBD
1
HTTP serveur Java EE
Seule la couche [web / JSF2] est à écrire. Les autres couches [metier, DAO, JPA] sont acquises.
Dans le document [ref3], il est montré qu'une application web où la couche web est implémentée avec Java Server Faces a une
architecture similaire à la suivante :
Application web
couche [web]
2a 2b
1
Faces Servlet Gestionnaire
3 d'évts couche Données
Navigateur JSF1
[métier, dao, jpa]
4b JSF2 2c
Modèles 4a
JSFn
Cette architecture implémente le Design Pattern MVC (Modèle, Vue, Contrôleur). Le traitement d'une demande d'un client se
déroule de la façon suivante :
Si la demande est faite avec un GET, les deux étapes suivantes sont exécutées :
1. demande - le client navigateur fait une demande au contrôleur [Faces Servlet]. Celui-ci voit passer toutes les demandes des
clients. C'est la porte d'entrée de l'application. C'est le C de MVC ;
2. réponse - le contrôleur C demande à la page JSF choisie de s'afficher. C'est la vue, le V de MVC. La page JSF utilise un modèle
M pour initialiser les parties dynamiques de la réponse qu'elle doit envoyer au client. Ce modèle est une classe Java qui peut faire
appel à la couche [métier] [4a] pour fournir à la vue V les données dont elle a besoin ;
Si la demande est faite avec un POST, deux étapes supplémentaires s'insèrent entre la demande et la réponse :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
214/489
JSF2, les définitions des beans peuvent se faire à l'aide d'annotations et les transitions entre pages peuvent se faire en
" dur " dans le code des beans ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
215/489
C
Cette version calcule un salaire fictif. Il ne faut pas prêter attention au contenu de la page mais à sa mise en forme. Lorsqu'on utilise
le bouton [Raz], on revient à la page [A].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
216/489
Application web
couche [web]
2a 2b
1
Faces Servlet Gestionnaire
3 d'évts couche
Navigateur JSF1 [metier]
4b JSF2 2c simulée
Modèles 4a
JSFn
Lorsque les gestionnaires d'événements ou les modèles demanderont des données à la couche [métier] [2b, 4a], celle-ci leur donnera
des données fictives. Le but est d'obtenir une couche web répondant correctement aux sollicitations de l'utilisateur. Lorsque ceci
sera atteint, il ne nous restera qu'à installer la couche serveur développée au paragraphe 1.6.1, page 160 :
Application web
couche [web]
2a 2b
1
Faces Servlet Gestionnaire
3 d'évts couche Données
Navigateur JSF1 [métier, dao, jpa]
4b JSF2 2c
Modèles 4a
JSFn
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
217/489
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
218/489
L'ajout du framework JSF au projet modifie le fichier [[Link]] qui configure l'application web [10] :
• lignes 7-11 : définissent la servlet Java qui va traiter les demandes faites aux URL de l'application web. Celle-ci sera trouvée
dans la dépendance [12] ;
• lignes 12-15 : définissent le type des URL acceptées, celles de chemin [/contexte/faces/*] où * désigne un chemin
quelconque ;
• lignes 21-23 : définissent la page servis lorsque l'URL [/contexte] est demandée sans précision de chemin. C'est alors la
page [/contexte/faces/[Link]] qui est alors servie. Celle-ci sera trouvée en [11] ci-dessus ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
219/489
5. <h:head>
6. <title>Facelet Title</title>
7. </h:head>
8. <h:body>
9. Hello from Facelets
10. </h:body>
11. </html>
• ligne 22, on voit que l'unique dépendance a l'attribut [provided] : elle est nécessaire pour la compilation du projet mais ne
fera pas partie de l'archive produite pour le projet. A l'exécution, cette dépendance sera trouvée dans les bibliothèques du
serveur Glassfish ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
220/489
C'est la page [[Link]] qui a été affichée.
Nous complétons le projet avec les éléments qui suivent. Ces éléments seront trouvés dans le support du TD.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
221/489
18. </context-param>
19. <!-- pour ignorer les commentaires des pages JSF -->
20. <context-param>
21. <param-name>[Link].FACELETS_SKIP_COMMENTS</param-name>
22. <param-value>true</param-value>
23. </context-param>
24. <!-- durée session -->
25. <session-config>
26. <session-timeout>
27. 30
28. </session-timeout>
29. </session-config>
30. <!-- page d'accueil -->
31. <welcome-file-list>
32. <welcome-file>faces/[Link]</welcome-file>
33. </welcome-file-list>
34. <!-- page d'exception -->
35. <error-page>
36. <error-code>500</error-code>
37. <location>/faces/[Link]</location>
38. </error-page>
39. <error-page>
40. <exception-type>[Link]</exception-type>
41. <location>/faces/[Link]</location>
42. </error-page>
43.
44. </web-app>
Toute exception non explicitement gérée par le code de l'application web provoquera l'affichage d'une page analogue à celle ci-
dessous :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
222/489
9. <locale-config>
10. <default-locale>fr</default-locale>
11. </locale-config>
12. <resource-bundle>
13. <base-name>
14. messages
15. </base-name>
16. <var>msg</var>
17. </resource-bundle>
18. <message-bundle>messages</message-bundle>
19. </application>
20. </faces-config>
• lignes 12-17 : le fichier [[Link]] sera utilisé pour l'internationalisation des pages. Il sera accessible dans les
pages XHTML via la clé msg ;
• ligne 18 : définit le fichier [[Link]] comme devant être exploré en priorité pour les messages d'erreur affichés
par les balises <h:messages> et <h:message>. Cela permet de redéfinir certains messages d'erreur par défaut de JSF ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
223/489
18. [Link]\u00e9nom=Pr\u00e9nom
19. [Link]=Adresse
20. [Link]=Ville
21. [Link]=Code postal
22. [Link]=Indice
23. [Link]=Informations Cotisations sociales
24. [Link]=CSGRDS
25. [Link]=CSGD
26. [Link]=Retraite
27. [Link]=S\u00e9curit\u00e9 sociale
28. [Link]=Informations Indemnit\u00e9s
29. [Link]=Salaire horaire
30. [Link]=Entretien / Jour
31. [Link]=Repas / Jour
32. [Link]\u00e9sPay\u00e9s=Cong\u00e9s pay\u00e9s
33. [Link]=Informations Salaire
34. [Link]=Salaire de base
35. [Link]=Cotisations sociales
36. [Link]=Indemnit\u00e9s d'entretien
37. [Link]=Indemnit\u00e9s de repas
38. [Link]=Salaire net
Ces messages sont tous utilisés dans la page [[Link]] à l'exception de ceux des lignes 11-15 utilisés dans la page
[[Link]].
Cette interface est celle utilisée dans la partie serveur de l'application client / serveur décrite au paragraphe 1.6.1, page 160. Ligne 6,
l'interface [IMetier] est la suivante :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
224/489
1. package metier;
2.
3. import [Link];
4. import [Link];
5.
6. public interface IMetier {
7. // obtenir la feuille de salaire
8. FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés );
9. // liste des employés
10. List<Employe> findAllEmployes();
11. }
La classe Metier que nous allons utiliser pour tester la couche [web] implémente cette interface de la façon suivante :
1. package metier;
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
12.
13. public class Metier implements IMetierLocal, Serializable {
14.
15. // liste des employes
16. private final Map<String, Employe> hashEmployes = new HashMap<>();
17. private List<Employe> listEmployes;
18.
19. // Indemnite(Long id, int version, int indice, double baseHeure, double entretienJour, double repasJour, double
indemnitesCp)
20. // Employe(Long id, int version, String prenom, String nom, String ss, String adresse, String ville, String cp, Indemnite
indemnite)
21. // Cotisation(Long id, int version, double secu, double retraite, double csgd, double csgrds)
22.
23. // obtenir la feuille de salaire
24. @Override
25. public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés) {
26. // on récupère l'employé de n° SS
27. Employe e = [Link](SS);
28. // on rend une exception si l'employé n'existe pas
29. if(e==null){
30. throw new PamException([Link]("L'employé de n° SS [%s] n'existe pas",SS),1);
31. }
32. // on rend une feuille de salaire fictive
33. return new FeuilleSalaire(e, new Cotisation(null, 1, 9.39, 7.88, 6.15, 3.49), new ElementsSalaire(100, 100, 100, 100,
100));
34. }
35.
36. // liste des employés
37. @Override
38. public List<Employe> findAllEmployes() {
39. if (listEmployes == null) {
40. // on crée une liste de deux employés
41. listEmployes = new ArrayList<>();
42. [Link](new Employe(null, 1, "Marie", "Jouveinal", "254104940426058", "5 rue des oiseaux", "St Corentin",
"49203", new Indemnite(null, 1, 2, 2.1, 2.1, 3.1, 15)));
43. [Link](new Employe(null, 1, "Justine", "Laverti", "260124402111742", "La br�lerie"lerie", "St Marcel", "49014",
new Indemnite(null, 1, 1, 1.93, 2, 3, 12)));
44. // dictionnaire des employes
45. for (Employe e : listEmployes) {
46. [Link]([Link](), e);
47. }
48. }
49. // on rend la liste des employés
50. return listEmployes;
51. }
52. }
Nous laissons au lecteur le soin de décrypter ce code. On notera la méthode utilisée : afin de ne pas avoir à mettre en place la partie
EJB de l'application, nous simulons la couche [métier]. Lorsque la couche [web] sera déclarée correcte, nous pourrons alors la
remplacer par la véritable couche [métier].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
225/489
[Link] étape 1
Travail à faire : Construire le formulaire [[Link]] et son modèle [[Link]] nécessaires pour obtenir la page suivante :
• la méthode getEmployes rendra une liste d'employés qu'elle obtiendra auprès de la couche [métier]. Les objets affichés par le
combo auront pour attribut itemValue, le n° SS de l'employé et pour attribut itemLabel, une chaîne constituée du
prénom et du nom de l'employé ;
• les boutons [Salaire] et [Raz] ne seront pour l'instant pas connectés à des gestionnaires d'événement ;
• la validité des saisies sera vérifiée ;
Testez cette version. Vérifiez notamment que les erreurs de saisie sont bien signalées.
Note : il est important que les attributs id des composants de la page ne comportent pas de caractères accentués. Cela peut
planter l'application.
[Link] étape 2
Travail à faire : compléter le formulaire [[Link]] et son modèle [[Link]] pour obtenir la page suivante une fois que le
bouton [Salaire] a été cliqué :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
226/489
Le bouton [Salaire] sera connecté au gestionnaire d'événement calculerSalaire du modèle. Cette méthode utilisera la méthode
calculerFeuilleSalaire de la couche [métier]. Cette feuille de salaire sera faite pour l'employé sélectionné en [1].
Dans le modèle, la feuille de salaire sera représentée par le champ privé suivant :
private FeuilleSalaire feuilleSalaire;
Pour obtenir les informations contenues dans cet objet, on pourra écrire dans la page JSF, des expressions comme la suivante :
<h:outputText value="#{[Link]}"/>
[form].getFeuilleSalaire().getEmploye().getNom() où [form] représente une instance de la classe [[Link]]. Le lecteur pourra vérifier que
les méthodes get utilisées ici existent bien respectivement dans les classes [Form], [FeuilleSalaire] et [Employe]. Si ce n'était pas le
cas, une exception serait lancée lors de l'évaluation de l'expression.
[Link] étape 3
Travail à faire : compléter le formulaire [[Link]] et son modèle [[Link]] pour obtenir les informations supplémentaires
suivantes :
On suivra la même démarche que précédemment. Il y a une difficulté pour le signe monétaire euro que l'on a en [1] par exemple.
Dans le cadre d'une application internationalisée, il serait préférable d'avoir le format d'affichage et le signe monétaire de la locale
utilisée (en, de, fr, ...). Cela peut s'obtenir de la façon suivante :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
227/489
1. <h:outputFormat value="{0,number,currency}">
2. <f:param value="#{[Link]}"/>
3. </h:outputFormat>
On aurait pu écrire :
<h:outputText value="#{[Link]} є">
mais avec la locale en_GB (Anglais GB) on continuerait à avoir un affichage en euros alors qu'il faudrait utiliser la livre £. La balise
<h:outputFormat> permet d'afficher des informations en fonction de la locale de la page JSF affichée :
• ligne 1 : affiche le paramètre {0} qui est un nombre (number) représentant une somme d'argent (currency) ;
• ligne 2 : la balise <f:param> donne une valeur au paramètre {0}. Une deuxième balise <f:param> donnerait une valeur
au paramètre noté {1} et ainsi de suite ;
[Link] étape 4
Lectures conseillées : exemple n° 7 (mv-jsf2-07) dans [ref3].
Travail à faire : compléter le formulaire [[Link]] et son modèle [[Link]] pour gérer le bouton [Raz].
Le bouton [Raz] ramène le formulaire dans l'état qu'il avait lorsqu'on l'a demandé la première fois par un GET. Il y a plusieurs
difficultés ici. Certaines ont été expliquées dans [ref3].
Le formulaire rendu par le bouton [Raz] n'est pas tout le formulaire mais seulement la partie saisie de celui-ci :
Ce résultat peut être obtenu avec une balise <f:subview> utilisée de la façon suivante :
1. <f:subview id="viewInfos" rendered="#{[Link]}">
2. ... la partie du formulaire qu'on veut pouvoir ne pas afficher
3. </f:subview>
La balise <f:subview> encadre toute la partie du formulaire qui est susceptible d'être affichée ou cachée. Tout composant peut être
affiché ou caché grâce à l'attribut rendered. Si rendered="true", le composant est affiché, si rendered="false", il ne l'est pas. Si l'attribut
rendered prend sa valeur dans le modèle, alors l'affichage du composant peut être contrôlé par programme.
accompagné de ses méthodes get et set. Les méthodes gérant les clics sur les bouton [Salaire] [Raz] mettront à jour ce booléen selon
que la vue viewInfos doit être affichée ou non.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
228/489
1.10 Version 6 - Intégration de la couche web dans une architecture 3 couches JSF
/ EJB
Application web
couche [web]
2a 2b
1
Faces Servlet Gestionnaire
3 d'évts couche
Navigateur JSF1 [metier]
4b JSF2 2c simulée
Modèles 4a
JSFn
Nous remplaçons la couche [métier] simulée, par les couches [métier, DAO, JPA] implémentées par des EJB au paragraphe 1.6.1,
page 160 :
Application web
couche [web]
2a 2b
1
Faces Servlet Gestionnaire
3 d'évts couches Données
Navigateur JSF1 [metier, DAO, JPA]
4b JSF2 2c
Modèles 4a
JSFn
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
229/489
Nous avons peu de modifications à faire pour adapter cette couche web à son nouvel environnement : la couche [metier] [2]
simulée doit être remplacée par la couche [metier, DAO, JPA] du serveur construit au paragraphe 1.6.1, page 160. Pour cela, nous
faisons deux choses :
• nous supprimons les paquetages [exception, metier, JPA] qui étaient présents dans le précédent projet ;
• pour compenser cette suppression, nous ajoutons aux dépendances du projet web, le projet du serveur EJB construit au
paragraphe 1.6.1, page 160 ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
230/489
3. public class Form {
4.
5. public Form() {
6. }
7.
8. // couche métier
9. private IMetierLocal metier=new Metier();
10.
11. // champs du formulaire
12. ...
La ligne 9 instanciait la couche [métier] simulée. Désormais elle doit référencer la couche [métier] réelle. Le code précédent devient
le suivant :
1. @ManagedBean
2. @RequestScoped
3. public class Form {
4.
5. public Form() {
6. }
7.
8. // couche métier
9. @EJB
10. private IMetierLocal metier;
11.
12. // champs du formulaire
Ligne 9, l'annotation @EJB indique au conteneur de servlets qui va exécuter la couche web, d'injecter dans le champ metier de la
couche 8, l'EJB qui implémente l'interface locale IMetierLocal.
Pourquoi l'interface locale IMetierLocal plutôt que l'interface IMetierRemote ? Parce que la couche web et la couche EJB s'exécutent
dans la même JVM :
Application web
couche [web]
2a 2b
1
Faces Servlet Gestionnaire
3 d'évts couche Données
Navigateur JSF1 [metier, dao, jpa]
4b JSF2 2c
Modèles 4a
JSFn
Les classes du conteneur de servlets peuvent référencer directement les classes EJB du conteneur EJB.
C'est tout. Notre couche web est prête. La transformation a été simple parce qu'on avait pris soin de simuler la couche [métier] par
une classe qui respectait l'interface IMetierLocal implémentée par la couche [métier] réelle.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
231/489
• en [1], on crée un nouveau projet ;
• en [2], on choisit la catégorie [Maven] ;
• en [3], on choisit le type [Enterprise Application] ;
• en [8], deux projets Maven ont été créés. Le projet d'entreprise est celui qui a le suffixe ear. L'autre projet est un projet
Maven parent du précédent. Nous ne nous en occuperons pas.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
232/489
• en [1], ajout d'une nouvelle dépendance ;
• en [2-4], ajout du projet EJB [mv-pam-ejb-metier-dao-eclipselink]. On notera son type ejb [3] ;
• en [5-7], ajout du projet web [mv-pam-jsf2-ejb]. On notera son type war [6] ;
Avant le déploiement de l'application d'entreprise [mv-pam-webapp-ear], on s'assurera que la base MySQL [dbpam_hibernate]
existe et est remplie. Ceci fait, nous pouvons déployer l'application d'entreprise [mv-pam-webapp-ear] :
• faire un [Clean and Build] sur les projets EJB [mv-pam-ejb-metier-dao-eclipselink], Web [mv-pam-jsf2-ejb] et EAR [mv-
pam-webapp-ear] ;
• exécuter le projet d'entreprise ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
233/489
• en [2-3], l'application d'entreprise [mv-pam-webapp-ear] a bien été déployée sur le serveur Glassfish ;
Le lecteur est invité à refaire les tests de la version web n° 1. Voici un exemple d'exécution :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
234/489
1.11 Version 7 - Application web PAM multi-vues / multi-pages
Nous revenons ici à l'architecture initiale où la couche [métier] était simulée. Nous savons désormais que celle-ci peut être aisément
remplacée par la couche [métier] réelle. La couche [métier] simulée facilite les tests.
Application web
couche [web]
2a 2b
1
Faces Servlet Gestionnaire
3 s d'évts couche
Navigateur JSF1 [metier]
4b JSF2 2c simulée
Modèles 4a
JSFn
• la servlet [Faces Servlet] est le contrôleur générique fourni par JSF. Ce contrôleur est étendu par les gestionnaires
d'événements spécifiques à l'application. Les gestionnaires d'événements rencontrés jusqu'ici étaient des méthodes des
classes servant de modèles aux pages JSF ;
• les pages JSF envoient les réponses au navigateur client. Ce sont les vues de l'application ;
• les pages JSF comportent des éléments dynamiques qu'on appelle le modèle de la page. On rappelle que pour certains
auteurs, le modèle recouvre les entités manipulées par l'application, telles par exemple les classes FeuilleSalaire ou Employe.
Pour distinguer ces deux modèles, on pourra parler de modèle de l'application et modèle d'une page JSF ;
Dans l'architecture JSF, le passage d'une page JSFi à une page JSFj peut être problématique :
• la page JSFi a été affichée. A partir de cette page, l'utilisateur provoque un POST par un événement quelconque [1] ;
• en JSF, ce POST sera traité [2a,2b] en général par une méthode C du modèle M i de la page JSFi. On peut dire que la
méthode C est un contrôleur secondaire ;
• si à l'issue de cette méthode, la page JSFj doit être affichée, le contrôleur C doit :
1. mettre à jour [2c] le modèle Mj de la page JSFj ;
2. rendre [2a] au contrôleur principal, la clé de navigation qui permettra l'affichage de la page JSF j ;
L'étape 1 nécessite que le modèle Mi de la page JSFi ait une référence sur modèle Mj de la page JSFj. Cela complique un
peu les choses rendant les modèles Mi dépendant les uns des autres. En effet, le gestionnaire C du modèle M i qui met à
jour le modèle Mj doit connaître celui-ci. Si on est amené à changer le modèle M j, on sera alors amené à changer le
gestionnaire C du modèle Mi.
Il existe un cas où la dépendance des modèles entre-eux peut être évitée : celui où il y a un unique modèle M qui sert à
toutes les pages JSF. Cette architecture n'est utilisable que dans les applications n'ayant que quelques vues mais elle se
révèle alors très simple d'usage. C'est celle que nous utilisons maintenant.
Application web
couche [web]
2a 2b
1
Faces Servlet [MC]
3 [Link] couche
JSF1 [metier]
4 Modèle M
simulée
JSF2 Gestionnaires
d'évts
JSFn
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
235/489
1.11.1 Les vues de l'application
Les différentes vues présentées à l'utilisateur seront les suivantes :
- la vue [VueSimulations] qui donne la liste des simulations faites par le client
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
236/489
- la vue [VueSimulationsVides] qui indique que le client n'a pas ou plus de simulations :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
237/489
• en [1], les fichiers de configuration,
• en [2], les pages JSF,
• en [3], la feuille de style et l'image de fond des vues,
• en [4], les classes de la couche [web],
• en [5], les couches basses de l'application,
• en [6], le fichier des messages pour l'internationalisation de l'application,
• en [7], les dépendances du projet.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
238/489
59. .erreursHeaders {
60. background: Teal;
61. background-color: #ff6633;
62. color: Snow;
63. font-style: italic;
64. text-align: center
65.
66. }
67.
68. .erreurClasse {
69. background: MediumTurquoise;
70. background-color: #ffcc66;
71. height: 25px;
72. text-align: center
73. }
74.
75. .erreurMessage {
76. background: PowderBlue;
77. background-color: #ffcc99;
78. text-align: left
79. }
Vue Simulations
<h:dataTable value="#{[Link]}" var="simulation"
headerClass="simulationsHeaders"
columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisationsSociales,simuSalaireNet">
Vue Erreur
<h:dataTable value="#{[Link]}" var="erreur"
headerClass="erreursHeaders" columnClasses="erreurClasse,erreurMessage">
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
239/489
25. [Link]=CSGD
26. [Link]=Retraite
27. [Link]=S\u00e9curit\u00e9 sociale
28. [Link]=Informations Indemnit\u00e9s
29. [Link]=Salaire horaire
30. [Link]=Entretien / Jour
31. [Link]=Repas / Jour
32. [Link]\u00e9sPay\u00e9s=Cong\u00e9s pay\u00e9s
33. [Link]=Informations Salaire
34. [Link]=Salaire de base
35. [Link]=Cotisations sociales
36. [Link]=Indemnit\u00e9s d'entretien
37. [Link]=Indemnit\u00e9s de repas
38. [Link]=Salaire net
39. [Link]=| Faire la simulation
40. [Link]=| Effacer la simulation
41. [Link]=| Enregistrer la simulation
42. [Link]=| Retour au simulateur
43. [Link]=| Voir les simulations
44. [Link]=| Terminer la session
45. [Link]=Nom
46. [Link]=Nom
47. [Link]=Pr\u00e9nom
48. [Link]=Heures travaill\u00e9es
49. [Link]=Jours Travaill\u00e9s
50. [Link]=Salaire de base
51. [Link]=Indemnit\u00e9s
52. [Link]=Cotisations sociales
53. [Link]=SalaireNet
54. [Link]=N\u00b0
55. [Link]=Une erreur s'est produite.
56. [Link]=Cha\u00eene des exceptions
57. [Link]=Type de l'exception
58. [Link]=Message associ\u00e9
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
240/489
54. }
55. }
• ligne 11 : l'annotation @Named fait de la classe un bean managé. On notera qu'à la différence du projet précédent, on n'a
pas utilisé l'annotation @ManagedBean. La raison en est que la référence de cette classe doit être injectée dans une autre
classe à l'aide de l'annotation @Inject et que celle-ci n'injecte que des classes annotées @Named ;
• ligne 12 : l'annotation @ApplicationScoped fait de la classe, un objet de portée application. On notera que la classe de
l'annotation est [[Link]] (ligne 6) et non [[Link]] comme
dans les beans du projet précédent ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
241/489
Le bean ApplicationData sert à deux choses :
• ligne 13 : la classe SessionData est un bean managé (@Named) qui pourra être injecté dans d'autres beans managés,
• ligne 14 : il est de portée session (@SessionScoped),
• lignes 18-19 : une référence sur le bean ApplicationData lui est injecté (@Inject),
• lignes 21-32 : les données de l'application qui doivent être maintenues au fil des sessions.
• ligne 21 : la liste des simulations faites par l'utilisateur,
• ligne 22 : le n° de la dernière simulation enregistrée,
• ligne 23 : la dernière simulation qui a été faite,
• lignes 25-30 : les options du menu,
• ligne 32 : la locale de l'application.
Lignes 39-44, la méthode init est exécutée après instanciation de la classe (@PostConstruct). Ici, elle n'est utilisée que pour laisser
une trace de son exécution. On doit pouvoir vérifier qu'elle n'est exécutée qu'une fois par utilisateur puisque la classe est de portée
session. Ligne 42, la méthode utilise le logueur défini dans la classe ApplicationData. C'est pour cette raison qu'on avait besoin
d'injecter une référence sur ce bean (lignes 18-19).
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
242/489
[Link] Le bean Form
Le bean Form est de portée requête :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
12. import [Link];
13. import [Link].*;
14. import [Link];
15. import [Link];
16.
17. @Named
18. @RequestScoped
19. public class Form {
20.
21. public Form() {
22. }
23. // autres beans
24. @Inject
25. private ApplicationData applicationData;
26. @Inject
27. private SessionData sessionData;
28. // le modèle des vues
29. private String comboEmployesValue = "";
30. private String heuresTravaillées = "";
31. private String joursTravaillés = "";
32. private Integer numSimulationToDelete;
33. private List<Erreur> erreurs = new ArrayList<Erreur>();
34. private FeuilleSalaire feuilleSalaire;
35.
36.
37. // liste des employés
38. public List<Employe> getEmployes(){
39. return [Link]().findAllEmployes();
40. }
41.
42. // action du menu
43. public String faireSimulation() {
44. ...
45. }
46.
47. public String enregistrerSimulation() {
48. ...
49. }
50.
51. public String effacerSimulation() {
52. ...
53. }
54.
55. public String voirSimulations() {
56. ...
57. }
58.
59. public String retourSimulateur() {
60. ...
61. }
62.
63. public String terminerSession() {
64. ...
65. }
66.
67. public String retirerSimulation() {
68. ...
69. }
70.
71. private void razFormulaire() {
72. ...
73. }
74.
75. // getters et setters
76. ...
77. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
243/489
1.11.4 Les pages de l'application
[Link] [[Link]]
La page [[Link]] assure la mise en page de toutes les vues :
1. <?xml version='1.0' encoding='UTF-8' ?>
2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[Link]
3. <html xmlns="[Link]
4. xmlns:h="[Link]
5. xmlns:f="[Link]
6. xmlns:ui="[Link]
7.
8. <f:view locale="#{[Link]}">
9. <h:head>
10. <title><h:outputText value="#{msg['[Link]']}"/></title>
11. <h:outputStylesheet library="css" name="[Link]"/>
12. </h:head>
13. <script type="text/javascript">
14. function raz(){
15. // on change les valeurs postées
16. [Link]['formulaire'].elements['formulaire:comboEmployes'].value="0";
17. [Link]['formulaire'].elements['formulaire:heuresTravaillees'].value="0";
18. [Link]['formulaire'].elements['formulaire:joursTravailles'].value="0";
19. }
20. </script>
21. <h:body style="background-image: url('${[Link]}/resources/images/[Link]');">
22. <h:form id="formulaire">
23. <!-- entete -->
24. <ui:include src="[Link]" />
25. <!-- contenu -->
26. <ui:insert name="part1" >
27. Gestion des assistantes maternelles
28. </ui:insert>
29. <ui:insert name="part2"/>
30. </h:form>
31. </h:body>
32. </f:view>
33. </html>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
244/489
• en [1], l'entête [[Link]],
• en [2], le fragment part1.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
245/489
6. xmlns:ui="[Link]
7.
8. <ui:composition template="[Link]">
9. <ui:define name="part1">
10. <ui:include src="[Link]"/>
11. </ui:define>
12. </ui:composition>
13. </html>
Travail à faire : compléter la ligne 13 du code XHTML. La liste des éléments du combo des employés est fournie par une
méthode du bean [Form]. Ecrire cette méthode. Les éléments affichés dans le combo auront leur propriété itemValue égale au n°
SS d'un employé et la propriété itemLabel sera une chaîne formée du prénom et du nom de celui-ci.
Travail à faire : comment doit être initialisé le bean [SessionData] pour que, lors de la requête GET initiale faite au formulaire, le
menu de l'entête soit celui montré ci-dessus ?
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
246/489
A partir de la page précédente, on obtient le résultat suivant :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
247/489
18. <h:outputText value="#{msg['[Link]']}"/>
19. <h:outputText value="#{[Link]}"/>
20. <h:outputText value="#{[Link]}"/>
21. <h:outputText value="#{[Link]}"/>
22. </h:panelGrid>
23. <h:panelGrid columns="3" rowClasses="libelle,info">
24. <h:outputText value="#{msg['[Link]']}"/>
25. <h:outputText value="#{msg['[Link]']}"/>
26. <h:outputText value="#{msg['[Link]']}"/>
27. <h:outputText value="#{[Link]}"/>
28. <h:outputText value="#{[Link]}"/>
29. <h:outputText value="#{[Link]}"/>
30. </h:panelGrid>
31. <br/>
32. <h:outputText value="#{msg['[Link]']}" styleClass="titreInfos"/>
33. <br/><br/>
34. <h:panelGrid columns="4" rowClasses="libelle,info">
35. <h:outputText value="#{msg['[Link]']}"/>
36. <h:outputText value="#{msg['[Link]']}"/>
37. <h:outputText value="#{msg['[Link]']}"/>
38. <h:outputText value="#{msg['[Link]']}"/>
39. <h:outputText value="#{[Link]} %"/>
40. <h:outputText value="#{[Link]} %"/>
41. <h:outputText value="#{[Link]} %"/>
42. <h:outputText value="#{[Link]} %"/>
43. </h:panelGrid>
44. <br/>
45. <h:outputText value="#{msg['[Link]']}" styleClass="titreInfos"/>
46. <br/><br/>
47. <h:panelGrid columns="4" rowClasses="libelle,info">
48. <h:outputText value="#{msg['[Link]']}"/>
49. <h:outputText value="#{msg['[Link]']}"/>
50. <h:outputText value="#{msg['[Link]']}"/>
51. <h:outputText value="#{msg['[Link]ésPayés']}"/>
52. <h:outputFormat value="{0,number,currency}">
53. <f:param value="#{[Link]}"/>
54. </h:outputFormat>
55. <h:outputFormat value="{0,number,currency}">
56. <f:param value="#{[Link]}"/>
57. </h:outputFormat>
58. <h:outputFormat value="{0,number,currency}">
59. <f:param value="#{[Link]}"/>
60. </h:outputFormat>
61. <h:outputText value="#{[Link]} %"/>
62. </h:panelGrid>
63. <br/>
64. <h:outputText value="#{msg['[Link]']}" styleClass="titreInfos"/>
65. <br/><br/>
66. <h:panelGrid columns="4" rowClasses="libelle,info">
67. <h:outputText value="#{msg['[Link]']}"/>
68. <h:outputText value="#{msg['[Link]']}"/>
69. <h:outputText value="#{msg['[Link]']}"/>
70. <h:outputText value="#{msg['[Link]']}"/>
71. <h:outputFormat value="{0,number,currency}">
72. <f:param value="#{[Link]}"/>
73. </h:outputFormat>
74. <h:outputFormat value="{0,number,currency}">
75. <f:param value="#{[Link]}"/>
76. </h:outputFormat>
77. <h:outputFormat value="{0,number,currency}">
78. <f:param value="#{[Link]}"/>
79. </h:outputFormat>
80. <h:outputFormat value="{0,number,currency}">
81. <f:param value="#{[Link]}"/>
82. </h:outputFormat>
83. </h:panelGrid>
84. <br/>
85. <h:panelGrid columns="3" columnClasses="libelle,col2,info">
86. <h:outputText value="#{msg['[Link]']}"/>
87. <h:panelGroup></h:panelGroup>
88. <h:outputFormat value="{0,number,currency}">
89. <f:param value="#{[Link]}"/>
90. </h:outputFormat>
91. </h:panelGrid>
92. </ui:define>
93. </ui:composition>
94. </html>
Travail à faire : écrire la méthode [faireSimulation] de la classe [Form]. La simulation sera enregistrée dans le bean SessionData.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
248/489
5. feuilleSalaire= ...
6. // on met à jour le menu
7. ...
8. // on rend la vue simulation
9. return "simulation";
10. }catch(Throwable th){
11. // on vide la liste des erreurs précédentes
12. ...
13. // on crée la nouvelle liste des erreurs
14. ...
15. // on affiche la vue vueErreur
16. ...
17. // on met à jour le menu
18. ...
19. // on affiche la vue erreur
20. return "erreurs";
21. }
22.}
Les erreurs seront des exceptions dont on mémorise le nom de la classe dans le champ classe et le message dans le champ
message.
Ci-dessus, l'employé [Z Y] n'existe pas dans le dictionnaire des employés utilisé par la couche [métier] simulée. Dans ce cas, la
couche [métier] simulée lance une exception (ligne 6 ci-dessous) :
1. public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés) {
2. // on récupère l'employé
3. Employe e=[Link](SS);
4. // on rend une exception si l'employé n'existe pas
5. if(e==null){
6. throw new PamException([Link]("L'employé de n° SS [%s] n'existe pas",SS),1);
7. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
249/489
8. ...
9. }
Travail à faire : compléter la méthode [faireSimulation] afin que lors d'une exception, elle fasse afficher la vue [vueErreur].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
250/489
Un clic sur le lien [EffacerSimulation] provoque d'abord l'appel de la fonction Javascript raz(). Cette méthode est définie dans la
page [[Link]] :
1. <script type="text/javascript">
2. function raz(){
3. // on change les valeurs postées
4. [Link]['formulaire'].elements['formulaire:comboEmployes'].value="0";
5. [Link]['formulaire'].elements['formulaire:heuresTravaillees'].value="0";
6. [Link]['formulaire'].elements['formulaire:joursTravailles'].value="0";
7. }
8. </script>
Au cours du post, les valeurs postées vont suivre un cheminement normal : validation puis affectation aux champs du modèle. Ceux-
ci sont les suivants dans la classe [Form] :
// le modèle des vues
private String comboEmployesValue;
private String heuresTravaillées;
private String joursTravaillés;
...
Ces trois champs vont recevoir les trois valeurs postées {"0","0","0"}. Une fois cette affectation opérée, la méthode effacerSimulation
va être exécutée.
Travail à faire : écrire la méthode [effacerSimulation] de la classe [Form]. On fera en sorte que :
- seule la zone des saisies soit affichée,
- le combo soit positionné sur son 1er élément,
- les zones de saisie heuresTravaillees et joursTravailles affichent des chaînes vides.
L'action [enregistrerSimulation] associée au lien permet d'enregistrer la simulation courante dans une liste de simulations maintenue
dans la classe [SessionData] :
private List<Simulation> simulations=new ArrayList<Simulation>();
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
251/489
30. }
Le n° de la simulation est un nombre incrémenté à chaque nouvel enregistrement. Il appartient au bean SessionData :
1. // simulations
2. private List<Simulation> simulations = new ArrayList<Simulation>();
3. private int numDerniereSimulation = 0;
4. private Simulation simulation;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
252/489
Le tableau des simulations est affiché par la page [[Link]] :
1. <?xml version='1.0' encoding='UTF-8' ?>
2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[Link]
3. <html xmlns="[Link]
4. xmlns:h="[Link]
5. xmlns:f="[Link]
6. xmlns:ui="[Link]
7.
8. <ui:composition template="[Link]">
9. <ui:define name="part1">
10. <!-- tableau des simulations -->
11. <h:dataTable value="#{[Link]}" var="simulation"
12. headerClass="simulationsHeaders"
columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisationsSociales,simuSalaireN
et">
13. <h:column>
14. <f:facet name="header">
15. <h:outputText value="#{msg['[Link]']}"/>
16. </f:facet>
17. <h:outputText value="#{[Link]}"/>
18. </h:column>
19. <h:column>
20. <f:facet name="header">
21. <h:outputText value="#{msg['[Link]']}"/>
22. </f:facet>
23. <h:outputText value="#{[Link]}"/>
24. </h:column>
25. <h:column>
26. <f:facet name="header">
27. <h:outputText value="#{msg['[Link]']}"/>
28. </f:facet>
29. <h:outputText value="#{[Link]}"/>
30. </h:column>
31. <h:column>
32. <f:facet name="header">
33. <h:outputText value="#{msg['[Link]']}"/>
34. </f:facet>
35. <h:outputText value="#{[Link]ées}"/>
36. </h:column>
37. <h:column>
38. <f:facet name="header">
39. <h:outputText value="#{msg['[Link]']}"/>
40. </f:facet>
41. <h:outputText value="#{[Link]és}"/>
42. </h:column>
43. <h:column>
44. <f:facet name="header">
45. <h:outputText value="#{msg['[Link]']}"/>
46. </f:facet>
47. <h:outputText value="#{[Link]}"/>
48. </h:column>
49. <h:column>
50. <f:facet name="header">
51. <h:outputText value="#{msg['[Link]']}"/>
52. </f:facet>
53. <h:outputText value="#{[Link]}"/>
54. </h:column>
55. <h:column>
56. <f:facet name="header">
57. <h:outputText value="#{msg['[Link]']}"/>
58. </f:facet>
59. <h:outputText value="#{[Link]}"/>
60. </h:column>
61. <h:column>
62. <f:facet name="header">
63. <h:outputText value="#{msg['[Link]']}"/>
64. </f:facet>
65. <h:outputText value="#{[Link]}"/>
66. </h:column>
67. <h:column>
68. <h:commandLink value="Retirer" action="#{[Link]}">
69. <f:setPropertyActionListener target="#{[Link]}" value="#{[Link]}"/>
70. </h:commandLink>
71. </h:column>
72. </h:dataTable>
73. </ui:define>
74. </ui:composition>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
253/489
75. </html>
- l'attribut var="simulation" fixe le nom de la variable représentant la simulation courante à l'intérieur de la balise
<h:datatable> ;
- l'attribut headerClass="simulationsHeaders" fixe le style des titres des colonnes du tableau ;
- l'attribut columnClasses="...." fixe le style de chacune des colonnes du tableau ;
Examinons l'une des colonnes du tableau et voyons comment elle est construite :
simulation désigne la simulation courante de la liste des simulations : d'abord la 1ère, puis la 2ème, ... ;
• [Link] fait référence au champ feuilleSalaire de la simulation courante ;
• [Link] fait référence au champ employe du champ feuilleSalaire ;
• [Link] fait référence au champ nom du champ employe ;
La même technique est répétée pour toutes les colonnes du tableau. Il y a une difficulté pour la colonne Indemnités qui est
générée avec le code suivant :
1. <h:column>
2. <f:facet name="header">
3. <h:outputText value="#{msg['[Link]']}"/>
4. </f:facet>
5. <h:outputText value="#{[Link]}"/>
6. </h:column>
Ligne 5, on affiche la valeur de [Link]. Or la classe Simulation n'a pas de champ indemnites. Il faut se rappeler
ici que le champ indemnites n'est pas utilisé directement mais via la méthode [Link](). Il suffit donc que
cette méthode existe. Le champ indemnites peut lui ne pas exister. La méthode getIndemnites doit rendre le total des indemnités de
l'employé. Cela nécessite un calcul intermédiaire car ce total n'est pas disponible directement dans la feuille de salaire. La méthode
getIndemnites est donnée page 251.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
254/489
[Link] L'action [retourSimulateur]
Le code JSF du lien [retourSimulateur] est le suivant :
<h:commandLink id="cmdRetourSimulateur" immediate="true" value="#{msg['[Link]']}"
action="#{[Link]}" rendered="#{[Link]}"/>
L'action [retourSimulateur] associée au lien permet à l'utilisateur de revenir de la vue [vueSimulations] à la vue [vueSaisies] :
Le résultat obtenu :
Travail à faire : écrire la méthode [retourSimulateur] de la classe [Form]. Le formulaire de saisie présenté doit être vide comme ci-
dessus.
L'action [voirSimulations] associée au lien permet à l'utilisateur d'avoir le tableau des simulations, ceci quelque soit l'état de ses
saisies :
Le résultat obtenu :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
255/489
Travail à faire : écrire la méthode [voirSimulations] de la classe [Form].
On fera en sorte que si la liste des simulations est vide, la vue affichée soit [vueSimulationsVides] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
256/489
Si ci-dessus, on retire la dernière simulation, on obtiendra le résultat suivant :
• ligne 5 : le lien [Retirer] est associé à la méthode [retirerSimulation] de la classe [Form]. Cette méthode a besoin de
connaître le n° de la simulation à retirer. Celui-ci lui est fourni par la balise <f:setPropertyActionListener> de la ligne 8.
Cette balise a deux attributs target et value : l'attribut target désigne un champ du modèle auquel la valeur de l'attribut
value sera affectée. Ici le n° de la simulation à retirer #{[Link]} sera affectée au champ
numSimulationToDelete de la classe [Form] :
1. // le modèle des vues
2. ...
3. private Integer numSimulationToDelete;
Lorsque la méthode [retirerSimulation] de la classe [Form] s'exécutera, elle pourra utiliser la valeur qui aura été stockée
auparavant dans le champ numSimulationToDelete.
L'action [terminerSession] associée au lien permet à l'utilisateur d'abandonner sa session et de revenir au formulaire de saisies vide :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
257/489
Si l'utilisateur avait une liste de simulations, celle-ci est vidée. Par ailleurs, la numérotation des simulations repart à 1.
1.11.6 Intégration de la couche web dans une architecture 3 couches JSF / EJB
L'architecture de l'application web précédente était la suivante :
Application web
couche [web]
2a 2b
1
Faces Servlet [MC]
3 [Link] couche
JSF1 [metier]
4 Modèle M
simulée
JSF2 Gestionnaires
d'évts
JSFn
Nous remplaçons la couche [métier] simulée, par les couches [métier, DAO, JPA] implémentées par des EJB au paragraphe 1.6.1,
page 160 :
Application web
couche [web]
2a 2b
1
Faces Servlet [MC]
3 [Link]
couche
4
JSF1 [metier, DAO, JPA]
Modèle M
JSF2 Gestionnaires
d'évts
JSFn
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
258/489
Travail à faire : réaliser l'intégration des couches JSF et EJB en suivant la méthodologie du paragraphe 1.10, page 229.
La ligne 7 instanciait la couche [métier] simulée. Désormais elle doit référencer la couche [métier] réelle. L'annotation @EJB de la
ligne 6 indique au conteneur de servlets qui va exécuter la couche web, d'injecter dans le champ metier de la ligne 7, l'EJB qui
implémente l'interface locale IMetierLocal.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
259/489
1.12 Versions 8 : Portages de l'application JSF multipages
Application web
couche [web]
2a 2b
1
Faces Servlet [MC]
3 [Link]
couche
4
JSF1 [métier, DAO, JPA]
Modèle M
JSF2 Gestionnaires
d'évts
JSFn
Serveur Tomcat
Nous recopions le projet Maven [mv-pam-jsf2-alone-multipages] dans le projet [mv-pam-jsf2-multipages-spring-hibernate-tomcat]
[1] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
260/489
• nous enlevons la dépendance [javaee-web-api.7.0] [7-9] qui est une dépendance pour un projet Glassfish et dont nous
n'avons pas besoin pour un projet Tomcat ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
261/489
35. <dependency>
36. <groupId>[Link]</groupId>
37. <artifactId>[Link]</artifactId>
38. <version>1</version>
39. </dependency>
40.
41. <!-- couches basses -->
42. <dependency>
43. <groupId>[Link]</groupId>
44. <artifactId>mv-pam-spring-hibernate</artifactId>
45. <version>1.0-SNAPSHOT</version>
46. </dependency>
47. </dependencies>
48.
49. <build>
50. <finalName>mv-pam-jsf2-multipages-spring-hibernate-tomcat</finalName>
51. </build>
52.
53. <properties>
54. <[Link]>UTF-8</[Link]>
55. <[Link]>1.8</[Link]>
56. <[Link]>1.8</[Link]>
57. </properties>
58. </project>
• lignes 42-46 : la dépendance sur le projet [mv-pam-spring-hibernate] que nous venons de créer ;
• lignes 22-32 : les dépendances sur la bibliothèque JSF. Celles-ci étaient fournies par le serveur Glassfish (provided). Ce
n'est pas le cas pour le serveur Tomcat. Il faut donc les embarquer dans l'archive qui va être déployée sur Tomcat ;
• lignes 34-39 : l'annotation [@Inject] qui permet d'injecter un bean JSF dans un autre bean JSF nécessite la bibliothèque
[[Link]] de la ligne 37 ;
• lignes 16-20 : la dépendance sur [spring-web] qui permet d'écrire des applications web avec le support de Spring ;
• lignes 49-51 : on fera attention à ces lignes apparemment inutiles (que font-elles au juste ?). Si on les enlève, l'application
de marche plus. Je n'ai pas d'explications ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
262/489
11. @Named
12. @Scope("application")
13. public class ApplicationData implements Serializable {
14.
15. // couche métier
16. @Autowired
17. private IMetier metier;
18. // logger
19. private final boolean logsEnabled = true;
20. private static final Logger logger = [Link]("pam");
• ligne 12 : l'annotation JSF [@ApplicationScoped] est remplacée par l'annotation Spring (ligne 9) [@Scope('application')]
indique que le bean JSF est de portée [Application] ;
• ligne 17 : l'interface [IMetierLocal] est remplacée par l'interface [IMetier] définie dans le projet [mv-pam-spring-hibernate] ;
• ligne 16 : l'annotation Spring [@Autowired] va nous permettre d'injecter ligne 17, une référence sur le bean de type
[IMetier] défini dans le projet [mv-pam-spring-hibernate] ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
263/489
• la ligne erronée 14 ci-dessus est corrigée comme indiqué en [21] ;
Notre projet n'a désormais plus d'erreurs Java. Il nous reste à configurer l'intégration Spring / JSF. Cela se passe essentiellement
dans le dossier [WEB-INF] :
Le fichier [[Link]] configure l'application web. Il était jusqu'à maintenant entièrement configuré pour JSF. Il évolue désormais de
la façon suivante :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <web-app version="3.1" xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
3. <!-- listener Spring -->
4. <listener>
5. <listener-class>
6. [Link]
7. </listener-class>
8. </listener>
9. <!-- la servlet FacesServlet -->
10. <servlet>
11. <servlet-name>Faces Servlet</servlet-name>
12. <servlet-class>[Link]</servlet-class>
13. <load-on-startup>1</load-on-startup>
14. </servlet>
15. <!-- forme des URL traitées -->
16. ...
17. </web-app>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
264/489
Le fichier [WEB-INF / [Link]] est exploité au démarrage de l'application web par le listener Spring
[ContextLoaderListener] :
• ligne 10 : on indique au listener Spring de chercher les beans JSF dans le package [[Link]]. Il va y trouver les classes
annotées par [@Scope('portée_du_bean')]. Ce sont les beans JSF ;
• ligne 13 : on importe le fichier de configuration Spring du projet des couches basses [mv-pam-spring-hibernate]. On
dispose ainsi de tous les beans des couches basses dont celui de la couche [métier] (lignes 6-9) ci-dessous :
1. <!-- dao -->
2. <bean id="employeDao" class="[Link]"/>
3. <bean id="indemniteDao" class="[Link]"/>
4. <bean id="cotisationDao" class="[Link]"/>
5. <!-- métier -->
6. <bean id="metier" class="[Link]">
7. <property name="employeDao" ref="employeDao"/>
8. <property name="cotisationDao" ref="cotisationDao"/>
9. </bean>
Dans le fichier [[Link]], nous avons indiqué à Spring qu'il devait exploiter les beans qu'il trouverait dans le package
[[Link]]. Il va y trouver la classe [ApplicationData] dont l'annotation [@Scope] fait qu'elle est reconnue comme un bean Spring.
Ligne 6, ci-dessus, l'annotation [@Autowired] va alors être honorée par Spring qui va injecter en ligne 7 le seul bean implémentant
l'interface [IMetier], à savoir celui défini aux lignes 6-9 ci-dessus du fichier [[Link]] qui configure les couches
basses de l'application web.
Il reste un dernier détail. Dans les pages xhtml, nous trouvons des annotations comme les suivantes :
1. <h:commandLink id="cmdFaireSimulation"
2. value="#{msg['[Link]']}"
3. action="#{[Link]}"
4. rendered="#{[Link]}"/>
• lignes 2-4, les expression du type #{expression} sont des expressions qui utilisent des beans JSF. Maintenant ces
expressions utilisent des beans Spring ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
265/489
Pour que ces expressions soient reconnues, il faut modifier le fichier [[Link]] de la façon suivante :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <!-- =========== FULL CONFIGURATION FILE ================================== -->
3. <faces-config version="2.0"
4. xmlns="[Link]
5. xmlns:xsi="[Link]
6. xsi:schemaLocation="[Link] [Link]
facesconfig_2_0.xsd">
7.
8. <!-- le fichier des messages -->
9. <application>
10. <application>
11. <!-- pour que Spring puisse gérer les expression #{expression} dans les pages JSF -->
12. <el-resolver>[Link]</el-resolver>
13. <!-- le fichier des messages -->
14. <resource-bundle>
15. <base-name>
16. messages
17. </base-name>
18. <var>msg</var>
19. </resource-bundle>
20. <message-bundle>messages</message-bundle>
21. </application>
22. </application>
23. </faces-config>
• les expression #{expressions} sont résolues par un programme appelé résolveur La ligne 12 permet de remplacer le
résolveur par défaut de JSF par un résolveur Spring ;
Voilà. On peut exécuter l'application web. On modifie ses propriétés pour qu'elle s'exécute sur le serveur Tomcat :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
266/489
1.12.2 Environnement Spring / EclipseLink / Tomcat
Travail à faire : portez le travail précédent dans un environnement Spring / EclipseLink / Tomcat.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
267/489
Application web
couche [web]
2a 2b
1
Faces Servlet [MC]
3 [Link]
couche
4
JSF1 [métier, DAO, JPA]
Modèle M
JSF2 Gestionnaires
d'évts
JSFn
Serveur EE Glassfish
[Link] Etape 1
Nous allons implémenter les couches basses de l'application web [métier, DAO, JPA] :
Application web
couche [web]
2a 2b
1
Faces Servlet [MC]
3 [Link]
couche
4
JSF1 [métier, DAO, JPA]
Modèle M
JSF2 Gestionnaires
d'évts
JSFn
Serveur EE Glassfish
Ce projet va être déployé sur le serveur Glassfish pour implémenter les couches basses de l'application web JSF / Spring /
Glassfish. A cause de cela, nous supprimons un certain nombre d'éléments inutiles [2-4] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
268/489
Le fichier [[Link]] [7] qui configure la couche JPA est actuellement le suivant :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link]
instance" xsi:schemaLocation="[Link]
[Link]
3. <persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
4. <class>[Link]</class>
5. <class>[Link]</class>
6. <class>[Link]</class>
7. </persistence-unit>
8. </persistence>
Ce fichier de persistence ne peut être déployé sur le serveur EE Glassfish car pour celui-ci, le type de transaction, ligne 3, doit être
obligatoirement JTA. Nous récupérons le fichier [[Link]] du projet [mv-pam-ejb-metier-dao-eclipselink] (paragraphe
[Link].3, page 164 :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link]
instance" xsi:schemaLocation="[Link]
[Link]
3. <persistence-unit name="mv-pam-ejb-metier-dao-eclipselinkPU" transaction-type="JTA">
4. <jta-data-source>jdbc/dbpam_hibernate</jta-data-source>
5. <exclude-unlisted-classes>false</exclude-unlisted-classes>
6. <properties>
7. <property name="[Link]" value="FINE"/>
8. <!-- weaving -->
9. <property name="[Link]" value="static"/>
10. <property name="[Link]" value="true"/>
11. <property name="[Link]" value="true"/>
12. </properties>
13. </persistence-unit>
14. </persistence>
Le fichier de configuration Spring [spring-config-metier-dao] [8] doit lui aussi être modifié. Il est actuellement le suivant :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="[Link]
3. xmlns:xsi="[Link]
4. xmlns:tx="[Link]
5.
6. xsi:schemaLocation="[Link] [Link]
[Link]
7. [Link] [Link]
8. ">
9. <!-- dao -->
10. <bean id="employeDao" class="[Link]"/>
11. <bean id="indemniteDao" class="[Link]"/>
12. <bean id="cotisationDao" class="[Link]"/>
13. <!-- métier -->
14. <bean id="metier" class="[Link]">
15. <property name="employeDao" ref="employeDao"/>
16. <property name="cotisationDao" ref="cotisationDao"/>
17. </bean>
18. <!-- configuration JPA -->
19. <bean id="entityManagerFactory" class="[Link]">
20. <property name="dataSource" ref="dataSource"/>
21. <property name="jpaVendorAdapter">
22. <bean class="[Link]">
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
269/489
23. <property name="databasePlatform" value="[Link]" />
24. </bean>
25. </property>
26. <property name="jpaProperties">
27. <props>
28. <!-- <prop key="[Link]">drop-and-create</prop>
29. <prop key="[Link]">FINE</prop>-->
30. <!-- weaving EclipseLink -->
31. <prop key="[Link]">static</prop>
32. <prop key="[Link]">true</prop>
33. <prop key="[Link]">true</prop>
34. </props>
35. </property>
36. </bean>
37. <!-- la source de donnéees DBCP -->
38. <bean id="dataSource" class="[Link]" destroy-method="close">
39. <property name="driverClassName" value="[Link]"/>
40. <property name="url" value="jdbc:mysql://localhost:3306/dbpam_hibernate"/>
41. <property name="username" value="root"/>
42. <property name="password" value=""/>
43. </bean>
44. <!-- le gestionnaire de transactions -->
45. <tx:annotation-driven transaction-manager="txManager"/>
46. <bean id="txManager" class="[Link]">
47. <property name="entityManagerFactory" ref="entityManagerFactory"/>
48. </bean>
49. <!-- traduction des exceptions -->
50. <bean class="[Link]"/>
51. <!-- persistence -->
52. <bean class="[Link]"/>
53. </beans>
Parce que la couche de persistance est désormais gérée par le serveur EE Glassfish,
• le bean [EntityManagerFactory] des lignes 19-36 doit être modifié ;
• les lignes 37-43 disparaissent. En effet, il n'y a pas lieu d'inclure dans l'application un pool de connexions. Celui-ci est
fourni par défaut par le serveur Glassfish ;
• la ligne 2 désigne l'EntityManagerFactory qui gère la couche de persistance. Il a été associé ici à l'unité de persistance [ mv-
pam-jpa-glassfish-eclipselinkPU] qui est le nom de l'unité de persistance dans le fichier [[Link]] :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link]
instance" xsi:schemaLocation="[Link]
[Link]
3. <persistence-unit name="mv-pam-jpa-glassfish-eclipselinkPU" transaction-type="JTA">
4. ...
5. </persistence-unit>
6. </persistence>
Dans la configuration Spring, le gestionnaire de transactions est actuellement configuré par les lignes suivantes :
1. <!-- le gestionnaire de transactions -->
2. <tx:annotation-driven transaction-manager="txManager"/>
3. <bean id="txManager" class="[Link]">
4. <property name="entityManagerFactory" ref="entityManagerFactory"/>
5. </bean>
• ligne 3, la classe [JpaTransactionManager] doit être changé au profit d'un gestionnaire de transactions JTA, le nouveau type
de transactions défini dans [persistence;xml] ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
270/489
7. [Link] [Link]
8. [Link] [Link]
9. ">
10. <!-- dao -->
11. <bean id="employeDao" class="[Link]"/>
12. <bean id="indemniteDao" class="[Link]"/>
13. <bean id="cotisationDao" class="[Link]"/>
14. <!-- métier -->
15. <bean id="metier" class="[Link]">
16. <property name="employeDao" ref="employeDao"/>
17. <property name="cotisationDao" ref="cotisationDao"/>
18. </bean>
19.
20. <!-- persistence -->
21. <jee:jndi-lookup id="entityManagerFactory" jndi-name="persistence/mv-pam-spring-glassfish-eclipselinkPU"/>
22.
23. <!-- le gestionnaire de transactions -->
24. <tx:jta-transaction-manager/>
25. <tx:annotation-driven/>
26.
27. <!-- traduction des exceptions -->
28. <bean class="[Link]"/>
29. <!-- persistence -->
30. <bean class="[Link]"/>
31. </beans>
Après toutes ces modifications, le fichier [[Link]] peut être allégé de certaines dépendances devenues inutiles. Sa version finale est
la suivante :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <project xmlns="[Link] xmlns:xsi="[Link]
3. xsi:schemaLocation="[Link] [Link]
4.
5. <!-- artifact -->
6. <modelVersion>4.0.0</modelVersion>
7. <groupId>[Link]</groupId>
8. <artifactId>mv-pam-spring-eclipselink-glassfish</artifactId>
9. <version>1.0-SNAPSHOT</version>
10. <packaging>jar</packaging>
11. <name>mv-pam-spring-eclipselink-glassfish</name>
12.
13. <dependencies>
14. <!-- eclipselink -->
15. <dependency>
16. <groupId>[Link]</groupId>
17. <artifactId>eclipselink</artifactId>
18. <version>2.6.3</version>
19. <scope>provided</scope>
20. </dependency>
21. <!-- mysql -->
22. <dependency>
23. <groupId>mysql</groupId>
24. <artifactId>mysql-connector-java</artifactId>
25. <version>5.1.39</version>
26. </dependency>
27. <!-- Spring framework - transactions -->
28. <dependency>
29. <groupId>[Link]</groupId>
30. <artifactId>spring-tx</artifactId>
31. <version>[Link]</version>
32. </dependency>
33. </dependencies>
34.
35. <properties>
36. <[Link]>UTF-8</[Link]>
37. <[Link]>1.8</[Link]>
38. <[Link]>1.8</[Link]>
39. </properties>
40. </project>
• en ligne 19, on peut se permettre de mettre l'attribut [provided] à la dépendance EclipseLink car par défaut le serveur
Glassfish la fournit ;
A ce stade, nous en avons fini avec le projet [mv-pam-spring-glassfish-eclipselink] qui va implémenter les couches basses de
l'application web.
[Link] Etape 2
Nous allons maintenant implémenter la couche web de l'application :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
271/489
Application web
couche [web]
2a 2b
1
Faces Servlet [MC]
3 [Link]
couche
4
JSF1 [métier, DAO, JPA]
Modèle M
JSF2 Gestionnaires
d'évts
JSFn
Serveur EE Glassfish
Nous dupliquons le projet [mv-pam-jsf2-multipages-spring-eclipselink-tomcat] dans le projet [mv-pam-jsf2-multipages-spring-
eclipselink-glassfish] [1] :
• en [2-3], nous supprimons la dépendance [mv-pam-spring-eclipselink] du projet pour la remplacer par la dépendance [mv-
pam-spring-eclipselink-glassfish] [4-5] :
En [6], nous devons modifier le fichier [[Link]] qui configure l'application web. Pour l'instant, celui-ci est le suivant :
1. <?xml version="1.0" encoding="UTF-8"?>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
272/489
2. <web-app version="3.1" xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
3. <!-- listener Spring -->
4. <listener>
5. <listener-class>
6. [Link]
7. </listener-class>
8. </listener>
9. <!-- la servlet FacesServlet -->
10. <servlet>
11. <servlet-name>Faces Servlet</servlet-name>
12. <servlet-class>[Link]</servlet-class>
13. <load-on-startup>1</load-on-startup>
14. </servlet>
15. <!-- forme des URL traitées -->
16. <servlet-mapping>
17. <servlet-name>Faces Servlet</servlet-name>
18. <url-pattern>/faces/*</url-pattern>
19. </servlet-mapping>
20. <!-- pour avoir des logs sur les pages affichées, lors du développement -->
21. <context-param>
22. <param-name>[Link].PROJECT_STAGE</param-name>
23. <param-value>Development</param-value>
24. </context-param>
25. <!-- pour ignorer les commentaires des pages JSF -->
26. <context-param>
27. <param-name>[Link].FACELETS_SKIP_COMMENTS</param-name>
28. <param-value>true</param-value>
29. </context-param>
30. <!-- durée session -->
31. <session-config>
32. <session-timeout>
33. 30
34. </session-timeout>
35. </session-config>
36. <!-- page d'accueil -->
37. <welcome-file-list>
38. <welcome-file>faces/[Link]</welcome-file>
39. </welcome-file-list>
40. <!-- page d'exception -->
41. <error-page>
42. <error-code>500</error-code>
43. <location>/faces/[Link]</location>
44. </error-page>
45. <error-page>
46. <exception-type>[Link]</exception-type>
47. <location>/faces/[Link]</location>
48. </error-page>
49.
50. </web-app>
Sans ces lignes, le nom JNDI [persistence] du code suivant du fichier [[Link]] n'est pas reconnu :
a) <!-- persistence -->
b) <jee:jndi-lookup id="entityManagerFactory" jndi-name="persistence/mv-pam-spring-glassfish-eclipselinkPU"/>
Lignes 3 et 4, nous utilisons le nom de l'unité de persistance définie dans le fichier [[Link]] du projet des couches basses :
<persistence-unit name="mv-pam-spring-glassfish-eclipselinkPU" transaction-type="JTA">
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
273/489
23. <dependency>
24. <groupId>[Link]</groupId>
25. <artifactId>jsf-api</artifactId>
26. <version>2.2.13</version>
27. <scope>provided</scope>
28. </dependency>
29. <dependency>
30. <groupId>[Link]</groupId>
31. <artifactId>jsf-impl</artifactId>
32. <version>2.2.13</version>
33. <scope>provided</scope>
34. </dependency>
35.
36. <!-- JSR-330 -->
37. <dependency>
38. <groupId>[Link]</groupId>
39. <artifactId>[Link]</artifactId>
40. <version>1</version>
41. <scope>provided</scope>
42. </dependency>
43.
44. <!-- couches basses -->
45. <dependency>
46. <groupId>${[Link]}</groupId>
47. <artifactId>mv-pam-spring-eclipselink-glassfish</artifactId>
48. <version>${[Link]}</version>
49. </dependency>
50. </dependencies>
51.
52. <properties>
53. <[Link]>UTF-8</[Link]>
54. <[Link]>1.8</[Link]>
55. <[Link]>1.8</[Link]>
56. </properties>
57. </project>
On notera aux lignes 27, 33 et 41, la balise [<scope>provided</scope>] qui fait que la dépendance ainsi annotée n'est pas incluse
dans l'archive war du projet. C'est ainsi parce que ces trois dépendances peuvent être fournies par Glassfish. Il est donc inutile de
les incorporer dans l'archive du projet déployé sur Glassfish.
[Link] Exécution
Il nous reste à configurer le projet pour qu'il s'exécute sur le serveur Glassfish [1-4] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
274/489
L'exécution du projet donne alors le résultat suivant :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
275/489
1.13 Version 9 : Implémentation de la couche web avec Primefaces
Pré-requis : on lira " Introduction à Primefaces " dans [ref3].
Travail à faire : construire un nouveau projet Maven de type [Web Application] où les pages XHTML de l'exemple précédent
seraient construites avec des composants Primefaces. On ne changera rien aux beans.
La page d'accueil utilisera les composants <p:panel>, <p:inputText>, <p:selectOneMenu>, <p:message>, <p:menubar> :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
276/489
Une simulation :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
277/489
La liste des simulations utilise les composants <p:dataTable>, <p:commandLink> :
• la zone [1] de la copie d'écran ci-dessus vient remplacer la ligne 23 ci-dessus, lorsqu'elle est affichée ;
Les actions Ajax sont toutes contenues dans la page [[Link]] suivante :
1. <?xml version='1.0' encoding='UTF-8' ?>
2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[Link]
3. <html xmlns="[Link]
4. xmlns:h="[Link]
5. xmlns:p="[Link]
6.
7. <!-- titre -->
8. <h2><h:outputText value="#{msg['[Link]']}"/></h2>
9. <!-- menu -->
10. <p:menubar style="background-color: lightblue">
11. <p:submenu label="#{msg['[Link]']}">
12. <p:menuitem id="cmdFaireSimulation" style="width: 200px" value="#{msg['[Link]']}"
actionListener="#{[Link]}" rendered="#{[Link]}" ajax="true" update="contenu" />
13. <p:menuitem id="cmdEffacerSimulation" style="width: 200px" onclick="raz();" immediate="true"
value="#{msg['[Link]']}" actionListener="#{[Link]}"
rendered="#{[Link]}" ajax="true" update="contenu"/>
14. <p:menuitem id="cmdEnregistrerSimulation" style="width: 200px" immediate="true"
value="#{msg['[Link]']}" actionListener="#{[Link]}"
rendered="#{[Link]}" ajax="true" update="contenu"/>
15. </p:submenu>
16. <p:submenu label="Navigation" rendered="#{[Link]}">
17. <p:menuitem id="cmdRetourSimulateur" style="width: 200px" immediate="true"
value="#{msg['[Link]']}" actionListener="#{[Link]}"
rendered="#{[Link]}" ajax="true" update="contenu"/>
18. <p:menuitem id="cmdVoirSimulations" style="width: 200px" immediate="true" value="#{msg['[Link]']}"
actionListener="#{[Link]}" rendered="#{[Link]}" ajax="true" update="contenu"/>
19. </p:submenu>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
278/489
20. <p:menuitem id="cmdTerminerSession" style="width: 200px" value="#{msg['[Link]']}"
actionListener="#{[Link]}" rendered="#{[Link]}" ajax="true" update="contenu"/>
21. </p:menubar>
22.
23. </html>
Conseils :
• dupliquez le projet [mv-pam-jsf2-multipages-spring-eclipselink-glassfish] puis dans les différentes pages XHTML,
remplacez les balises JSF par les balises Primefaces équivanlentes ;
• une feuille de style pour les composants Primefaces vous est fournie dans le support du cours ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
279/489
2 Étude de cas n° 2 : client / serveur - EJB / SOAP
2.2 Le problème
Une société de services en informatique [ISTIA-IAIE] désire proposer un service de prise de rendez-vous. Le premier marché visé
est celui des médecins travaillant seuls. Ceux-ci n'ont en général pas de secrétariat. Les clients désirant prendre rendez-vous
téléphonent alors directement au médecin. Celui-ci est ainsi dérangé fréquemment au cours d'une journée ce qui diminue sa
disponibilité à ses patients. La société [ISTIA-IAIE] souhaite leur proposer un service de prise de rendez-vous fonctionnant sur le
principe suivant :
• un secrétariat assure les prises de RV pour un grand nombre de médecins. Ce secrétariat peut être réduit à une unique
personne. Le salaire de celle-ci est mutualisé entre tous les médecins utilisant le service de RV.
• le secrétariat et tous les médecins sont reliés à Internet
• les RV sont enregistrés dans une base de données centralisée, accessible par Internet, par le secrétariat et les médecins
• la prise de RV est normalement faite par le secrétariat. Elle peut être faite également par les médecins eux-mêmes. C'est le
cas notamment lorsqu'à la fin d'une consultation, le médecin fixe lui-même un nouveau RV à son patient.
Les médecins gagnent en efficacité s'ils n'ont plus à gérer les RV. S'ils sont suffisamment nombreux, leur contribution aux frais de
fonctionnement du secrétariat sera faible.
La société [ISTIA-IAIE] décide de confier à un stagiaire l'élaboration d'une première maquette du service. C'est cette maquette que
nous nous proposons de réaliser ici.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
280/489
Tests Couche Couche Couche Couche
JUnit métier d'accès aux JPA Jdbc Base de
utilisateur (métier) données données
(dao)
SPRING
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
281/489
• VERSION : n° identifiant la version de la ligne dans la table. Ce nombre est incrémenté de 1 à chaque fois qu'une
modification est apportée à la ligne ;
• NOM : le nom du client ;
• PRENOM : son prénom ;
• TITRE : son titre (Melle, Mme, Mr) ;
La seconde ligne de la table [CRENEAUX] (cf [1] ci-dessus) indique, par exemple, que le créneau n° 2 commence à 8 h 20 et se
termine à 8 h 40 et appartient au médecin n° 1 (Mme Marie PELISSIER).
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
282/489
• ID_CLIENT : n° du client pour qui est faite la réservation – clé étrangère sur le champ [ID] de la table [CLIENTS] ;
Cette table a une contrainte d'unicité sur les valeurs des colonnes jointes (JOUR, ID_CRENEAU) :
Si une ligne de la table[RV] a la valeur (JOUR1, ID_CRENEAU1) pour les colonnes (JOUR, ID_CRENEAU), cette valeur ne peut
se retrouver nulle part ailleurs. Sinon, cela signifierait que deux RV ont été pris au même moment pour le même médecin. D'un
point de vue programmation Java, le pilote JDBC de la base lance une SQLException lorsque ce cas se produit.
La ligne d'id égal à 7 (cf [1] ci-dessus) signifie qu'un RV a été pris pour le créneau n° 10 et le client n° 2 le 10/09/2006. La table
[CRENEAUX] nous apprend que le créneau n° 10 correspond au créneau horaire 11 h - 11 h 20 et appartient au médecin n° 1
(Mme Marie PELISSIER). La table [CLIENTS] nous apprend que le client n° 2 est Mme Christine GERMAN.
Note : un script SQL vous est donné afin que vous puissiez construire la base précédente.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
283/489
• en [1], le projet dans son ensemble ;
• en [2] : le package [[Link]] contient les quatre entités JPA images des quatre tables de la base de données ;
• en [3] : le dossier [META-INF] contient le fichier [[Link]] qui configure la couche [JPA] ;
• en [4] : le package [[Link]] contient l'interface [IDao] de la couche [dao] et son implémentation [DaoJpa]. La
couche lance des exceptions de type [RdvMedecinsException] ;
Travail à faire : à l'aide de WampServer / PhpMyAdmin, créez la base MySQL [dbrdvmedecins2] à l'aide du script SQL
[[Link]].
Travail à faire : chargez le projet Netbeans [mv-rdvmedecins-spring-hibernate] et exécutez les tests JUnit [JUnitTestDao] et
[JUnitTestMetier]. Ils doivent réussir.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
284/489
Note : le projet Netbeans [mv-rdvmedecins-spring-hibernate] utilise les dates de Java 8. Le projet doit donc utiliser un JDK 1.8 [1-
3] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
285/489
Tests Couche Couche Couche Couche
JUnit métier d'accès aux JPA Jdbc Base de
(métier) données données
(dao)
Travail à faire : en suivant la méthode vue en TD, créez le projet Maven du serveur EJB, puis déployez celui-ci sur le serveur
Glassfish.
Contraintes :
Note :
Pour une raison inconnue, le serveur Glassfish 4.1.1 (novembre 2016) bogue sur les flux stream (lignes 3-4 ci-dessus). On remplacera
alors le code précédent par le code suivant :
1. @Override
2. public void supprimerTousLesRv() {
3. for (Rv rv : (List<Rv>) [Link]("select rv from Rv rv").getResultList()) {
4. [Link](rv);
5. }
6. }
Une fois le serveur EJB déployé sur Glassfish, des logs analogues aux suivants doivent être présents dans la console Glassfish :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
286/489
1. ...
2. Infos: Portable JNDI names for EJB DaoJpa: [java:global/mv-rdvmedecins-ejb-metier-dao-eclipselink/DaoJpa, java:global/mv-
rdvmedecins-ejb-metier-dao-eclipselink/DaoJpa![Link]]
3. Infos: Portable JNDI names for EJB Metier: [java:global/mv-rdvmedecins-ejb-metier-dao-eclipselink/Metier!
[Link], java:global/mv-rdvmedecins-ejb-metier-dao-eclipselink/Metier!
[Link]]
4. ...
Travail à faire : en suivant la méthode vue en TD, créez le projet Maven d'un client du serveur EJB qui vient d'être déployé. Ce
client ne contiendra que le test JUnit [JUnitTestMetier] présent dans l'application Spring / Hibernate et qui aura été adapté pour
travailler avec un serveur EJB. Vérifiez que le test passe.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
287/489
On désire créer un service web SOAP avec les éléments suivants :
• 2 : le serveur EJB créé précédemment ;
• 3 : une application web qui va implémenter le service web SOAP ;
• 1 : une application d'entreprise qui va servir à déployer ensemble les deux éléments précédents sur le serveur Glassfish ;
Travail à faire : en suivant la méthode vue en TD, écrivez le service web [WsMetier] implémentant l'interface [IWsMetier]. Ceci
fait, déployez ce service web sur le serveur Glassfish. Pour cela, le serveur EJB des questions précédentes doit être déchargé du
serveur Glassfish (undeploy).
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
288/489
Note :
Si à la compilation du projet web, vous obtenez une erreur qui parle de weaving des entités JPA :
===================================
1 - requête
1 - réponse
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
289/489
===================================
2 - requête
2 - réponse
===================================
3 - requête
3 - réponse
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
290/489
===================================
4 - requête
4 - réponse
Ci-dessus, l'exception avec le message [Le médecin n° [200] n'existe pas] a été lancée par la classe [WsMetier] du service web.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
291/489
9. throw new RdvMedecinsException(e, 201);
10. }
11. }
12.
13. @Override
14. public String getAllCreneaux(long idMedecin) {
15. try {
16. return [Link]([Link](localGetMedecinById(idMedecin)));
17. } catch (IOException e) {
18. throw new RdvMedecinsException(e, 202);
19. }
20. }
21.
22. private Medecin localGetMedecinById(long idMedecin) {
23. Medecin medecin = [Link](idMedecin);
24. if (medecin == null) {
25. throw new RdvMedecinsException([Link]("Le médecin n° [%s] n'existe pas", idMedecin), 200);
26. } else {
27. return medecin;
28. }
29. }
La ligne 2 ci-dessus utilise un mappeur jSON fourni par la bibliothèque jSON Jackson. Pour y avoir accès, on ajoutera les
dépendances suivantes au fichier [[Link]] du projet Maven du service web SOAP :
1. <!-- jSON -->
2. <dependency>
3. <groupId>[Link]</groupId>
4. <artifactId>jackson-core-asl</artifactId>
5. <version>1.9.13</version>
6. </dependency>
7. <dependency>
8. <groupId>[Link]</groupId>
9. <artifactId>jackson-mapper-asl</artifactId>
10. <version>1.9.13</version>
11. </dependency>
Pour vérifier la validité d'une date au format 'année-mois-an' (ex : 2016-10-24), on pourra utiliser le code Java 8 suivant :
1. private void checkJour(String jour) {
2. try {
3. [Link](jour, [Link]("yyyy-MM-dd"));
4. } catch (DateTimeParseException e) {
5. throw new RdvMedecinsException(e, 300);
6. }
7. }
La méthode [checkJour] lance une exception si la date est incorrecte et ne fait rien sinon.
Nous voulons porter le test JUnit utilisé dans une architecture client / serveur EJB dans la nouvelle architecture client / service web
SOAP :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
292/489
• 1 : le nouveau projet est un client du service web SOAP développé précédemment ;
• 2 : le package [[Link]] reprend les entités présentes côté serveur. Si ces entités sont des entités JPA, elles sont
totalement débarrassées des annotations JPA qu'elles contiennent ;
• 3 : deux tests JUnit sont là pour tester le bon déploiement du service web SOAP ;
Le test [JUnitTestMetier] est un simple portage du test [JUnitTestMetier] utilisé par deux fois précédemment. Il est simplement
adapté à son nouvel environnement.
Le test [JUnitTestFaults] teste les cas d'erreurs, ceux où le service web SOAP doit lancer une exception : date erronée, n°s médecin,
client, créneau horaire et rendez-vous inexistants.
Travail à faire : en suivant la méthode vue en TD, créez ce nouveau projet avec ses deux tests unitaires. Vérifiez qu'ils passent
tous les deux.
Conseils et contraintes :
• [Link](100);
• [Link](100);
• [Link](100);
• [Link](100);
• [Link](1, "abcd");
• [Link](100, "2016-10-24");
Lorsque le service web [WsMetier] lance une exception de type quelconque, le client reçoit lui une exception de type
[ServerSOAPFaultException]. Si [ex] est une exception de ce type, l'expression [[Link]().getFaultString()] est le
message encapsulé dans l'exception envoyée par le service web SOAP.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
293/489
3 Étude de cas n° 3 : gestion de bilans de formation
3.2 Le problème
On désire gérer les bilans des étudiants d'une formation à l'aide d'une application web. Celle-ci présentera les deux vues suivantes :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
294/489
3.3 L'architecture de l'application
L'application à écrire a l'architecture en couches suivante :
Avec [PhpMyAdmin] :
• exécutez le script [[Link]] qui va créer la base et ses tables et remplir celles-ci ;
• utilisez le script [[Link]] lorsque vous souhaitez remplir les tables (elles seront automatiquement vidées
auparavant) ;
• utilisez le script [[Link]] lorsque vous souhaitez vider les tables sans les remplir ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
295/489
• [ID] : clé primaire générée automatiquement par le SGBD MySQL ;
• [VERSIONING] : le n° de version de la formation. Est incrémenté de 1 à chaque modification. Chaque table a cette
colonne et vous n'avez pas à vous en préoccuper ;
Les deux colonnes précédentes sont présentes dans toutes les tables. Nous ne les présenterons plus.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
296/489
La table [modules] contient la liste des modules des formations de la table [formations]
La table [cours] contient la liste des cours des modules de la table [modules]
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
297/489
La table [bilans] contient les bilans faits par les étudiants de la table [utilisateurs] sur les cours de la table [cours] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
298/489
3.5 Les couches [DAO, JPA]
Serveur
7 Tomcat
L'implémentation des couches [DAO, JPA] vous est donnée ainsi qu'un programme de test de celle-ci :
étape 1 [1]
Il vous faut tout d'abord mettre l'archive [[Link]] dans le dépôt Maven local.
Ceci est fait par le script [[Link]]. Double-cliquez dessus pour l'exécuter.
Note : vous aurez probablement à changer le chemin du dossier Maven dans le script [[Link]].
étape 2 [2]
Videz la base de données en exécutant le script SQL [[Link]]. Vérifiez que la base est désormais vide.
étape 3 [2]
Ouvrez le projet Netbeans [bilan-dao-jpa-tests] et exécutez l'application console [DaoTest] qui remplit la base de données. Vérifiez
que la base de données a été remplie. Cela veut dire que :
• la base de données est correctement installée ;
• l'archive [[Link]] est correctement installée dans le dépôt Maven
local ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
299/489
3.5.2 L'interface de la couche [DAO]
Désormais, l'architecture de l'application est la suivante :
Couche
[web / DAO - JPA - JDBC BD
JSF]
Serveur7Tomcat 7
Le bloc [DAO-JPA-JDBC] est une boîte noire dont vous devez connaître l'interface pour l'exploiter. Cette interface est la suivante :
1. package [Link];
2.
3. import [Link];
4.
5. import [Link];
6. import [Link];
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
300/489
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11.
12. public interface IDao {
13.
14. // utilisateur identifié par login / password
15. Utilisateur getLongUtilisateurByCredentials(String login, String password);
16.
17. Utilisateur getShortUtilisateurByCredentials(String login, String password);
18.
19. // liste des formations
20. List<Formation> getAllShortFormations();
21.
22. // liste des formations d'un utilisateur donné
23. List<Formation> getShortFormationsByUtilisateurId(Long utilisateurId);
24.
25. // liste des modules d'une formation
26. List<Module> getShortModulesByFormationId(Long formationId);
27.
28. // liste des cours d'un module
29. List<Cours> getShortCoursByModuleId(Long moduleId);
30.
31. // liste des cours d'une formation
32. List<Cours> getShortCoursByFormationId(Long formationId);
33.
34. // liste des utilisateurs d'une formation
35. List<Utilisateur> getShortUtilisateursByFormationId(Long formationId, UtilisateurType utilisateurType);
36.
37. // persistance d'un bilan
38. Bilan saveBilan(Bilan bilan);
39.
40. // suppression d'un bilan
41. void deleteBilan(Long bilanId);
42.
43. // liste des bilans d'une formation
44. List<Bilan> getShortBilansByFormationId(Long formationId);
45.
46. // liste des bilans d'un cours
47. List<Bilan> getShortBilansByCoursId(Long coursId);
48.
49. // liste des bilans d'un étudiant pour une formation
50. List<Bilan> getShortBilansByEtudiantIdByFormationId(Long etudiantId, Long formationId);
51.
52. // liste des bilans d'un étudiant
53. List<Bilan> getShortBilansByEtudiantId(Long etudiantId);
54.
55. // requêtes byId
56. Utilisateur getLongUtilisateurById(Long utilisateurId);
57.
58. Formation getLongFormationById(Long formationId);
59.
60. Cours getLongCoursById(Long coursId);
61.
62. Module getLongModuleById(Long moduleId);
63.
64. Bilan getLongBilanById(Long bilanId);
65.
66. // requêtes byName
67. Formation getShortFormationByName(String name);
68.
69. // gestion base de données
70. void cleanDatabase();
71.
72. void fillDatabase();
73. }
Nous donnons dans ce qui suit, une définition simplifiée des entités manipulées par cette interface. Si vous avez des doutes, vous
pouvez connaître la définition exacte de ces entités en examinant le dossier [Dependencies] du projet de test :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
301/489
5. ...
6.
7. @Table(name = ConfigJdbc.TAB_FORMATIONS)
8. public class Formation {
9.
10. @Id
11. @GeneratedValue(strategy = [Link])
12. @Column(name = ConfigJdbc.TAB_ID)
13. protected Long id;
14.
15. @Version
16. @Column(name = ConfigJdbc.TAB_VERSIONING)
17. protected Long version;
18.
19. // propriétés
20. @Column(name = ConfigJdbc.TAB_FORMATIONS_NOM, unique = true, length = 30, nullable = false)
21. private String nom;
22.
23. // constructeurs
24. public Formation() {
25.
26. }
27.
28. public Formation(String nom) {
29. [Link] = nom;
30. }
31.
32. public Formation(Long id, Long version) {
33. [Link] = id;
34. [Link] = version;
35. }
36.
37. // getters et setters
38. ...
39. }
La correspondance entre champs de la classe [Formation] et colonnes de la table [FORMATIONS] est la suivante :
champ Colonne
id ID
version VERSIONING
nom NOM
1. package [Link];
2.
3. ...
4.
5. @Entity
6. @Table(name = ConfigJdbc.TAB_UTILISATEURS)
7. public class Utilisateur{
8.
9. @Id
10. @GeneratedValue(strategy = [Link])
11. @Column(name = ConfigJdbc.TAB_ID)
12. protected Long id;
13.
14. @Version
15. @Column(name = ConfigJdbc.TAB_VERSIONING)
16. protected Long version;
17.
18. @Column(name = ConfigJdbc.TAB_UTILISATEURS_NOM, nullable = false, length = 30)
19. protected String nom;
20.
21. @Column(name = ConfigJdbc.TAB_UTILISATEURS_PRENOM, nullable = false, length = 30)
22. protected String prenom;
23.
24. @Column(name = ConfigJdbc.TAB_UTILISATEURS_LOGIN, nullable = false, length = 15, unique = true)
25. protected String login;
26.
27. @Column(name = ConfigJdbc.TAB_UTILISATEURS_PASSWORD, nullable = false, length = 60)
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
302/489
28. protected String password;
29.
30. @Column(name = ConfigJdbc.TAB_UTILISATEURS_FORMATION_ID, nullable = false, insertable = false, updatable = false)
31. protected Long formationId;
32.
33. public static enum UtilisateurType {
34. ALL, ETUDIANT, PROF
35. };
36.
37. @Column(name = ConfigJdbc.TAB_UTILISATEURS_ROLE, nullable = false)
38. protected UtilisateurType role;
39.
40. // la formation
41. @ManyToOne(fetch = [Link])
42. @JoinColumn(name = ConfigJdbc.TAB_UTILISATEURS_FORMATION_ID)
43. protected Formation formation;
44.
45. // constructeurs
46. public Utilisateur() {
47.
48. }
49.
50. public Utilisateur(String nom, String prenom, String login, String password, UtilisateurType role) {
51. [Link] = nom;
52. [Link] = prenom;
53. [Link] = login;
54. [Link] = password;
55. [Link] = role;
56. }
57.
58. public Utilisateur(Long id, Long version) {
59. [Link] = id;
60. [Link] = version;
61. }
62.
63. // getters et setters
64. ...
65. }
• ligne 38 : pour attribuer une valeur au champ [role], vous pouvez écrire :
◦ role= [Link] ;
La correspondance entre champs de la classe [Utilisateur] et colonnes de la table [UTILISATEURS] est la suivante :
champ Colonne
id ID
version VERSIONING
nom NOM
prenom PRENOM
login LOGIN
password PASSWORD
role ROLE
formationId FORMATION_ID
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
303/489
1. package [Link];
2.
3. ...
4.
5. @Entity
6. @Table(name = ConfigJdbc.TAB_MODULES)
7. public class Module {
8.
9. @Id
10. @GeneratedValue(strategy = [Link])
11. @Column(name = ConfigJdbc.TAB_ID)
12. protected Long id;
13.
14. @Version
15. @Column(name = ConfigJdbc.TAB_VERSIONING)
16. protected Long version;
17.
18. @Column(name = ConfigJdbc.TAB_MODULES_NOM, nullable = false, length = 30, unique = true)
19. protected String nom;
20.
21. @Column(name = ConfigJdbc.TAB_MODULES_FORMATION_ID, nullable = false, insertable = false, updatable = false)
22. protected Long formationId;
23.
24. // la formation
25. @ManyToOne(fetch = [Link])
26. @JoinColumn(name = ConfigJdbc.TAB_MODULES_FORMATION_ID)
27. protected Formation formation;
28.
29. // constructeurs
30. public Module() {
31.
32. }
33.
34. public Module(String nom) {
35. [Link] = nom;
36. }
37.
38. public Module(Long id, Long version) {
39. [Link] = id;
40. [Link] = version;
41. }
42.
43. // getters et setters
44. ...
45. }
La correspondance entre champs de la classe [Module] et colonnes de la table [MODULES] est la suivante :
champ Colonne
id ID
version VERSIONING
nom NOM
formationId FORMATION_ID
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
304/489
1. package [Link];
2.
3. ...
4.
5. @Entity
6. @Table(name = ConfigJdbc.TAB_COURS)
7. public class Cours extends AbstractCoreEntity {
8.
9. @Id
10. @GeneratedValue(strategy = [Link])
11. @Column(name = ConfigJdbc.TAB_ID)
12. protected Long id;
13.
14. @Version
15. @Column(name = ConfigJdbc.TAB_VERSIONING)
16. protected Long version;
17.
18. @Transient
19. protected EntityType entityType = [Link];
20.
21. @Column(name = ConfigJdbc.TAB_COURS_NOM, nullable = false, length = 30, unique = true)
22. protected String nom;
23.
24. @Column(name = ConfigJdbc.TAB_COURS_ECTS, nullable = false)
25. protected int ects;
26.
27. @Column(name = ConfigJdbc.TAB_COURS_MODULE_ID, nullable = false, insertable = false, updatable = false)
28. protected Long moduleId;
29.
30. // le module
31. @ManyToOne(fetch = [Link])
32. @JoinColumn(name = ConfigJdbc.TAB_COURS_MODULE_ID)
33. protected Module module;
34.
35. // constructeurs
36. public Cours() {
37.
38. }
39.
40. public Cours(String nom, int ects) {
41. [Link] = nom;
42. [Link] = ects;
43. }
44.
45. public Cours(Long id, Long version) {
46. [Link] = id;
47. [Link] = version;
48. }
49.
50. // getters et setters
51. ...
52. }
La correspondance entre champs de la classe [Cours] et colonnes de la table [COURS] est la suivante :
champ Colonne
id ID
version VERSIONING
nom NOM
ects ECTS
moduleId MODULE_ID
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
305/489
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
12. import [Link];
13. import [Link];
14.
15. import [Link];
16.
17. import [Link];
18.
19. @Entity
20. @Table(name = ConfigJdbc.TAB_BILANS)
21. @JsonFilter("jsonFilterBilan")
22. public class Bilan extends AbstractCoreEntity {
23.
24. @Id
25. @GeneratedValue(strategy = [Link])
26. @Column(name = ConfigJdbc.TAB_ID)
27. protected Long id;
28.
29. @Version
30. @Column(name = ConfigJdbc.TAB_VERSIONING)
31. protected Long version;
32.
33. @Transient
34. protected EntityType entityType = [Link];
35.
36. // la note
37. @Column(name = ConfigJdbc.TAB_BILANS_NOTE, nullable = false, length = 10)
38. protected int note;
39.
40. // les commentaires +
41. @Column(name = ConfigJdbc.TAB_BILANS_PLUS, length = 1000)
42. protected String plus;
43.
44. // les commentaires -
45. @Column(name = ConfigJdbc.TAB_BILANS_MOINS, length = 1000)
46. protected String moins;
47.
48. @Column(name = ConfigJdbc.TAB_BILANS_COURS_ID, nullable = false, insertable = false, updatable = false)
49. protected Long coursId;
50.
51. @Column(name = ConfigJdbc.TAB_BILANS_ETUDIANT_ID, nullable = false, insertable = false, updatable = false)
52. protected Long utilisateurId;
53.
54. // l'étudiant
55. @ManyToOne(fetch = [Link])
56. @JoinColumn(name = ConfigJdbc.TAB_BILANS_ETUDIANT_ID)
57. protected Utilisateur utilisateur;
58.
59. // le cours
60. @ManyToOne(fetch = [Link])
61. @JoinColumn(name = ConfigJdbc.TAB_BILANS_COURS_ID)
62. protected Cours cours;
63.
64. // constructeurs
65. public Bilan() {
66.
67. }
68.
69. public Bilan(int note, String moins, String plus) {
70. [Link] = note;
71. [Link] = plus;
72. [Link] = moins;
73. }
74.
75. public Bilan(Long id, Long version) {
76. [Link] = id;
77. [Link] = version;
78. }
79.
80. // getters et setters
81. ...
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
306/489
82. }
La correspondance entre champs de la classe [Bilan] et colonnes de la table [BILANS] est la suivante :
champ Colonne
id ID
version VERSIONING
note NOTE
plus PLUS
moins MOINS
coursId COURS_ID
utilisateurId ETUDIANT_ID
La méthode de la ligne 1 rend la version longue (Long) de l'utilisateur alors que la ligne 3 rend la version courte (Short). Revenons à
la définition simplifiée de la classe [Utilisateur] :
1. ...
2. @Entity
3. @Table(name = ConfigJdbc.TAB_UTILISATEURS)
4. public class Utilisateur{
5.
6. @Id
7. @GeneratedValue(strategy = [Link])
8. @Column(name = ConfigJdbc.TAB_ID)
9. protected Long id;
10.
11. @Version
12. @Column(name = ConfigJdbc.TAB_VERSIONING)
13. protected Long version;
14.
15. @Column(name = ConfigJdbc.TAB_UTILISATEURS_NOM, nullable = false, length = 30)
16. protected String nom;
17.
18. @Column(name = ConfigJdbc.TAB_UTILISATEURS_PRENOM, nullable = false, length = 30)
19. protected String prenom;
20.
21. @Column(name = ConfigJdbc.TAB_UTILISATEURS_LOGIN, nullable = false, length = 15, unique = true)
22. protected String login;
23.
24. @Column(name = ConfigJdbc.TAB_UTILISATEURS_PASSWORD, nullable = false, length = 60)
25. protected String password;
26.
27. @Column(name = ConfigJdbc.TAB_UTILISATEURS_FORMATION_ID, nullable = false, insertable = false, updatable = false)
28. protected Long formationId;
29.
30. public static enum UtilisateurType {
31. ALL, ETUDIANT, PROF
32. };
33.
34. @Column(name = ConfigJdbc.TAB_UTILISATEURS_ROLE, nullable = false)
35. protected UtilisateurType role;
36.
37. // la formation
38. @ManyToOne(fetch = [Link])
39. @JoinColumn(name = ConfigJdbc.TAB_UTILISATEURS_FORMATION_ID)
40. protected Formation formation;
41.
42. // constructeurs
43. public Utilisateur() {
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
307/489
44.
45. }
46. ...
• lignes 38-40 : on a une propriété annotée avec [@ManyToOne(fetch = [Link])]. Cela signifie que lorsqu'on
récupère un utilisateur, on ne récupère pas forcément sa formation avec (mode lazy loading). On appellera :
◦ version courte d'un utilisateur, une instance [Utilisateur] où le champ [Formation formation] n'a pas été initialisé avec
la formation de l'utilisateur mais avec autre chose appelée un PROXY. Accéder à ce champ provoque alors une
exception ;
◦ version longue d'un utilisateur, une instance [Utilisateur] où le champ [Formation formation] a été initialisé avec la
formation de l'utilisateur ;
• la méthode de la ligne 1 rend un utilisateur U où la propriété [[Link]] peut être utilisée via son getter ;
• la méthode de la ligne 3 rend un utilisateur U où la propriété [[Link]] ne peut pas être utilisée via son getter ;
Il en est ainsi pour toutes les entités de cet exercice. Une entité E ayant une propriété (ou plusieurs) p annotée(s) avec
[@ManyToOne(fetch = [Link])] a :
• une version courte (Short) où la propriété [E.p] ne doit pas être utilisée ;
• une version longue (Long) où la propriété [E.p] peut être utilisée ;
Lorsqu'on a la version courte de l'utilisateur, le champ [formation] (ligne 7) n'est pas utilisable. Mais le champ [formationId] de la
ligne 2 lui l'est. Il représente la clé primaire de la formation à laquelle appartient l'étudiant. Si on voulait la formation, elle pourrait
alors être obtenue par la méthode suivante de l'interface [IDao] :
Formation getLongFormationById(Long formationId);
Couche
[web / DAO - JPA - JDBC BD
JSF]
Spring / Serveur
7 Tomcat 7
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
308/489
3.6.1 Configuration de l'application [web / JSF]
Une implémentation de démarrage de la couche [web / JSF] vous est fournie :
Les lignes 8-11 déclarent la servlet qui va traiter les demandes des navigateurs clients. C'est la servlet du framework JSF ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
309/489
6. <application>
7. <!-- pour que Spring puisse gérer les beans JSF -->
8. <el-resolver>[Link]</el-resolver>
9. </application>
10. </faces-config>
La ligne 8 va permettre de gérer les beans JSF comme des composants Spring. Ceci va nous permettre d'utiliser des annotations
Spring dans les beans JSF.
• ligne 15 : on importe le fichier de configuration Spring de la couche [DAO]. Nous n'explicitons pas ici celui-ci car nous
utilisons la couche [DAO] comme une boîte noire ;
• ligne 16 : demande à Spring d'explorer le package [[Link]] pour y trouver des beans Spring. Les beans suivants y
seront trouvés [Test, Connect, Bilans, ApplicationModel, SessionModel] ;
• les lignes 22-27 servent à indiquer que la servlet [FacesServlet] (ligne 24) du framework JSF traitera les URL de forme
[*.xhtml] (ligne 25), ç-à-d toute URL de suffixe [.xhtml] ;
• les lignes 29-35 servent à lancer un serveur Tomcat (ligne 31) qui va attendre les requêtes des clients sur le port 8080 (ligne
32) ;
• la ligne 9 lance une classe spéciale du framework Spring Boot (ligne 3) qui va exloiter la configuration faite dans la classe
[JsfConfig] (ligne 9). C'est ainsi que le serveur web Tomcat va être lancé, que l'application web / JSF va être déployée
dessus, et que la servlet [FacesServlet] de JSF va traiter les URL de suffixe [.xhtml] ;
Pour que tout ceci soit possible, il faut qu'un certain nombre de dépendances soient présentes dans le fichier [[Link]] qui
configure le projet Maven. Celui-ci est le suivant :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <project xsi:schemaLocation="[Link] [Link]
3. xmlns="[Link] xmlns:xsi="[Link]
4. <modelVersion>4.0.0</modelVersion>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
310/489
5. <groupId>[Link]</groupId>
6. <artifactId>bilans-jsf-dao-jpa-skel</artifactId>
7. <version>1.0</version>
8. <name>bilans-jsf-dao-jpa-skel</name>
9. <!-- [Link]
context-face/25509937#25509937 -->
10. <parent>
11. <groupId>[Link]</groupId>
12. <artifactId>spring-boot-starter-parent</artifactId>
13. <version>[Link]</version>
14. </parent>
15. <dependencies>
16. <!-- JSF -->
17. <dependency>
18. <groupId>[Link]</groupId>
19. <artifactId>[Link]</artifactId>
20. <version>2.2.11</version>
21. </dependency>
22. <!-- JSR-330 -->
23. <dependency>
24. <groupId>[Link]</groupId>
25. <artifactId>[Link]</artifactId>
26. <version>1</version>
27. </dependency>
28. <!-- JSP API -->
29. <dependency>
30. <groupId>[Link]</groupId>
31. <artifactId>tomcat-embed-jasper</artifactId>
32. </dependency>
33. <!-- Spring web -->
34. <dependency>
35. <groupId>[Link]</groupId>
36. <artifactId>spring-web</artifactId>
37. </dependency>
38. <!-- couche dao -->
39. <dependency>
40. <groupId>[Link]</groupId>
41. <artifactId>bilans-dao-jpa-with-dependencies</artifactId>
42. <version>1.0.0</version>
43. </dependency>
44. </dependencies>
45. <build>
46. <outputDirectory>src/main/webapp/WEB-INF/classes</outputDirectory>
47. <plugins>
48. <plugin>
49. <artifactId>maven-compiler-plugin</artifactId>
50. <version>3.0</version>
51. <configuration>
52. <source>1.8</source>
53. <target>1.8</target>
54. </configuration>
55. </plugin>
56. <plugin>
57. <groupId>[Link]</groupId>
58. <artifactId>maven-surefire-plugin</artifactId>
59. <version>2.18.1</version><!--$NO-MVN-MAN-VER$-->
60. </plugin>
61. </plugins>
62. </build>
63. <properties>
64. <[Link]>UTF-8</[Link]>
65. <[Link]>1.8</[Link]>
66. </properties>
67.
68. </project>
• lignes 10-14 : déclaration d'un projet Maven parent. Un tel projet définit un certain nombre de dépendances avec leurs
versions. Un projet Maven fils qui reprend certaines de ces dépendances n'a pas alors besoin d'indiquer leurs versions. Ce
sont celles du projet Maven parent ;
• lignes 17-21 : dépendance qui fournit le framework JSF ;
• lignes 23-27 : cette dépendance permet d'utiliser les annotations @Inject et @Named ;
• lignes 29-32 : si cette dépendance n'est pas là, ça plante ;
• lignes 34-37 : dépendance sur le framework web de Spring ;
• lignes 39-43 : dépendance sur la couche [DAO] de l'application que vous avez installée au paragraphe 3.5.1, page 299.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
311/489
2
1
Chacune d'elles est associée à un bean Java, respectivement : [Test], [Connect] et [Bilans]. Deux autres beans sont utilisés :
• le bean [ApplicationModel] de portée [Application] ;
• le bean [SessionModel] de portée [Session] ;
• en [5], on voit qu'un serveur Tomcat a été lancé sur le port 8080. C'est lui qui exécute l'application web ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
312/489
6
Si vous obtenez cette page et qu'il n'y a pas d'exception dans la console [5], alors l'application est correctement installée. En effet, au
démarrage, le bean [ApplicationModel] de portée [application] est instancié :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
313/489
29. }
30.
31. @Override
32. public Utilisateur getShortUtilisateurByCredentials(String login, String password) {
33. return [Link](login, password);
34. }
35.
36. @Override
37. public List<Formation> getAllShortFormations() {
38. return [Link]();
39. }
40.
41. @Override
42. public List<Formation> getShortFormationsByUtilisateurId(Long utilisateurId) {
43. return [Link](utilisateurId);
44. }
45.
46. @Override
47. public List<Module> getShortModulesByFormationId(Long formationId) {
48. return [Link](formationId);
49. }
50.
51. @Override
52. public List<Cours> getShortCoursByModuleId(Long moduleId) {
53. return [Link](moduleId);
54. }
55.
56. @Override
57. public List<Cours> getShortCoursByFormationId(Long formationId) {
58. return [Link](formationId);
59. }
60.
61. @Override
62. public List<Utilisateur> getShortUtilisateursByFormationId(Long formationId, UtilisateurType utilisateurType) {
63. return [Link](formationId, utilisateurType);
64. }
65.
66. @Override
67. public Bilan saveBilan(Bilan bilan) {
68. return [Link](bilan);
69. }
70.
71. @Override
72. public List<Bilan> getShortBilansByFormationId(Long formationId) {
73. return [Link](formationId);
74. }
75.
76. @Override
77. public List<Bilan> getShortBilansByCoursId(Long coursId) {
78. return [Link](coursId);
79. }
80.
81. @Override
82. public List<Bilan> getShortBilansByEtudiantIdByFormationId(Long etudiantId, Long formationId) {
83. return [Link](etudiantId, formationId);
84. }
85.
86. @Override
87. public Utilisateur getLongUtilisateurById(Long utilisateurId) {
88. return [Link](utilisateurId);
89. }
90.
91. @Override
92. public Formation getLongFormationById(Long formationId) {
93. return [Link](formationId);
94. }
95.
96. @Override
97. public Cours getLongCoursById(Long coursId) {
98. return [Link](coursId);
99. }
100.
101. @Override
102. public Module getLongModuleById(Long moduleId) {
103. return [Link](moduleId);
104. }
105.
106. @Override
107. public Bilan getLongBilanById(Long bilanId) {
108. return [Link](bilanId);
109. }
110.
111. @Override
112. public Formation getShortFormationByName(String name) {
113. return [Link](name);
114. }
115.
116. @Override
117. public void cleanDatabase() {
118. [Link]();
119. }
120.
121. @Override
122. public void fillDatabase() {
123. [Link]();
124. }
125.
126. @Override
127. public void deleteBilan(Long bilanId) {
128. [Link](bilanId);
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
314/489
129. }
130.
131. @Override
132. public List<Bilan> getShortBilansByEtudiantId(Long etudiantId) {
133. return [Link](etudiantId);
134. }
135.
136. }
Vue Bean
Serveur Tomcat
• lignes 10 et 15 : le bean nommé [test] est utilisé. Il correspond à une instance de la classe [Test] suivante :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6.
7. import [Link];
8. import [Link];
9.
10. import [Link];
11.
12. @Named
13. @Scope("request")
14. public class Test {
15.
16. // injections
17. @Autowired
18. private ApplicationModel application;
19.
20. // champs de la vue
21. private List<Formation> formations;
22. private String message;
23.
24. @PostConstruct
25. public void initView() {
26. try {
27. formations = [Link]();
28. message = "Formations chargées";
29. } catch (Exception e) {
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
315/489
30. message = "L'erreur suivante s'est produite : " + [Link]();
31. }
32. }
33.
34. // getters et setters
35. public List<Formation> getFormations() {
36. return formations;
37. }
38.
39. public String getMessage() {
40. return message;
41. }
42.
43. }
• ligne 12 : l'annotation [@Named] fait de la classe [Test] un bean nommé qui peut être injecté dans d'autres beans avec
l'annotation [@Inject]. Le nom donné par défaut au bean est le nom de la classe avec la 1ère majuscule transformée en
minuscule, donc ici [test]. C'est grâce à cette annotation qu'on peut écrire dans la page [[Link]] l'expression
[#{[Link]}] (ligne 10) ;
• ligne 13 : l'annotation Spring [@Scope("request")] fait de la classe [Test] un bean JSF de portée [Request]. Cela signifie
qu'il est instancié à chaque nouvelle requête HTTP le nécessitant et détruit à la fin de celle-ci. On ne peut rien mémoriser
dans un tel bean. Il faut pour cela utiliser un bean de portée [Session]. C'est le bean [SessionModel] qui jouera ce rôle de
mémoire des requêtes ;
• lignes 17-18 : injection Spring du bean [ApplicationModel] que nous venons de décrire ;
• ligne 21 : la liste des formations affichée par la page [[Link]] ;
• ligne 22 : le message affiché par la page [[Link]] ;
• ligne 24 : l'annotation [@PostConstruct] assure que la méthode taguée [initView] sera exécutée après que l'injection des
lignes 17-18 aura été faite ;
• lignes 26-31 : on récupère la liste des formations et on initialise le champ [message] avec la valeur adéquate ;
• au final, après instanciation du bean [Test], les champs des lignes 21-22 ont été initialisés. Ce sont eux que la page
[[Link]] affiche ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
316/489
30. private double noteFormation;
31. // init page bilans
32. private boolean initBilansDone;
33.
34. // getters et setters
35. ...
36. }
La classe [SessionModel] joue un rôle central dans cet exercice. Elle permet de se souvenir des choix faits par l'utilisateur connecté,
au fil de ses actions sur les pages de l'application. Vous pourrez changer le contenu proposé pour la classe [SessionModel] si vous le
souhaitez.
Si vous oubliez de suivre la procédure précédente, lors de la deuxième exécution du projet, vous aurez l'exception suivante :
1. Exception in thread "main" [Link]: Tomcat connector in failed state
2. at [Link]([Link])
3. at
[Link]([Link])
4. at [Link]([Link])
5. at [Link]([Link])
6. at [Link]([Link])
7. at [Link]([Link])
8. at [Link]([Link])
9. at [Link]([Link])
10. at [Link]([Link])
11. at [Link]([Link])
L'erreur ligne 1 vient du fait que la deuxième exécution a voulu lancer le serveur Tomcat sur le port 8080 alors que le serveur
Tomcat de la 1ère exécution est toujours actif sur ce même port. Il faut alors arrêter le serveur de la première exécution comme
indiqué en [1] plus haut.
Parfois la fenêtre [output] de la 1ère exécution a disparu et on ne peut alors arrêter le serveur Tomcat à partir de Netbeans. Dans ce
cas faites [Ctrl-Alt-Supp] pour ouvrir le gestionnaire de tâches :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
317/489
En [2], supprimez les tâches Java que vous trouverez.
3.7.1 Étape 1
Note : du code vous est fourni dans l'application squelette. Vous n'êtes en aucun cas obligés de le conserver. Tant qu'il produit le
résultat désiré, tout code est acceptable.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
318/489
13. <h:inputText id="login" value="#{[Link]}" />
14. </h:column>
15. <h:column>
16. <h:outputText id="errLogin" value="Information nécessaire !" rendered="#{[Link]}"
17. style="color: red" />
18. </h:column>
19. <h:column>Mot de passe :</h:column>
20. <h:column>
21. <h:inputSecret id="password" value="#{[Link]}" />
22. </h:column>
23. <h:column></h:column>
24. </h:panelGrid>
25. <h:commandLink action="#{[Link]}">Connexion</h:commandLink>
26. <br/>
27. <br/>
28. <h:outputText id="exception" rendered="#{[Link]}" value="#{[Link]}"
29. style="color: red" />
30. </h:form>
31. </h:body>
32. </f:view>
Le bean nommé [connect] aux lignes 13, 16, 21, 25 et 28 est une instance du bean [Connect] suivant :
1. package [Link];
2.
3. import [Link];
4.
5. import [Link];
6. import [Link];
7.
8. @Named
9. @Scope("request")
10. public class Connect {
11.
12. // injections
13. @Autowired
14. private ApplicationModel application;
15. @Autowired
16. private SessionModel session;
17.
18. // champs de la vue
19. private String login = "Form0-Etudiant";
20. private String password;
21. private boolean errLoginRendered = false;
22. private String exceptionMsg;
23. private boolean exceptionMsgRendered = false;
24.
25. // gestionnaires d'évts
26. public String connect() {
27. // ...
28. // c'est erroné
29. // return null;
30. // c'est bon
31. //return "[Link]";
32. return null;
33. }
34.
35. // getters et setters
36. ...
37. }
Conseils et contraintes :
• utilisez la méthode [getLongUtilisateurByCredentials(login, password)] de la classe [ApplicationModel] qui :
◦ lance une exception si un problème survient (erreur de connexion à la base de données...) ;
◦ rend un pointeur null si l'utilisateur n'a pas pu être identifié ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
319/489
◦ sinon rend l'utilisateur identifié par le login et le mot de passe passés en paramètres ;
Ci-dessus, l'utilisateur qui se connecte est un enseignant, ç-à-d un utilisateur avec le champ [role=[Link]]. Il est
refusé car seuls les étudiants peuvent se connecter à l'application.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
320/489
1 2
Il y a deux étudiants de logins [Form0-Etudiant] et [Form1-Etudiant] (cf base de données). Leurs mots de passe sont égaux à leurs
logins. Pour vous connecter, utilisez par exemple les codes Form0-Etudiant / Form0-Etudiant qui désignent l'unique étudiant de la
formation [Form0].
3.7.2 Étape 2
La page [[Link]] est pour l'instant la suivante :
1. <f:view xmlns="[Link] xmlns:f="[Link]
2. xmlns:h="[Link] xmlns:ui="[Link]
3. <h:head>
4. <title>CC Java Session 2 - EI5 AGI 1415</title>
5. </h:head>
6. <h:body>
7. <h2>Gestion des bilans</h2>
8. <h:form id="formulaire">
9. <!-- saisies -->
10. <h:panelGrid columns="3">
11. <h:column>Utilisateur :</h:column>
12. <h:column>
13. <h:outputText id="etudiant" value="#{[Link]}" />
14. </h:column>
15. <h:column />
16. <!-- ...-->
17. </h:panelGrid>
18. <h:commandLink action="#{[Link]}">Déconnexion</h:commandLink>
19. <br />
20. <h:outputText id="exception" rendered="#{[Link]}" value="#{[Link]}" style="color:
red" />
21. </h:form>
22. </h:body>
23. </f:view>
Lignes 13, 18, 20, un bean nommé [bilans] est utilisé. C'est une instance de la classe [Bilans] suivante :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5.
6. import [Link];
7. import [Link];
8.
9. import [Link];
10.
11. @Named
12. @Scope("request")
13. public class Bilans {
14.
15. // injections Spring
16. @Autowired
17. private ApplicationModel application;
18. @Autowired
19. private SessionModel session;
20.
21. // champs de la vue
22. private String exceptionMsg;
23. private boolean exceptionMsgRendered = false;
24. // ...
25.
26. @PostConstruct
27. public void initView() {
28. // informations à aller chercher ?
29. if (![Link]()) {
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
321/489
30. // on récupère l'utilisateur connecté
31. Utilisateur utilisateur = [Link]();
32. // ...
33. // init terminée
34. [Link](true);
35. }
36. }
37. // ---------------------------------------------------gestionnaires d'évts
38. // déconnexion
39.
40. public String disconnect() {
41. // fin de session
42. [Link](false);
43. [Link](null);
44. // page de connexion
45. return "[Link]";
46. }
47.
48. // getters et setters
49. // ...
50.
51. }
• lignes 11612 : la classe [Bilans] est un bean JSF de portée [Request]. Cela veut dire qu'on ne peut rien mémoriser dedans.
La classe est :
◦ instanciée au début de la requête qui a besoin du bean ;
◦ supprimée à la fin de cette requête ;
Si vous voulez mémoriser des informations pour la prochaine requête, vous devez utiliser le bean [SessionModel] de la
ligne 19.
• lignes 16-19 : injection des beans de portée [Application] et [Session] ;
• ligne 22 : un message d'erreur affiché par la vue [[Link]] ;
• ligne 23 : un booléen contrôlant l'affichage du message d'erreur précédent ;
• lignes 24-... : les champs utilisés par la page [[Link]]. Ce sera à vous de trouver ceux dont vous avez besoin. Il en faut
un par valeur postée. Cela signifie qu'à chaque fois que vous ajoutez dans la page [[Link]] un composant de saisie
(select, inputText, textArea, ...) vous devez prévoir un champ dans le bean [Bilan] pour accueillir la valeur postée. Revoyez
le bean [Connect] et sa page [[Link]] pour voir comment cela a été fait pour ce bean et sa page ;
• ligne 26-36 : la méthode exécutée après l'injection des beans des lignes 18-21 (annotation @PostConstruct). Comme le
bean est de portée [Request], cette méthode va être exécutée à chaque nouvelle requête. Or le travail qu'elle doit faire ne
doit être fait qu'une fois. On utilise donc le booléen [[Link]] (cf page 316) pour savoir si ce travail a déjà
été fait. Si ce n'est pas le cas, la méthode est exécutée et le booléen [[Link]] est mis à vrai (ligne 34) ;
• lignes 40-46 : la méthode exécutée lorsque l'utilisateur clique sur le lien [Déconnexion] de la page [[Link]] ;
• ligne 42 : on indique que la page de bilans n'est plus initialisée pour que la prochaine fois qu'elle sera chargée, la méthode
[initView] soit exécutée de nouveau ;
• ligne 43 : on indique qu'il n'y a plus d'utilisateur connecté ;
• ligne 45 : on affiche la page de connexion ;
Question 2 : modifiez l'application (page [[Link]], méthode [[Link]]) pour obtenir la page suivante après la
connexion.
• en [1], tous les cours de la formation. Ce sont ces cours que l'étudiant va évaluer ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
322/489
Conseils et contraintes :
• utilisez la méthode [[Link]] pour obtenir tous les cours de la formation de l'étudiant. Le
paramètre de la méthode est le n° [id] de la formation ;
• la liste déroulante sera une liste HTML où la valeur (attribut value) des options de la liste sera le n° [id] des cours et le
libellé des options (ce que l'utilisateur voit) sera le nom du cours. La valeur de la liste déroulante sera postée au champ
[[Link]]. Cela signifie que lorsque la page sera postée par l'un des liens qu'on va y mettre, le n° [id] du cours
sélectionné sera affecté au champ [[Link]] ;
• la liste de cours sera mise dans la session ;
3.7.3 Étape 3
Question 3 : Modifiez la méthode [[Link]] pour qu'à son issue, tous les bilans déjà faits par l'utilisateur connecté (ç-à-d
présents en base de données), sur les cours de sa formation, soient en session. Cette liste vous sera utile pour gérer les bilans de
l'étudiant.
Conseils et contraintes :
• utilisez la méthode [[Link]] qui admet pour paramètre l'identifiant de l'étudiant dont on
cherche les bilans ;
• cette liste de bilans sera mise en session ;
3.7.4 Étape 4
Question 4 : modifiez l'application (page [[Link]], méthode [[Link]]) pour obtenir la page suivante après la
connexion.
Conseils et contraintes :
• attention : le bilan du cours affiché en [1] n'est pas forcément le 1er bilan de la liste des bilans reçue. Il suffit en effet que
l'étudiant n'ait pas encore fait le bilan du cours [1]. Par ailleurs, rien ne dit que la liste des bilans obtenue respecte l'ordre
des cours de la liste déroulante [1]. Chaque bilan a un champ [coursId] qui permet de le relier à un cours. Si le cours
affiché en [1] n'a pas de bilan, on affichera un bilan vierge [note=0, plus=null ; moins=null] ;
• mémorisez dans la session le bilan affiché, avec toutes ses propriétés [note, plus, moins] mais aussi [cours, coursId,
utilisateur, utilisateurId] (cf définition de la classe [Bilan] au paragraphe 3.5.2) ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
323/489
• utilisez une méthode séparée pour afficher le bilan car vous en aurez besoin à d'autres moments ;
3.7.5 Étape 5
Question 5 : modifiez l'application (classe [Bilans]) pour gérer le lien [Bilan] qui affiche le bilan du cours sélectionné par
l'utilisateur.
Conseils et contraintes :
• en [1], le nouveau bilan :
• s'il n'existe pas encore de bilan pour le cours sélectionné, on affichera un bilan vierge : 0 pour [note] et rien pour [plus] et
[moins] ;
• on exploitera la liste des bilans présente en session ;
• le nouveau bilan affiché sera mis en session avec tous ses attributs connus ;
• le nouveau cours sélectionné sera mis en session ;
3.7.6 Étape 6
Question 6 : modifiez l'application (classe [Bilans]) pour gérer le lien [Sauvegarder] qui sauvegarde en base le bilan affiché et
éventuellement modifié par l'utilisateur.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
324/489
1
• en [1], apparemment rien n'a changé. Il faut aller dans la base de données vérifier l'ajout ou la modification dans la table
[BILANS] :
Conseils et contraintes :
• utilisez la méthode [[Link]] qui permet de sauvegarder en base le bilan passé en paramètre. Si celui-ci a un
identifiant [id] égal à null, c'est une insertion qui sera faite, sinon ce sera une modification. Vous n'avez pas à vous en
préoccuper. La méthode rend le nouveau bilan, avec sa clé primaire si une insertion a eu lieu, avec sa nouvelle version si
une modification a eu lieu ;
• dans les bilans mis en session, supprimez l'ancien bilan (s'il existe) et ajoutez le nouveau ;
• mettez le nouveau bilan en session ;
• l'utilisateur peut choisir un cours dans la liste déroulante sans cliquer ensuite sur le lien [Bilan]. Ceci fait que le cours
actuellement mémorisé dans la session n'est pas celui que l'utilisateur voit dans la liste déroulante. Idem pour le bilan qui
est celui du cours courant actuellement en session. Si ensuite l'utilisateur modifie le bilan qu'il voit et clique sur le lien
[Sauvegarder], ce n'est pas le bilan du cours qu'il voit dans la liste déroulante qui va être sauvegardé mais le bilan du cours
actuellement en session. Ce cas peut être traité mais on ne le fera pas. On supposera toujours que lorsque l'utilisateur
change de cours dans la liste déroulante, il clique ensuite sur le lien [Bilan] pour changer le bilan affiché. Cela
signifie que pour nous, le bilan en session qui va être sauvegardé ou supprimé est toujours celui du cours actuellement
sélectionné dans la liste déroulante ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
325/489
3.7.7 Étape 7
Question 7 : modifiez l'application (classe [Bilans]) pour gérer le lien [Supprimer] qui supprime en base le bilan affiché.
• en [1], un bilan vide a été affiché. Il faut aller dans la base de données pour vérifier la suppression du bilan de la table
[BILANS] :
Conseils et contraintes :
• utiliser la méthode [[Link]] pour supprimer un bilan dont on passe l'identifiant ;
• le bilan supprimé doit être retiré de la liste des bilans présente en session ;
• le bilan vide affiché doit être mis en session avec toutes ses caractéristiques (cf définition de la classe [Bilan] au paragraphe
3.5.2), notamment un identifiant null qui provoquera une insertion si l'utilisateur modifie ce bilan puis le sauvegarde ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
326/489
Faites également le test suivant :
• lancez l'application ;
• supprimez le bilan du 1er cours [Form0-Module0-Cours0] ;
• relancez l'application ;
• le 1er bilan affiché, celui du cours [Form0-Module0-Cours0] doit être vide ;
Cette séquence peut faire apparaître des exceptions si vos algorithmes de gestion des bilans et des cours en session sont incorrects.
3.7.8 Étape 8
Question 8 : modifiez l'application (page [[Link]], classe [Bilans]) pour afficher la note de la formation. Celle-ci est obtenue
de la façon suivante : note=(n0*ects0+... + np*ectsp)/(ects0+...+ectsp) où ni est la note du bilan n° i et ectsi les ECTS du cours
associé à ce bilan.
• la note de la formation est affichée dès le 1er affichage des bilans [1]. Ensuite, à chaque fois qu'on sauvegarde / supprime
le bilan affiché, cette note est recalculée ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
327/489
2
4
3
3.7.9 Étape 9
Question 9 : modifiez l'application (page [[Link]], classe [Bilans]) pour gérer la saisie de la note de bilan qui doit être un
nombre entier dans l'intervalle [0,10].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
328/489
Voici un exemple de ce qui est attendu :
3.7.10 Étape 10
Question 10 : modifiez l'affichage de la note de la formation afin qu'elle ne présente au plus que deux chiffres après la virgule.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
329/489
1
3.7.11 Étape 11
Question 11 : gérez l'affichage du bilan pour que lorsque le cours sélectionné n'a pas de bilan enregistré en base, le lien [Supprimer]
soit caché.
1 2
3.7.12 Étape 12
Il est techniquement possible pour un hacker d'appeler directement les méthodes qui gèrent les liens de la page [[Link]] sans
passer par la phase d'authentification.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
330/489
Conseils et contraintes :
• toute méthode liée à l'un des liens de la page [[Link]] devra avant de faire quoique ce soit, vérifier que dans la
session, il y a bien un utilisateur enregistré et que celui-ci est un étudiant. Si ce n'est pas le cas, la page d'authentification
devra être affichée.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
331/489
4 Étude de cas n° 4 : client - serveur web / jSON
4.1 Objectifs
1. renforcer les connaissances acquises dans l'étude de cas n° 1 en abordant celle-ci sous un autre angle ;
2. découvrir une architecture client / serveur, où le serveur est un service web / jSON ;
3. découvrir ou redécouvrir comment configurer Spring sans fichiers XML ;
4. découvrir ou redécouvrir comment accéder aux bases de données avec Spring Data ;
5. découvrir ou redécouvrir comment créer un serveur web / jSON et son client ;
6. découvrir l'IDE Intellij IDEA Community Edition ;
Notes : l'IDE Intellij IDEA Community Edition peut être obtenu à l'URL
[[Link] Il est présent sur les machines de l'ISTIA ;
Références :
Ce document utilise des technologies décrites dans le document « Un exemple de client / serveur AngularJS - Spring 4 » à l'URL
[[Link] On notera ce document par la suite [ref1].
4.2 Le problème
Il s'agit de refaire l'application du paragraphe 4.10 du TD :
7 Spring
L'architecture ci-dessus s'exécute dans une unique JVM (Java Virtual Machine). Nous allons la porter vers une architecture client /
serveur où le client et le serveur s'exécuteront dans deux JVM différentes.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
332/489
4.3 Introduction à l'IDE Intellij IDEA Community Edition
Nous allons donner ici quelques informations sur l'IDE Intellij IDEA Community Edition. Une fois lancé l'IDE, on charge le
projet [pam-server-maven] qui vous sera donné :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
333/489
• en [3], on a un projet Maven (présence d'un fichier [[Link]]). Lorsqu'il est modifié ou qu'on a l'impression que les
dépendances du [[Link]] n'ont pas été importées, on peut réimporter celles-ci [4-5] voire régénérer certains fichiers
source gérés par Maven [6].
Vérifiez que le projet est configuré pour être compilé et exécuté avec un JDK 1.8 [1-6] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
334/489
Dans un projet Maven, il est important de spécifier les dossiers qui contiennent le code Java [1-12] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
335/489
• en [2], on a un aperçu des modules (= projets) ouverts. Ici il n'y en a qu'un [3] ;
• en [4-5, 7, 9], l'arborescence du projet :
◦ en bleu [5-6], les dossiers des codes source ;
◦ en vert [9-10], les dossiers des codes source de la branche [test] ;
◦ en violet [7-8], les ressources du projet ;
◦ en brun [11-12], le dossier [target] dans lequel sont générées classes (.class) et archives (.jar) du projet ;
Pour fixer la nature d'un dossier, on clique droit dessus et on choisit sa nature [13-14] :
Lorsqu'on importe un projet Maven (Eclipse, Netbeans) dans Intellij IDEA, il est ensuite important de vérifier cette configuration.
Le projet importé peut ne pas fonctionner tout simplement parce que sa configuration de build (sources, tests, ressources) est
incorrecte.
Pour exécuter une classe exécutable, il suffit de cliquer droit dessus comme montré ci-dessous en [1-2] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
336/489
Pour générer les getters / setters d'une classe on utilisera la procédure [3-4] en cliquant droit sur le code dans lequel on veut les
générer.
Les deux méthodes sont susceptibles de lancer une IOException. Voici un exemple présent dans le dossier du TP :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
337/489
Le projet ci-dessus est un projet Maven avec le fichier [[Link]] suivant ;
1. <?xml version="1.0" encoding="UTF-8"?>
2. <project xmlns="[Link]
3. xmlns:xsi="[Link]
4. xsi:schemaLocation="[Link] [Link]
5. <modelVersion>4.0.0</modelVersion>
6.
7. <groupId>[Link]</groupId>
8. <artifactId>json</artifactId>
9. <version>1.0-SNAPSHOT</version>
10.
11. <dependencies>
12. <dependency>
13. <groupId>[Link]</groupId>
14. <artifactId>jackson-databind</artifactId>
15. <version>2.6.1</version>
16. </dependency>
17. </dependencies>
18.
19. <properties>
20. <[Link]>UTF-8</[Link]>
21. <[Link]>1.8</[Link]>
22. <[Link]>1.8</[Link]>
23. </properties>
24.
25. </project>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
338/489
10. public class Main {
11. // l'outil de sérialisation / désérialisation
12. static ObjectMapper mapper = new ObjectMapper();
13.
14. public static void main(String[] args) throws IOException {
15. // création d'une personne
16. Personne paul = new Personne("Denis", "Paul", 40);
17. // affichage Json
18. String json = [Link](paul);
19. [Link]("Json=" + json);
20. // instanciation Personne à partir du Json
21. Personne p = [Link](json, [Link]);
22. // affichage personne
23. [Link]("Personne=" + p);
24. // un tableau
25. [Link]("chaîne jSON d'un tableau de personnes");
26. Personne virginie = new Personne("Radot", "Virginie", 20);
27. Personne[] personnes = new Personne[]{paul, virginie};
28. // affichage Json
29. json = [Link](personnes);
30. [Link]("Json personnes=" + json);
31. // restitution du tableau
32. [Link]("restitution du tableau à partir de sa chaîne jSON");
33. personnes=[Link](json, new TypeReference<Personne[]>() {
34. });
35. for(Personne personne : personnes){
36. [Link](personne);
37. }
38. // dictionnaire
39. [Link]("chaîne jSON d'un dictionnaire de personnes");
40. Map<String, Personne> hpersonnes = new HashMap<String, Personne>();
41. [Link]("1", paul);
42. [Link]("2", virginie);
43. // affichage Json
44. json = [Link](hpersonnes);
45. [Link]("Json hpersonnes=" + json);
46. // restitution du dictionnaire
47. [Link]("restitution du dictionnaire à partir de sa chaîne jSON");
48. hpersonnes=[Link](json, new TypeReference<Map<String, Personne>>() {
49. });
50. for(Personne personne : [Link]()){
51. [Link](personne);
52. }
53. }
54. }
De l'exemple on retiendra :
• l'objet [ObjectMapper] nécessaire aux transformations jSON / Object : ligne 12 ;
• la transformation [Personne] --> jSON : ligne 18 ;
• la transformation jSON --> [Personne] : ligne 21 ;
• l'exception [IOException] lancée par les deux méthodes : ligne 14 ;
• la transformation jSON --> Personne[], ligne 33 ;
• la transformation jSON --> Map<String,Personne>, ligne 48 ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
339/489
Lectures nécessaires : chapitre 2 de [ref1].
Spring
7 4
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
340/489
• [1] : le projet dans son ensemble ;
• [2] : contient des éléments qui vous sont donnés :
◦ le package [boot] contient les classes exécutables : [BootConsole] est un programme console qui teste la couche
[métier] ainsi que les interfaces du dossier [repositories] [3]. [BootWeb] est le programme qui lance le service
web/jSON ;
◦ le package [config] contient les classes de configuration de Spring :
▪ [PersistenceConfig] configure la couche [métier] ainsi que les interfaces du dossier [repositories] ;
▪ [WebConfig] configure la couche [web/jSON] ;
▪ [AppConfig] utilise les deux fichiers de configuration précédents pour configurer l'ensemble de l'application ;
◦ le package [entities] contient les entités JPA ;
◦ le package [helpers] contient une classe [Static] contenant une méthode utilitaire ;
• [3] : contient les éléments à compléter :
◦ le package [metier] contient la couche [métier],
▪ les entités nécessaires à cette couche ;
▪ l'exception [PamException] ;
◦ le package [repositories] contient les interfaces d'accès aux différentes tables de la base de données. On aurait pu
appeler ce package [dao] mais on a préféré garder la terminologie de Spring. Ces interfaces implémentent la couche
[DAO],
◦ le package [webapi] implémente la couche [web/jSON] ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
341/489
Couche Couche Couche Couche Pilote
[web / [métier] [DAO] [JPA] [JDBC] SGBD
jSON]
Spring
7 4
On lira les paragraphes 2.2 à 2.2.4 (pages 20-29) du document [ref1]. Utilisez STS (Spring Tool Suite) pour suivre le tutoriel.
Spring
7 4
On lira le paragraphe 2.11 (pages 58-71) du document [ref1]. Utilisez STS (Spring Tool Suite) pour suivre le tutoriel.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
342/489
49. <!-- Google Guava -->
50. <dependency>
51. <groupId>[Link]</groupId>
52. <artifactId>guava</artifactId>
53. <version>19.0</version>
54. </dependency>
55.
56. </dependencies>
57.
58. <build>
59. <plugins>
60. <plugin>
61. <groupId>[Link]</groupId>
62. <artifactId>maven-surefire-plugin</artifactId>
63. <version>2.19.1</version>
64. </plugin>
65. <plugin>
66. <groupId>[Link]</groupId>
67. <artifactId>spring-boot-maven-plugin</artifactId>
68. </plugin>
69. </plugins>
70. </build>
71.
72. <properties>
73. <[Link]>UTF-8</[Link]>
74. <[Link]>1.8</[Link]>
75. <[Link]>1.8</[Link]>
76. <start-class>[Link]</start-class>
77. </properties>
78. </project>
• lignes 11-15 : une dépendance sur le framework [Spring Boot], une branche de l'écosystème Spring. Ce framework permet
une configuration minimale de Spring. Selon les archives présentes dans le Classpath du projet, [Spring Boot] infère une
configuration plausible ou probable pour celui-ci. Ainsi si Hibernate est dans le Classpath du projet alors [Spring Boot]
infèrera que l'implémentation JPA utilisée va être Hibernate et configurera Spring dans ce sens. Le développeur n'a plus à
le faire. Il ne lui reste alors à faire que les configurations que [Spring Boot] n'a pas faites par défaut ou celles que [Spring
Boot] a faites par défaut mais qui doivent être précisées. Dans tous les cas c'est la configuration faite par le développeur
qui a le dernier mot ;
• lignes 11-15 : définissent un projet Maven parent. Ce dernier, [Spring Boot], définit les dépendances les plus courantes
pour les divers types de projets Spring qui peuvent exister (cf page 41 de [ref1]). Lorsqu'on utilise l'une d'elles dans le
fichier [[Link]], sa version n'a pas à être précisée. C'est la version définie dans le projet parent qui sera utilisée ;
• lignes 19-22 : définissent une dépendance sur l'artifact [spring-boot-starter-web]. Cet artifact amène avec lui toutes les
archives nécessaires à un projet Spring MVC. Parmi celles-ci on trouve l'archive d'un serveur Tomcat. C'est lui qui sera
utilisé pour déployer l'application web. On notera que la version de la dépendance n'a pas été mentionnée. C'est celle
mentionnée dans le projet parent qui sera utilisée ;
• lignes 24-27 : définissent une dépendance sur [spring-boot-starter-data-jpa]. Cet artifact amène avec lui les archives
nécessaires à l'implémentation d'une couche [DAO] s'appuyant sur une interface JPA. L'implémentation JPA utilisée par
défaut est Hibernate ;
• lignes 44-48 : définissent une dépendance sur [spring-boot-starter-test]. Cet artifact amène avec lui les archives nécessaires
à l'intégration de tests unitaires JUnit avec Spring. On notera ligne 48, que le [scope] est test, ce qui signifie que la
dépendance n'est nécessaire que pour les tests. Elle ne sera pas incluse dans l'archive produite par la compilation du
projet ;
• lignes 29-32 : le pilote JDBC du SGBD MySQL ;
• lignes 34-37 : pool de connexions. Un pool de connexions est un pool de connexions ouvertes. L'ouverture / fermeture
d'une connexion à un SGBD a un coût (temps, mémoire) et le nombre de connexions ouvertes à un moment donné est
limité. Le pool de connexions tente de résoudre ces deux problèmes : un certain nombre de connexions au SGBD sont
faites dès l'instanciation du pool. Ensuite, de façon transparente, lorsqu'un code ouvre une connexion, une connexion du
pool lui est donnée (pas de temps d'ouverture) et lorsque ce même code ferme la connexion, celle-ci est rendue au pool
(elle n'est pas fermée). Ceci est fait de façon transparente pour le développeur. Le code n'a pas à être modifié ;
• lignes 39-42 : bibliothèque Jackson de gestion du jSON ;
• lignes 50-54 : bibliothèque Google de gestion des collections ;
L'ensemble des dépendances ainsi amenée dans le projet peut être visualisé [1-5] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
343/489
• en [5], la liste des dépendances (vue partielle). Elles sont très nombreuses. L'intérêt de [Spring Boot] est qu'on peut
configurer un projet Spring sans avoir à les connaître toutes ;
Spring
7 4
Travail : en suivant le paragraphe 2.5 page 42 de [ref1] ainsi que ce qui a été fait en TD, construire les entités JPA [AbstractEntity,
Cotisation, Employe, Indemnite]. Le champ annoté par [@ManyToOne] de la classe [Employe] sera cherché en base en mode
Lazy. Les classes JPA vous sont données sans les annotations JPA. Ce sont ces dernières que vous devez créer.
Note : la stratégie de génération de la clé primaire [id] sera la stratégie [IDENTITY] et non [AUTO] comme utilisée dans le
paragraphe 2.5 page 42 de [ref1].
Spring
7 4
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
344/489
Travail : en suivant le paragraphe 2.6 page 47 de [ref1], construire les interfaces [CotisationRepository, EmployeRepository,
IndemniteRepository] de la couche [DAO].
Notes :
• toutes les interfaces étendront l'interface [CrudRepository] de [Spring Data]. L'interface [EmployeRepository] proposera
en plus la méthode suivante :
// recherche d'un employé via son n° SS
Iterable<Employe> findBySS(String SS);
L'employé renvoyé aura son champ [indemnite] rempli. Il faut se rappeler ici que ce champ a une annotation
[@ManyToOne] avec un mode de recherche Lazy. Donc par défaut, ce champ n'est pas initialisé lorsqu'on demande un
employé à l'interface [EmployeRepository] ;
• il peut être utile de lire la classe de test [JUnitDao] qui teste les trois interfaces. Vous comprendrez mieux leur utilisation ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
345/489
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
12. import [Link];
13. import [Link];
14.
15. import [Link];
16.
17. @ComponentScan(basePackages = {"[Link]"})
18. @EnableJpaRepositories(basePackages = { "[Link]"})
19. public class PersistenceConfig {
20.
21. // packages des entités JPA
22. public final static String[] ENTITIES_PACKAGES = { "[Link]"};
23.
24. // la source de données MySQL
25. @Bean
26. public DataSource dataSource() {
27. // source de données TomcatJdbc
28. DataSource dataSource = new DataSource();
29. [Link]("[Link]");
30. [Link]("jdbc:mysql://localhost:3306/dbpam_hibernate");
31. [Link]("root");
32. [Link]("");
33. return dataSource;
34. }
35.
36. // le provider JPA - n'est pas nécessaire si on est satisfait des valeurs par
37. // défaut utilisées par Spring boot
38. // ici on le définit pour activer / désactiver les logs SQL
39. @Bean
40. public JpaVendorAdapter jpaVendorAdapter() {
41. HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
42. [Link](false);
43. [Link](false);
44. [Link]([Link]);
45. return hibernateJpaVendorAdapter;
46. }
47.
48.
49. // EntityManagerFactory : gère la couche JPA
50. @Bean
51. public EntityManagerFactory entityManagerFactory(JpaVendorAdapter jpaVendorAdapter,
[Link] dataSource) {
52. LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
53. [Link](jpaVendorAdapter);
54. [Link](ENTITIES_PACKAGES);
55. [Link](dataSource);
56. [Link]();
57. return [Link]();
58. }
59.
60. // Transaction manager : gère les transactions utilisées par défaut par les [repositories] de Sprin Data
61. @Bean
62. public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
63. JpaTransactionManager txManager = new JpaTransactionManager();
64. [Link](entityManagerFactory);
65. return txManager;
66. }
67.
68. }
Depuis les versions 3.x, il est possible de configurer Spring avec des classes Java plutôt qu'avec des fichiers XML. C'est la voie qui a
été suivie dans ce projet.
• ligne 17 : l'annotation [ComponentScan] est une annotation du framework [Spring] (ligne 5). Elle permet d'indiquer le ou
les dossiers dans lesquels on trouvera des composants Spring (@Component, @Service, @Controller, ...). Ici l'annotation
@Service sera utilisée ultérieurement pour la couche [métier]. La présence de l'annotation [ComponentScan] fait
automatiquement de la classe ainsi annotée, une classe de configuration Spring Boot ;
• ligne 18 : l'annotation [EnableJpaRepositories] est une annotation du framework [Spring Data] (ligne 8). Elle permet
d'indiquer le ou les dossiers dans lesquels on trouvera les interfaces étendant l'interface [CrudRepository] de [Spring Data].
La présence de cette annotation fait automatiquement de la classe ainsi annotée, une classe de configuration Spring Boot ;
• ligne 25 : l'annotation [Bean] est une annotation du framework [Spring] (ligne 4). Elle désigne un objet géré par Spring. Cet
objet est instancié une fois (singleton) lors de l'exploitation initiale de la classe de configuration. Par défaut, le bean porte le
nom de la méthode annotée. On peut également lui donner explicitement un autre nom avec un attribut de l'annotation
[Bean] ;
• lignes 25-34 : la méthode [dataSource] définit le bean nommé 'dataSource'. Ce nom est prédéfini et ne doit pas être changé.
Il sert à définir les quatre informations JDBC nécessaires pour accéder à la base de données ;
• on pourrait s'arrêter là. Les autres informations nécessaires (JPA provider, EntityManagerFactory, gestionnaire de
transactions) peuvent être inférées par [Spring Boot] à partir du Classpath du projet (auto-configuration). Pour cela, il
aurait fallu annoter la classe avec l'annotation [@EnableAutoConfiguration]. L'expérience montre qu'il est préférable de
fixer soi-même la configuration souhaitée. C'est ce qui a été fait ici ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
346/489
• lignes 39-46 : définissent le provider JPA, ici Hibernate. La méthode doit obligatoirement s'appeler [jpaVendorAdapter] ;
• ligne 42 : on inhibe les logs SQL ;
• ligne 43 : le provider JPA ne doit pas régénérer la base ;
• ligne 44 : le provider JPA est associé au SGBD MySQL ;
• lignes 50-58 : définissent l'objet [EntityManagerFactory] qui gère la couche JPA de l'application ;
• ligne 52 : création de l'objet implémentant l'interface [EntityManagerFactory] ;
• ligne 53 : définit Hibernate comme implémentation JPA à utiliser ;
• ligne 54 : définit le (ou les) dossier(s) contenant les entités JPA ;
• ligne 55 : définir la source de données (=base de données) exploitée par la couche JPA ;
• lignes 61-66 : définissent le gestionnaire de transactions utilisé par les classes [repositories] qui implémentent la couche
[DAO] ;
• ligne 63 : instanciation d'un gestionnaire de transactions adapté à une couche JPA ;
• ligne 64 : ce gestionnaire de transactions est associé à l'objet [EntityManagerFactory] qui gère la couche JPA de
l'application ;
Spring
7 4
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
347/489
6. import [Link];
7. import [Link];
8. import [Link].junit4.SpringJUnit4ClassRunner;
9. import [Link];
10. import [Link];
11. import [Link];
12. import [Link];
13. import [Link];
14. import [Link];
15. import [Link];
16.
17. @SpringBootTest(classes = [Link])
18. @RunWith([Link])
19. public class JUnitInitDB {
20.
21. // repositories
22. @Autowired
23. private EmployeRepository employeRepository;
24. @Autowired
25. private IndemniteRepository indemniteRepository;
26. @Autowired
27. private CotisationRepository cotisationRepository;
28.
29. @Before()
30. public void clean() {
31. log("clean------------------------");
32. // on vide la base
33. [Link]();
34. [Link]();
35. [Link]();
36. }
37.
38. // logs
39. private static void log(String message) {
40. [Link]("----------- " + message);
41. }
42.
43. // tests
44. @Test
45. public void test01() {
46. log("test01---------------------");
47. // on la remplit
48. Indemnite indemnite1 = [Link](new Indemnite(1, 1.93, 2, 3, 12));
49. Indemnite indemnite2 = [Link](new Indemnite(2, 2.1, 2.1, 3.1, 15));
50. Employe employe2 = [Link](new Employe("254104940426058", "Jouveinal", "Marie", "5 rue des oiseaux", "St
Corentin", "49203",
51. indemnite2));
52. Employe employe1 = [Link](new Employe("260124402111742", "Laverti", "Justine", "La br�lerie"lerie", "St
Marcel", "49014", indemnite1));
53. Cotisation cotisation1 = [Link](new Cotisation(3.49, 6.15, 9.39, 7.88));
54. // on l'affiche
55. [Link]("Employés ----------------------");
56. for (Employe employe : [Link]()) {
57. [Link](employe);
58. }
59. [Link]("Indemnités ----------------------");
60. for (Indemnite indemnite : [Link]()) {
61. [Link](indemnite);
62. }
63. [Link]("Cotisations ----------------------");
64. for (Cotisation cotisation : [Link]()) {
65. [Link](cotisation);
66. }
67. }
68. }
On a là un test JUnit intégré avec Spring. C'est l'annotation [@RunWith] (ligne 18), une annotation JUnit (ligne 5), qui assure cette
intégration.
• ligne 17 : l'annotation [SpringBootTest] permet d'indiquer la ou les classes de configuration à exploiter pour faire le test. Ici
la classe [PersistenceConfig] que nous avons détaillée va être exploitée. La couche [DAO] va alors être instanciée et reliée
au SGBD. Les tests peuvent alors se dérouler ;
• ligne 22 : l'annotation [Autowired] sert à injecter un composant Spring après instanciation de la classe de test. Il y a
différents types de composants Spring et les interfaces du dossier [repositories] en font partie (cf la classe
[PersistenceConfig]). On peut donc les injecter (lignes 22-27). Ces injections implémentent la couche [DAO] qui va alors
pouvoir être testée ;
Travail : exécutez la classe [JUnitInitDB] qui régénère la base de données. Vérifiez que le test passe et que la base a bien été
régénérée.
Travail : exécutez la classe [JUnitDao] composée de 11 tests. Vérifiez que tous les tests passent.
Spring
7 4
L'interface [IMetier] de la couche [métier] est la suivante :
1. package [Link];
2.
3. import [Link];
4.
5. import [Link];
6.
7. public interface IMetier {
8.
9. // liste des employés
10. List<Employe> getEmployes();
11. // feuille de salaire
12. FeuilleSalaire getFeuilleSalaire(String ss, double ht, int jt);
13.
14. }
• ligne 10 : la méthode [getEmployes] renvoie la liste de tous les employés en base, sans remplir le champ [Indemnite
indemnite] ;
• ligne 12 : la méthode [getFeuilleSalaire] renvoie la feuille de salaire de l'employé de n° SS [ss], ayant travaillé [ht] heures sur
[jt] jours. Cette feuille de salaire inclut l'employé avec le champ [Indemnite indemnite] rempli. Si l'employé de n° SS [ss]
n'existe pas, une exception de type [PamException] sera lancée ;
Spring
7 4
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
349/489
Dans la classe [JUnitMetier], on trouve le code suivant :
1. package metier;
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link].junit4.SpringJUnit4ClassRunner;
10. import [Link];
11. import [Link];
12. import [Link];
13.
14. @SpringBootTest(classes = [Link])
15. @RunWith([Link])
16. public class JUnitMetier {
17.
18. @Autowired
19. @Qualifier("métier")
20. private IMetier metier;
21.
22. @Test
23. public void test1() {
24. [Link](72.4, [Link]("260124402111742", 30, 5).getElementsSalaire().getSalaireNet(), 1e-
6);
25. [Link](368.77, [Link]("254104940426058", 150, 20).getElementsSalaire().getSalaireNet(),
1e-6);
26. int code = 0;
27. try {
28. [Link]("xx", 150, 20);
29. } catch (PamException ex) {
30. code = [Link]();
31. }
32. [Link](101, code);
33. }
34. }
• lignes 18-20 : on injecte une référence sur la couche [métier]. Pour que cela fonctionne, il faut faire plusieurs choses :
C'est l'annotation [@Service] qui fait de la classe [Metier] un composant géré par Spring. On aurait pu utiliser
également l'annotation [@Component]. On peut écrire [@Service] ou [@Service(« nom »]. Dans le premier cas, le
service a par défaut le nom de la classe où la 1ère majuscule est transformée en minuscule. Le nommage n'est
nécessaire que lorsqu'il y a plusieurs composants Spring de même type, ici [IMetier] (Spring travaille avec les
interfaces plutôt qu'avec les classes).
Ici, on dit à Spring de consulter le package [[Link]] pour y trouver des composants. Il y trouvera la classe
[Metier] avec son annotation [@Service] ;
• ligne 19 : l'annotation [@Qualifier] sert à nommer le bean à injecter. N'est utile que si plusieurs beans ont le type [IMetier]
de la ligne 20. C'est le cas dans ce projet ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
350/489
Travail : exécutez la classe [JUnitMetier]. Vérifiez que le test passe. Régénérez auparavant la base avec le test [JUnitInitDB].
Spring
7 4
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
351/489
• ligne 26 : utilisation de la méthode [getSalaire] de l'interface [IMetier] ;
• lignes 2-3 : on remarquera que les employés affichés n'ont pas de champ [Indemnite indemnite] mais ont le champ [long
idIndemnite] qui est la clé étrangère de l'employé vers son indemnité ;
• ligne 4 : l'employé de la feuille de salaire a son champ [Indemnite indemnite] rempli ;
Spring
7 4
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
352/489
Application web
couche [web]
2a 2b
1 Dispatcher
Servlet Contrôleurs/
3 Actions couches Données
Navigateur Vue1
[métier, DAO, JPA]
4b Vue2 2c
Modèles
Vuen
Application web
couche [web]
2a 2b
1 Dispatcher
Servlet Contrôleurs/ couches
3 Actions Données
Navigateur [métier, DAO,
4b ORM]
JSON 2c
Modèles
4a
• en [4a], le modèle qui est une classe Java est transformé en chaîne jSON par une bibliothèque jSON ;
• en [4b], cette chaîne jSON est envoyée au navigateur ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
353/489
On demande les employés :
Le type [Response] ci-dessus est la réponse faite par les deux URL du service web :
1. package [Link];
2.
3. import [Link];
4.
5. public class Response<T> {
6.
7. // ----------------- propriétés
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
354/489
8. // statut de l'opération
9. private int status;
10. // les éventuels messages d'erreur
11. private List<String> messages;
12. // le corps de la réponse
13. private T body;
14.
15. // constructeurs
16. public Response() {
17.
18. }
19.
20. public Response(int status, List<String> messages, T body) {
21. [Link] = status;
22. [Link] = messages;
23. [Link] = body;
24. }
25.
26. // getters et setters
27. ...
28.
29. }
Application web
couche [web]
Dispatcher
Servlet Contrôleurs/
Actions [Application couches
Navigateur
Model] [métier, DAO, ORM]
JSON 1
Modèles
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
355/489
17. @Autowired
18. private IMetier métier;
19.
20. // liste des employés
21. private List<Employe> employés = null;
22. // liste des messages d'erreur
23. private List<String> messages = null;
24.
25. @PostConstruct
26. public void init() {
27. // on récupère les employés
28. try {
29. employés = mé[Link]();
30. } catch (Exception ex) {
31. messages = [Link](ex);
32. }
33. }
34.
35.
36. // getters et setters
37. public List<String> getMessages() {
38. return messages;
39. }
40.
41. public void setMessages(List<String> messages) {
42. [Link] = messages;
43. }
44.
45. @Override
46. public List<Employe> getEmployes() {
47. return employés;
48. }
49.
50. @Override
51. public FeuilleSalaire getFeuilleSalaire(String ss, double ht, int jt) {
52. return mé[Link](ss, ht, jt);
53. }
54. }
• ligne 13 : la classe [ApplicationModel] sera un composant géré par Spring. Il sera instancié en un unique exemplaire
(singleton) lors de l'exploitation de la classe de configuration de l'application web ;
• ligne 14 : la classe [ApplicationModel] implémente l'interface [IMetier] ;
• lignes 17-18 : une référence de la couche [métier] sera injectée ici. A noter que le champ annoté par [Autowired] ne
nécessite pas la présence d'un setter ;
• ligne 25 : l'annotation [PostConstruct] désigne une méthode qui doit être exécutée après l'instanciation de la classe.
Lorsqu'elle s'exécute, les injections Spring ont déjà eu lieu ;
• lignes 26-39 : la méthode [init] a pour but de stocker la liste des employés dans le champ de la ligne 21. Si elle n'y arrive
pas, elle stocke la liste des messages d'erreur de l'exception dans le champ de la ligne 23. La classe [Static] utilisée ici vous
est donnée ;
• lignes 36-44 : implémentent les méthodes de l'interface [IMetier] :
◦ lignes 45-48 : la méthode [getEmployes] se contente de rendre la liste des employés stockée localement. Ainsi si cette
liste change en base, l'utilisateur ne verra pas ce changement. On suppose ici que ce cas est rare ;
◦ lignes 50-53 : la méthode [getFeuilleSalaire] se contente d'appeler la méthode de même nom dans la couche [métier] ;
[Link] Le contrôleur
Application web
couche [web]
Dispatcher
Servlet Contrôleurs/
Actions [Application couches
Navigateur
Model] [métier, DAO, ORM]
JSON 1
Modèles
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
356/489
Le contrôleur [PamController] a la structure suivante :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
12. import [Link];
13. import [Link];
14. import [Link];
15. import [Link];
16.
17. import [Link];
18. import [Link];
19.
20. @Controller
21. public class PamController {
22.
23. @Autowired
24. private ApplicationModel application;
25.
26. // liste de messages
27. private List<String> messages;
28.
29. @PostConstruct
30. public void init() {
31. // messages d'erreur de l'application
32. messages = [Link]();
33. }
34.
35. @RequestMapping(...)
36. @ResponseBody
37. public String getEmployes() throws JsonProcessingException {
38. // mapper JSON
39. ObjectMapper mapper=new ObjectMapper();
40.
41. ...
42. }
43.
44. @RequestMapping(...)
45. @ResponseBody
46. public String getFeuilleSalaire(...) throws JsonProcessingException {
47. // mapper JSON
48. ObjectMapper mapper=new ObjectMapper();
49.
50. ...
51. }
52. }
• lignes 36 et 45 : l'annotation [@ResponseBody] indique que la méthode envoie elle-même la réponse au client qui a émis la
requête HTTP qu'elle traite. Dans les deux cas, cette réponse est une chaîne jSON ;
• la méthode [getEmployes] rend un objet de type [Response<List<Employe>>] qu'il faut sérialiser en jSON ;
• la méthode [getFeuilleSalaire] rend un objet de type [Response<FeuilleSalaire>] qu'il faut sérialiser en jSON ;
La sérialisation par défaut sérialise tous les champs des objets [Response<List<Employe>>] et [Response<FeuilleSalaire>].
Il y a un problème avec le type [Employe]. Il a un champ [Indemnite indemnite] annoté par [@ManyToOne(fetch =
[Link])]. Ainsi la méthode [getEmployes] de la couche [métier] rend une liste d'employés avec le champ [Indemnite
indemnite] non initialisé. Si on ne fait rien, la sérialisation par défaut de Spring va tenter de sérialiser le champ [Indemnite
indemnite] en appelant la méthode [[Link]()]. Or cette méthode est redéfinie par Hibernate pour aller chercher
l'indemnité dans le contexte de persistance JPA. Nous sommes ici dans la couche web et le contexte de persistance n'existe pas. On
obtient alors une exception. Il faut donc trouver un moyen de dire que le champ [Indemnite indemnite] du type [Employe] ne doit
pas être sérialisé en jSON.
Par ailleurs, la méthode [getFeuilleSalaire] rend une feuille de salaire qui contient un employé avec son indemnité. Donc celle-ci doit
être sérialisée en jSON.
Résumons : on a un problème avec l'entité [Employe]. Son champ [Indemnite indemnite] doit parfois être sérialisé (méthode
getFeuilleSalaire) parfois pas (méthode getEmployes).
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
357/489
Le contrôle de la sérialisation de la classe [Employe] qui pose problème se fait en deux temps :
• il faut annoter la classe [Employe] avec des annotations de la bibliothèque jSON utilisée ici (il en existe d'autres) ;
• indiquer dans le code quels champs doivent être sérialisés ;
Le premier point se fait en ajoutant une annotation de filtre sur la classe [Employe] ;
1. import [Link];
2. ...
3.
4. ...
5. @JsonFilter("employeFilter")
6. public class Employe extends AbstractEntity implements Serializable {
• ligne 5 : on déclare que la sérialisation de la classe [Employe] est contrôlée par un filtre nommé [employeFilter]. Ce filtre
est défini dans le code Java et c'est grâce à lui qu'on définit les champs à sérialiser ;
• ligne 8 : on définit un filtre qui sérialise tous les champs de l'objet auquel il sera appliqué sauf le champ appelé
[indemnite] ;
• ligne 9 : ce filtre est ajouté aux filtres du convertisseur jSON et appelé [employeFilter] ;
Le type T (lignes 2 et 10) va être le type List<Employe>. Le champ [body] de la ligne va être sérialisé et ainsi chaque employé va
être sérialisé. Comme la classe [Employe] utilise le filtre jSON [employeFilter] et que ce filtre ne sérialise pas le champ [Indemnite
indemnite], on aura une liste d'employés sans leurs indemnités.
Ici la réponse est de type [Response<FeuilleSalaire>] qui contient un champ de type [Employe]. Ce champ sera alors sérialisé en
jSON avec le champ [Indemnite indemnite].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
358/489
Application web
couche [web]
2a 2b
1 Dispatcher
Servlet Contrôleurs/ couches
3 Actions Données
Navigateur [métier, DAO,
4b ORM]
JSON 2c
Modèles
4a
• le fichier [PersistenceConfig], déjà présenté, qui configure les couches [métier, DAO, JPA] ;
• le fichier [WebConfig] qui configure la couche [web/jSON] ;
• le fichier [AppConfig] qui configure la totalité de l'application ;
• ligne 12 : l'annotation [ComponentScan] définit les packages où des composants Spring ont été définis. Cette annotation
fait de la classe annotée, une classe de configuration Spring ;
• ligne 13 : active le framework Spring MVC. Cette annotation entraîne des configurations par défaut de ce framework ;
• lignes 16-20 : définissent le bean [dispatcherServlet]. La classe [DispatcherServlet] est la servlet du framework Spring
MVC. Elle joue le rôle de [FrontController] : elle intercepte les requêtes adressées au site Spring MVC et les fait traiter par
un des contrôleurs (Controller) du site ;
• ligne 18 : instanciation de la classe ;
• lignes 22-25 : la servlet [dispatcherServlet] traite toutes les URL (/*) ;
• lignes 27-30 : activent le serveur Tomcat embarqué dans les dépendances du projet. Il fonctionnera sur le port 8080 ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
359/489
8.
9. }
• ligne 6 : cette ligne permet d'indiquer les autres fichiers de configuration à exploiter :
◦ [PersistenceConfig] qui configure les couches [métier, DAO, JPA],
◦ [WebConfig] qui configure la couche [web/jSON] ;
La classe [BootWeb] est la classe exécutable qui lance le service web. Son code est le suivant :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5.
6. public class BootWeb {
7. public static void main(String[] args) {
8. [Link]([Link], args);
9. }
10.
11. }
• ligne 8 : la méthode [[Link]] est une méthode statique de [Spring Boot] (ligne 3). Elle permet de lancer une
application Spring en lui donnant comme paramètres :
◦ la classe de configuration de l'ensemble du projet ;
◦ les éventuels arguments passés à la méthode [main] ;
Parce qu'ici le fichier [AppConfig] configure à la fois une couche de persistance et une couche web, le serveur Tomcat embarqué
dans les dépendances, va être lancé et l'application web déployée dessus. Les logs console sont alors les suivants :
1. ...
2. 2014-09-18 [Link].260 INFO 10560 --- [ main] [Link] : HCANN000001: Hibernate
Commons Annotations {[Link]}
3. 2014-09-18 [Link].360 INFO 10560 --- [ main] [Link] : HHH000400: Using
dialect: [Link]
4. 2014-09-18 [Link].627 INFO 10560 --- [ main] [Link] : HHH000397: Using
ASTQueryTranslatorFactory
5. 2014-09-18 [Link].907 INFO 10560 --- [ main] [Link] : Mapped URL path
[/**/[Link]] onto handler of type [class [Link]]
6. 2014-09-18 [Link].028 INFO 10560 --- [ main] [Link] : Mapped
"{[/employes],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public
[Link] [Link]()
7. 2014-09-18 [Link].029 INFO 10560 --- [ main] [Link] : Mapped
"{[/salaire/{SS}/{ht}/{jt}],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public
[Link] [Link]([Link],double,int)
8. 2014-09-18 [Link].033 INFO 10560 --- [ main] [Link] : Mapped
"{[/error],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public
[Link]<[Link]<[Link], [Link]>>
[Link]([Link])
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
360/489
9. 2014-09-18 [Link].033 INFO 10560 --- [ main] [Link] : Mapped
"{[/error],methods=[],params=[],headers=[],consumes=[],produces=[text/html],custom=[]}" onto public
[Link]
[Link]([Link])
10. 2014-09-18 [Link].061 INFO 10560 --- [ main] [Link] : Mapped URL path [/**]
onto handler of type [class [Link]]
11. 2014-09-18 [Link].061 INFO 10560 --- [ main] [Link] : Mapped URL path
[/webjars/**] onto handler of type [class [Link]]
12. 2014-09-18 [Link].429 INFO 10560 --- [ main] [Link] : Registering beans for
JMX exposure on startup
13. 2014-09-18 [Link].562 INFO 10560 --- [ main] [Link] : Tomcat started on
port(s): 8080/http
14. 2014-09-18 [Link].565 INFO 10560 --- [ main] [Link] : Started BootWeb in
8.801 seconds (JVM running for 9.397)
On prend alors un navigateur et on demande l'URL [[Link] pour avoir la liste des employés sous forme
jSON :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
361/489
• en [4], on donne un nom à la configuration ;
• en [5-6], on se positionne sur le répertoire du projet (c'est lui qui contient le fichier [Link] qui va être exécuté) ;
• en [7], la commande Maven à exécuter est la commande [package] ;
C'est le plugin [spring-boot-maven-plugin] (ligne 9) qui va produire l'archive exécutable. Pour qu'une archive jar soit exécutable, il
faut indiquer quelle est la classe à exécuter dans l'archive, celle qui contient une méthode statique [main]. Cette information est
donnée par la ligne 3.
Ceci fait, on exécute [1] cette configuration d'exécution (il faut auparavant arrêter le serveur web s'il est en cours d'exécution) :
Si l'exécution se passe bien une archive a été créée dans le dossier [target] du projet [2-3]. On l'exécute dans une fenêtre DOS :
...\server-pam\target>java -jar [Link]
Il faut être positionné sur le dossier [target]. Le service web est alors lancé :
1. ....
2. 2014-09-18 [Link].050 INFO 8552 --- [ main] [Link] : Mapped URL path [/**]
onto handler of type [class [Link]]
3. 2014-09-18 [Link].050 INFO 8552 --- [ main] [Link] : Mapped URL path
[/webjars/**] onto handler of type [class [Link]]
4. 2014-09-18 [Link].568 INFO 8552 --- [ main] [Link] : Registering beans for
JMX exposure on startup
5. 2014-09-18 [Link].712 INFO 8552 --- [ main] [Link] : Tomcat started on
port(s): 8080/http
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
362/489
6. 2014-09-18 [Link].715 INFO 8552 --- [ main] [Link] : Started BootWeb in 11.163 seconds (JVM running
for 11.981)
4.6 Le client
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
363/489
16.
17. <!-- spring boot -->
18. <parent>
19. <groupId>[Link]</groupId>
20. <artifactId>spring-boot-starter-parent</artifactId>
21. <version>[Link]</version>
22. </parent>
23.
24. <dependencies>
25. <!-- RestTemplate -->
26. <dependency>
27. <groupId>[Link]</groupId>
28. <artifactId>spring-boot-starter-web</artifactId>
29. </dependency>
30. <!-- librairie jSON utilisée par Spring RestTemplate -->
31. <dependency>
32. <groupId>[Link]</groupId>
33. <artifactId>jackson-databind</artifactId>
34. </dependency>
35. <!-- pour les tests -->
36. <dependency>
37. <groupId>[Link]</groupId>
38. <artifactId>spring-boot-starter-test</artifactId>
39. <scope>test</scope>
40. </dependency>
41. <!-- composant utilisé par Spring RestTemplate -->
42. <dependency>
43. <groupId>[Link]</groupId>
44. <artifactId>httpclient</artifactId>
45. </dependency>
46. </dependencies>
47.
48. </project>
• lignes 25-29 : une dépendance sur le framework [Spring web]. Ce framework fournit la classe [RestTemplate] qui permet
de dialoguer avec un serveur web/jSON. La version utilisée est celle définie par le projet Maven parent des lignes 18-22 ;
• lignes 31-35 : la dépendance sur la bibliothèque jSON [Jackson]. Elle est utilisée ici pour désérialiser les chaînes jSON
envoyées par le service web. La version utilisée est celle définie par le projet Maven parent des lignes 14-18 ;
• lignes 37-41 : les dépendances sur les bibliothèques d'intégration Spring / JUnit ;
• lignes 42-45 : une dépendance sur le composant [HttpClient] qui permet de fixer les timeouts des requêtes HTTP faites
par le client ;
Les éléments du dossier [entities] sont identiques à leurs versions de mêmes noms côté serveur. Cependant, pour les entités JPA
[AbstractEntity, Cotisation, Employe, Indemnite], on n'a pas conservé les annotations JPA, inutiles côté client.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
364/489
La classe [Config] configure la couche [DAO] :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8.
9. @ComponentScan(basePackages = {"[Link]"})
10. public class Config {
11.
12. @Bean
13. public RestTemplate restTemplate() {
14. // création du composant RestTemplate
15. HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
16. RestTemplate restTemplate = new RestTemplate(factory);
17. // résultat
18. return restTemplate;
19. }
20.
21. }
• ligne 9 : demande à ce que le package [[Link]] soit exploré pour trouver des composants Spring. Un seul sera
trouvé : la classe [Dao] annotée par l'annotation Spring [@Service]. L'annotation [@ComponentScan] fait de la classe
annotée une classe de configuration Spring ;
• lignes 12-19 : définition du bean [restTemplate] qui va assurer les échanges avec le serveur web / jSON. Ce bean est
amené avec la dépendance [Spring web] ;
• ligne 15 : le composant [HttpComponentsClientHttpRequestFactory factory] va permettre de fixer les timeouts des
requêtes HTTP vers le serveur. Ce composant a besoin de la dépendance sur la bibliothèque [[Link]|
httpclient] ;
• ligne 16 : le bean [restTemplate] est instancié avec pour paramètre le composant
[HttpComponentsClientHttpRequestFactory factory] précédent ;
Couche Couche
[console] [DAO] Service
web
Spring 4
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
365/489
La classe [Response] est la même que celle présente côté serveur. C'est la réponse reçue pour tout appel au service web / jSON.
• les méthodes des lignes 10 et 13 reprennent les mêmes noms et signatures que leurs équivalentes dans la couche [métier]
du serveur. C'est normal. Tout se passe comme si la couche [console] du client dialoguait de façon transparente avec la
couche [métier] du service web ;
• ligne 16 : sert à fixer un délai maximal d'attente d'une réponse du service web. Un délai par défaut existe mais il est de
plusieurs dizaines de secondes. En général, on ne veut pas attendre aussi longtemps ;
• ligne 19 : sert à fixer l'URL racine du service web / jSON. Dans nos exemples précédents, cette URL serait
[[Link] ;
Avant d'implémenter l'interface [IDao], nous passons par l'étape intermédaire de création d'une classe abstraite [AbstractDao] :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
12. import [Link];
13. import [Link];
14.
15. import [Link];
16. import [Link];
17. import [Link];
18.
19. public abstract class AbstractDao {
20.
21. // data
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
366/489
22. @Autowired
23. private RestTemplate restTemplate;
24. private String urlServiceWebJson;
25.
26. // URL service web / jSON
27. public void setUrlServiceWebJson(String url) {
28. [Link] = url;
29. }
30.
31. // timeout
32. public void setTimeout(int timeout) {
33. // on fixe le timeout des requêtes du client web
34. HttpComponentsClientHttpRequestFactory factory = (HttpComponentsClientHttpRequestFactory) restTemplate
35. .getRequestFactory();
36. [Link](timeout);
37. [Link](timeout);
38. }
39.
40. // requête générique
41. protected String getResponse(String url, String jsonPost) {
42. // url : URL à contacter
43. // jsonPost : la valeur jSON à poster, null si opération GET
44. try {
45. // exécution requête
46. RequestEntity<?> request;
47. // valeur à poster ?
48. if (jsonPost == null) {
49. // opération GET
50. HeadersBuilder<?> headersBuilder = [Link](new URI([Link]("%s%s", urlServiceWebJson, url)))
51. .accept(MediaType.APPLICATION_JSON);
52. request = [Link]();
53. } else {
54. // opération POST
55. BodyBuilder bodyBuilder = [Link](new URI([Link]("%s%s", urlServiceWebJson, url)))
56. .header("Content-Type", "application/json").accept(MediaType.APPLICATION_JSON);
57. request = [Link](jsonPost);
58. }
59. // on exécute la requête
60. return [Link](request, new ParameterizedTypeReference<String>() {
61. }).getBody();
62. } catch (URISyntaxException e) {
63. throw new PamException(getMessageForException(e), 20);
64. } catch (RuntimeException e) {
65. throw new PamException(getMessageForException(e), 21);
66. }
67. }
68.
69. // liste des messages d'erreur d'une exception
70. protected static String getMessageForException(Exception exception) {
71. ...
72. }
73. }
• ligne 19 : la classe [AbstractDao] est déclarée abstraite : elle ne peut pas être instanciée. Pour l'utiliser, il faudra la dériver ;
• lignes 22-23 : injection du composant [RestTemplate restTemplate] qui va assurer les échanges HTTP avec le serveur
web / jSON ;
• lignes 24-29 : gestion de l'URL du service web / jSON ;
• lignes 32-38 : gestion des timeouts des requêtes HTTP du client ;
• ligne 34 : on récupère le composant [HttpComponentsClientHttpRequestFactory factory] encapsulé dans le composant
[RestTemplate restTemplate] ;
• ligne 36 : fixe le timeout de l'attente de la connexion au service web / jSON ;
• ligne 37 : fixe le timeout de l'attente de la réponse à une requête HTTP ;
• lignes 41-67 : obtient la réponse jSON d'une requête HTTP faite à l'URL [url]. Si cette requête doit poster une valeur, le
paramètre [jsonPost] représente la chaîne jSON de cette valeur postée. Dans ce client, les deux URL [/employes] et
[/salaire] sont demandées avec une opération GET. On a cependant voulu donner un caractère plus générique à la
méthode [getResponse] ;
• lignes 60-61 : l'instruction qui fait la requête au serveur et reçoit sa réponse. Le composant [RestTemplate] offre un
nombre important de méthodes d'échange avec le serveur. On aurait pu choisir une autre méthode que [exchange]. Le
second paramètre de l'appel fixe le type de la réponse attendue, ici une chaîne jSON. Le premier paramètre est la requête
de type [RequestEntity] (ligne 46). Le résultat de la méthode [exchange] est de type [ResponseEntity<String>]. Le type
[ResponseEntity] encapsule la réponse complète du serveur, entêtes HTTP et document envoyés par celui-ci. De même le
type [RequestEntity] encapsule toute la requête du client incluant les entêtes HTTP et l'éventuelle valeur postée ;
• ligne 61 : c'est le corps de l'objet [ResponseEntity<String>] qui est rendue à la méthode appelante, ç-à-d la chaîne jSON
envoyée par le serveur ;
• lignes 45-58 : il nous faut construire la requête de type [RequestEntity]. Elle est différente selon que l'on utilise un GET ou
un POST pour faire la requête ;
• lignes 50-52 : la requête pour un GET. La classe [RequestEntity] offre des méthodes statiques pour créer les requêtes
GET, POST, HEAD,... La méthode [[Link]] permet de créer une requête GET en chaînant les différentes
méthodes qui construisent celle-ci :
◦ la méthode [[Link]] admet pour paramètre l'URL cible sous la forme d'une instance URI ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
367/489
◦ la méthode [accept] permet de définir les éléments de l'entête HTTP [Accept]. Ici, nous indiquons que nous acceptons
le type [application/json] que va envoyer le serveur ;
◦ le résultat de ce chaînage de méthodes est un type [HeadersBuilder] ;
• ligne 52 : la méthode [[Link]] utilise ces différentes informations pour construire le type [RequestEntity] de
la requête ;
• lignes 55-57 : la requête pour un POST. La méthode [[Link]] permet de créer une requête POST en chaînant
les différentes méthodes qui construisent celle-ci :
◦ la méthode [[Link]] admet pour paramètre l'URL cible sous la forme d'une instance URI ;
◦ la méthode [header] permet de définir les entêtes HTTP que l'on souhaite utiliser. Ici on inclut dans la requête l'entête
[Content-Type: application/json] pour indiquer au serveur web / jSON que la valeur postée va lui arriver sous la
forme d'une chaîne jSON ;
◦ la méthode [accept] permet d'indiquer que nous acceptons le type [application/json] que va envoyer le serveur ;
• ligne 57 : la méthode [[Link]] fixe la valeur postée. Celle-ci est le 2ième paramètre de la méthode générique
[getResponse] (ligne 41) ;
• lignes 62-66 : s'il se produit une erreur quelconque on lance une exception de type [PamException] ;
• ligne 2 : la méthode [getMessageForException] reçoit pour paramètre une exception. Elle rend comme résultat, une chaîne
de caractères qui est la concaténation des messages d'erreurs de la pile d'exceptions contenue dans l'exception reçue en
paramètre, sous la forme [msg1][msg2][...] ;
Nous l'avons dit, la classe [AbstractDao] n'est pas utilisable car elle est abstraite. Il faut la dériver pour en faire quelque chose. Elle
contient tout ce qui peut être factorisé dans le code des classes filles. C'est souvent de cette façon qu'on utilise les classes abstraites.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
368/489
36. if (status != 0) {
37. throw new PamException(getMessageFromMessages([Link]()), status);
38. } else {
39. return [Link]();
40. }
41. }
42.
43. // concaténer une liste de messages
44. private String getMessageFromMessages(List<String> messages) {
45. StringBuilder buffer = new StringBuilder();
46. for (String message : messages) {
47. [Link](message).append("\n");
48. }
49. return [Link]();
50. }
51.
52. @Override
53. public FeuilleSalaire getFeuilleSalaire(String SS, double ht, int jt) {
54. ...
55. }
56. }
Spring 4
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
369/489
25. [Link]("[Link]
26. }
27.
28. @Test
29. public void test1() {
30. [Link](72.4, [Link]("260124402111742", 30, 5).getElementsSalaire().getSalaireNet(), 1e-6);
31. [Link](368.77, [Link]("254104940426058", 150, 20).getElementsSalaire().getSalaireNet(), 1e-
6);
32. boolean erreur = false;
33. try {
34. [Link]("xx", 150, 20);
35. } catch (PamException ex) {
36. erreur = true;
37. }
38. [Link](erreur);
39. }
40.
41. @Test
42. public void test2() {
43. [Link]("Liste des employés");
44. for (Employe employe : [Link]()) {
45. [Link](employe);
46. }
47. }
48. }
• ligne 16 : le test est configuré avec la classe de configuration [Config] ce qui permet au composant [Dao] d'être injecté aux
lignes 19-20 ;
• lignes 22-26 : avant chaque test (annotation @Before), on fixe le timeout des connexions HTTP et l'URL du service
web/jSON ;
Travail : écrire la classe [Main]. Elle doit faire la même chose que dans le TD (cf paragraphe 4.10.1).
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
370/489
5 Étude de cas n° 5 : couches basses d'une application e-commerce
5.2 Le problème
Nous allons étudier les couches basses d'une application de commerce électronique dans un environnement Spring MVC /
Hibernate / Tomcat :
7 Spring
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
371/489
L'application est une application de commerce électronique.
• on y vend des produits enregistrés dans la table [product]. Un produit appartient à une catégorie enregistrée dans la table
[category]. Le lien entre les deux tables se fait via la clé étrangère [product].category_id ;
• les trois autres tables [customer, customer_order, ordered_product] sont au départ vides ;
• un client qui fait un achat le fait via une page web de confirmation où il entre des informations le concernant. Celles-ci
seront enregistrées dans la table [customer]. Sa commande est enregistrée dans la table [customer_order]. Celle-ci a un lien
vers la table [customer] via la clé étrangère [customer_order].customer_id. Chaque produit acheté fait l'objet d'un
enregistrement dans la table [ordered_product]. La clé étrangère [ordered_product].product_id fait un lien vers le
produit acheté, la clé étrangère [ordered_product].customer_order_id faisant elle un lien vers la table [customer_order]
et donc vers la table [customer].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
372/489
20. `cc_number` VARCHAR(19) COLLATE utf8_unicode_ci NOT NULL,
21. PRIMARY KEY (`id`) USING BTREE,
22. UNIQUE KEY `name` (`name`) USING BTREE,
23. UNIQUE KEY `cc_number` (`cc_number`) USING BTREE
24. ) ENGINE=InnoDB
25. AUTO_INCREMENT=249 CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci'
26. COMMENT='maintains customer details'
27. ;
28.
29. CREATE TABLE `customers_orders` (
30. `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
31. `versioning` BIGINT(20) DEFAULT 1,
32. `amount` DECIMAL(6,2) NOT NULL,
33. `date_created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
34. `confirmation_number` INTEGER(10) UNSIGNED NOT NULL,
35. `customer_id` BIGINT(20) UNSIGNED NOT NULL,
36. PRIMARY KEY (`id`) USING BTREE,
37. KEY `fk_customer_order_customer` (`customer_id`) USING BTREE,
38. CONSTRAINT `customers_orders_fk1` FOREIGN KEY (`customer_id`) REFERENCES `customers` (`id`) ON DELETE CASCADE ON UPDATE
CASCADE
39. ) ENGINE=InnoDB
40. AUTO_INCREMENT=19 CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci'
41. COMMENT='maintains customer order details'
42. ;
43.
44. CREATE TABLE `products` (
45. `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
46. `versioning` BIGINT(20) DEFAULT 1,
47. `name` VARCHAR(45) COLLATE utf8_unicode_ci NOT NULL,
48. `price` DECIMAL(5,2) NOT NULL,
49. `description` TINYTEXT COLLATE utf8_unicode_ci,
50. `last_update` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
51. `category_id` BIGINT(20) UNSIGNED NOT NULL,
52. PRIMARY KEY (`id`) USING BTREE,
53. UNIQUE KEY `name` (`name`) USING BTREE,
54. KEY `fk_product_category` (`category_id`) USING BTREE,
55. CONSTRAINT `products_fk1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
56. ) ENGINE=InnoDB
57. AUTO_INCREMENT=17 CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci'
58. COMMENT='contains product details'
59. ;
60.
61. CREATE TABLE `ordered_products` (
62. `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
63. `versioning` BIGINT(20) DEFAULT 1,
64. `customer_order_id` BIGINT(20) UNSIGNED NOT NULL,
65. `product_id` BIGINT(20) UNSIGNED NOT NULL,
66. `quantity` SMALLINT(5) UNSIGNED NOT NULL DEFAULT 1,
67. PRIMARY KEY (`id`) USING BTREE,
68. UNIQUE KEY `customer_order_id` (`customer_order_id`, `product_id`) USING BTREE,
69. KEY `fk_ordered_product_customer_order` (`customer_order_id`) USING BTREE,
70. KEY `fk_ordered_product_product` (`product_id`) USING BTREE,
71. CONSTRAINT `ordered_products_fk1` FOREIGN KEY (`customer_order_id`) REFERENCES `customers_orders` (`id`) ON DELETE
CASCADE ON UPDATE CASCADE,
72. CONSTRAINT `ordered_products_fk2` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE
CASCADE
73. ) ENGINE=InnoDB
74. AUTO_INCREMENT=37 CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci'
75. COMMENT='matches products with customer orders and records their quantity'
76. ;
• on notera que toutes les clés primaires sont en mode [AUTO_INCREMENT], ç-à-d générées par le SGBD ;
• on notera lignes 38, 55, 71, 72 les attributs [ON DELETE CASCADE] et [ON UPDATE CASCADE] sur les clés
étrangères ;
• ligne 38, l'attibut [ON DELETE CASCADE] sur la clé étrangère [CustomerOrders.customer_id --> [Link]] fait
que lorsqu'on supprime un client [Customer], tous les ordres [CustomerOrder] qui dépendent de lui sont également
supprimés ;
• ligne 71, l'attibut [ON DELETE CASCADE] sur la clé étrangère [OrderedProducts.customer_order_id -->
[Link]] fait que lorsqu'on supprime une commande [CustomerOrder], tous les produits [OrderedProduct] de
cette commande sont également supprimés ;
• au final, les deux règles précédentes font que lorsqu'on supprime un client [Customer], tous les ordres [CustomerOrder] et
les produits [OrderedProduct] qui dépendent de lui sont également supprimés. C'est important à comprendre car le code
qui suit utilise cette particularité ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
373/489
[category]
[product]
[customer] [customer_order]
[ordered_product]
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
374/489
Avec WampServer / PhpMyAdmin, exécutez le script SQL trouvé dans le dossier [database]. Le script crée la base et la remplit
avec des données. La base ne doit pas déjà exister.
5.5 L'application
Nous voulons construire une application client / serveur suivant l'architecture suivante :
Les échanges client / serveur se font en jSON. Il y a deux projets Netbeans pour implémenter cette architecture :
5.6 Le serveur
Nous nous intéressons ici à l'écriture du serveur. Celui-ci a l'architecture suivante :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
375/489
Application web
couche [web]
2a 2b
1 Dispatcher
Servlet Contrôleurs/
3 Actions couches Données
Navigateur [métier, DAO, JPA]
4b
JSON 2c
Modèles
4a
Spring
7
1 2 3
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
376/489
◦ [[Link]] : implémente la couche [métier] - à implémenter ;
◦ [[Link]] : implémente le chariot de l'utilisateur - vous est donné ;
◦ [[Link]] : implémente le serveur web / jSON - à implémenter partiellement ;
◦ [[Link]] : contient la classe exécutable qui lance le serveur - vous est donné ;
◦ [[Link]] : rassemble les classes de configuration Spring - vous est donné ;
◦ [[Link]] : contient les classes d'exception utilisées par le serveur - vous est donné ;
• [3] : les tests JUnit qui vont vous permettre de tester les différents éléments de votre serveur ;
Note : vérifiez que le JDK utilisé par le projet est un JDK 1.8.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
377/489
5.6.2 Configuration Maven
Le projet du serveur est un projet Maven dont le fichier [[Link]] des dépendances est le suivant :
1. <project xsi:schemaLocation="[Link] [Link]
2. xmlns="[Link] xmlns:xsi="[Link]
3.
4. <modelVersion>4.0.0</modelVersion>
5. <groupId>[Link]</groupId>
6. <artifactId>afb-server-metier-dao-skel</artifactId>
7. <version>0.0.1-SNAPSHOT</version>
8. <name>afb-server-metier-dao-skel</name>
9.
10. <parent>
11. <groupId>[Link]</groupId>
12. <artifactId>spring-boot-starter-parent</artifactId>
13. <version>[Link]</version>
14. </parent>
15.
16. <dependencies>
17. <!-- Spring web -->
18. <dependency>
19. <groupId>[Link]</groupId>
20. <artifactId>spring-boot-starter-web</artifactId>
21. </dependency>
22. <!-- Spring data jpa -->
23. <dependency>
24. <groupId>[Link]</groupId>
25. <artifactId>spring-boot-starter-data-jpa</artifactId>
26. </dependency>
27. <!-- Spring test -->
28. <dependency>
29. <groupId>[Link]</groupId>
30. <artifactId>spring-boot-starter-test</artifactId>
31. <scope>test</scope>
32. </dependency>
33. <!-- logs -->
34. <dependency>
35. <groupId>[Link]</groupId>
36. <artifactId>spring-boot-starter-logging</artifactId>
37. </dependency>
38. <!-- pilote JDBC du SGBD -->
39. <dependency>
40. <groupId>mysql</groupId>
41. <artifactId>mysql-connector-java</artifactId>
42. </dependency>
43. <!-- Tomcat JDBC -->
44. <dependency>
45. <groupId>[Link]</groupId>
46. <artifactId>tomcat-jdbc</artifactId>
47. </dependency>
48. <!-- bibliothèque jSON -->
49. <dependency>
50. <groupId>[Link]</groupId>
51. <artifactId>jackson-core</artifactId>
52. </dependency>
53. <dependency>
54. <groupId>[Link]</groupId>
55. <artifactId>jackson-databind</artifactId>
56. </dependency>
57. <!-- Google Guava -->
58. <dependency>
59. <groupId>[Link]</groupId>
60. <artifactId>guava</artifactId>
61. <version>16.0.1</version>
62. </dependency>
63. <!-- Spring Boot -->
64. <dependency>
65. <groupId>[Link]</groupId>
66. <artifactId>spring-boot</artifactId>
67. </dependency>
68. </dependencies>
69.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
378/489
70. <properties>
71. <[Link]>UTF-8</[Link]>
72. <[Link]>1.8</[Link]>
73. <[Link]>1.8</[Link]>
74. <[Link]>1.8</[Link]>
75. </properties>
76.
77. <build>
78. <plugins>
79. <plugin>
80. <groupId>[Link]</groupId>
81. <artifactId>maven-surefire-plugin</artifactId>
82. <version>2.18.1</version>
83. </plugin>
84. <plugin>
85. <artifactId>maven-assembly-plugin</artifactId>
86. <configuration>
87. <descriptorRefs>
88. <descriptorRef>jar-with-dependencies</descriptorRef>
89. </descriptorRefs>
90. </configuration>
91. </plugin>
92. </plugins>
93. </build>
94.
95. <repositories>
96. <repository>
97. <id>[Link]</id>
98. <name>Spring Milestone Repository</name>
99. <url>[Link]
100. </repository>
101. </repositories>
102.
103. </project>
• lignes 10-14 : une dépendance sur le framework [Spring Boot], une branche de l'écosystème Spring. Ce framework permet
une configuration minimale de Spring. Selon les archives présentes dans le Classpath du projet, [Spring Boot] infère une
configuration plausible ou probable pour celui-ci. Ainsi si Hibernate est dans le Classpath du projet alors [Spring Boot]
infèrera que l'implémentation JPA utilisée va être Hibernate et configurera Spring dans ce sens. Le développeur n'a plus à
le faire. Il ne lui reste alors à faire que les configurations que [Spring Boot] n'a pas faites par défaut ou celles que [Spring
Boot] a faites par défaut mais qui doivent être précisées. Dans tous les cas c'est la configuration faite par le développeur
qui a le dernier mot. C'est la cas ici : nous n'utilisons jamais le concept d'auto-configuration de [Spring Boot] ;
• lignes 10-14 : définissent un projet Maven parent. Ce dernier, [Spring Boot], définit les dépendances les plus courantes
pour les divers types de projets Spring qui peuvent exister (cf page 41 de [ref1]). Lorsqu'on utilise l'une d'elles dans le
fichier [[Link]], sa version n'a pas à être précisée. C'est la version définie dans le projet parent qui sera utilisée ;
• lignes 18-21 : définissent une dépendance sur l'artifact [spring-boot-starter-web]. Cet artifact amène avec lui toutes les
archives nécessaires à un projet Spring MVC. Parmi celles-ci on trouve l'archive d'un serveur Tomcat. C'est lui qui sera
utilisé pour déployer l'application web. On notera que la version de la dépendance n'a pas été mentionnée. C'est celle
mentionnée dans le projet parent qui sera utilisée ;
• lignes 23-26 : définissent une dépendance sur [spring-boot-starter-data-jpa]. Cet artifact amène avec lui les archives
nécessaires à l'implémentation d'une couche [DAO] s'appuyant sur une interface JPA. L'implémentation JPA utilisée par
défaut est Hibernate ;
• lignes 28-32 : définissent une dépendance sur [spring-boot-starter-test]. Cet artifact amène avec lui les archives nécessaires
à l'intégration de tests unitaires JUnit avec Spring. On notera ligne 31, que le [scope] est test, ce qui signifie que la
dépendance n'est nécessaire que pour les tests. Elle ne sera pas incluse dans l'archive produite par la compilation du
projet ;
• lignes 34-37 : cette dépendance amène les bibliothèques nécessaires pour les logs. Nous en parlerons peu ici ;
• lignes 39-42 : le pilote JDBC du SGBD MySQL ;
• lignes 44-47 : pool de connexions. Un pool de connexions est un pool de connexions ouvertes. L'ouverture / fermeture
d'une connexion à un SGBD a un coût (temps, mémoire) et le nombre de connexions ouvertes à un moment donné est
limité. Le pool de connexions tente de résoudre ces deux problèmes : un certain nombre de connexions au SGBD sont
faites dès l'instanciation du pool. Ensuite, de façon transparente, lorsqu'un code ouvre une connexion, une connexion du
pool lui est donnée (pas de temps d'ouverture) et lorsque ce même code ferme la connexion, celle-ci est rendue au pool
(elle n'est pas fermée). Ceci est fait de façon transparente pour le développeur. Le code n'a pas à être modifié ;
• lignes 49-56 : bibliothèque Jackson de gestion du JSON ;
• lignes 58-62 : bibliothèque Google de gestion des collections ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
379/489
Couche Couche Spring Couche Pilote
[web / [metier] [Repositories] [JPA] [JDBC] SGBD
jSON]
Spring
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
380/489
56. // colonnes table USERS
57. public final static String TAB_USERS_NAME = "NAME";
58. public final static String TAB_USERS_LOGIN = "LOGIN";
59. public final static String TAB_USERS_PASSWORD = "PASSWORD";
60.
61. // colonnes table ROLES
62. public final static String TAB_ROLES_NAME = "NAME";
63.
64. // colonnes table USERS_ROLES
65. public final static String TAB_USERS_ROLES_ROLE_ID = "ROLE_ID";
66. public final static String TAB_USERS_ROLES_USER_ID = "USER_ID";
67. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
381/489
une connexion du pool. Lorsque le code ferme cette connexion, elle n'est pas fermée mais simplement rendue au pool.
Cela se fait de façon transparente pour le code et améliore les performances : moins de temps passé à ouvrir / fermer les
connexions, pas de risque de laisser une connexion ouverte et inutilisée, ... ;
• ligne 44 : le pool de connexion aura de base 5 connexions ouvertes. Cette ligne est montrée pour l'exemple. Dans notre
cas, 1 connexion serait suffisante. Dans le cas où la couche [DAO] serait utilisée par plusieurs threads, cette ligne serait
nécessaire ;
• lignes 25-31 : l'implémentation JPA utilisée est une implémentation Hibernate ;
• ligne 27 : pas de logs SQL ;
• ligne 29 : pas de régénération des tables ;
• ligne 28 : le SGBD utilisé est MySQL ;
• lignes 50-58 : définissent l'EntityManagerFactory de la couche JPA. A partir de cet objet, on obtient l'objet
[EntityManager] qui permet de faire les opérations JPA ;
• ligne 54 : on indique le ou les packages où se trouvent les entités JPA ;
• ligne 55 : indique la source de données à connecter à la couche JPA ;
• lignes 61-66 : le gestionnaire de transactions associé à l'EntityManagerFactory précédent ;
Par ailleurs, la couche JPA est configurée par le fichier [[Link]] suivant :
Ce fichier n'est pas indispensable à la configuration Spring / Hibernate utilisée ici. Vous pouvez donc le laisser en l'état. Pour
Eclipselink, il est nécessaire et il faudrait décommenter les lignes 5-9.
Spring
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
382/489
Il y a autant d'entités JPA que de tables :
Entité Table
Category Categories
Customer Customers
CustomerOrder CustomerOrders
OrderedProduct OrderedProducts
Product Products
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
383/489
52. }
53.
54. // deux entités sont égales si ce sont des instances de la même classe
55. // et si elles ont la même clé primaire
56. @Override
57. public boolean equals(Object object) {
58. if (! (object instanceof AbstractCoreEntity) && ![Link]().equals([Link]())) {
59. return false;
60. }
61. AbstractCoreEntity other = (AbstractCoreEntity) object;
62. return !(([Link] == null && [Link] != null) || ([Link] != null && ));
63. }
64.
65. // signature jSON
66. @Override
67. public String toString() {
68. String signature;
69. try {
70. signature = new ObjectMapper().writeValueAsString(this);
71. } catch (JsonProcessingException ex) {
72. [Link](getClass().getName()).log([Link], null, ex);
73. signature = [Link]();
74. }
75. return signature;
76. }
77.
78. // setter des clés étrangères
79.
80. protected void checkProxy(AbstractCoreEntity foreignKey) {
81. if ([Link]() == [Link]) {
82. throw new RuntimeException("Il est impossible de fixer la clé étrangère d'un type PROXY");
83. }
84. }
85.
86. // getters et setters
87. ...
88. }
• lignes 24-27 : on pourra ignorer cette énumération dans tout l'exercice ainsi que le champ [entityType] (lignes 42-44) et la
méthode [checkProxy] (lignes 80-84) qui lui sont liés ;
• lignes 30-34 : la clé primaire de l'entité. Le mode de génération utilisé [strategy = [Link]] suppose
que les clés primaires des tables MySQL ont l'attribut [auto_increment]. C'est effectivement le cas ;
• lignes 37-40 : la version de l'entité. Dans tout cet exercice, elle n'est pas utilisée ;
• lignes 46-63 : les méthodes nécessaires pour comparer deux identités : deux identités seront dites égales si :
◦ ligne 58 : elles dérivent toutes deux de [AbstractCoreEntity] et sont des instances de la même classe ;
◦ ligne 62 : elles ont la même clé primaire ;
• lignes 66-76 : la méthode [toString] des entités rend la chaîne jSON de celle-ci ;
Question 1 : compléter le code des entités JPA. Les champs annotés par [@ManyToOne] seront cherchés en base en mode Lazy.
Les classes JPA vous sont données sans les annotations JPA. Ce sont ces dernières que vous devez créer. Respectez les noms des
champs qui vous sont donnés. Ils sont utilisés dans les tests unitaires qui suivent.
Conseils :
• on ne mettra pas les annotations [@OneToMany] ;
• on conservera les filtres jSON présents sur certaines classes comme par exemple ci-dessous dans la classe [Product] :
1. @JsonFilter("jsonFilterProduct")
2. public class Product extends AbstractCoreEntity {
Couche Pilote
Test JUnit [JPA] [JDBC] SGBD
Spring
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
384/489
La classe [JUnitTestJpa] permet de tester les entités JPA :
1. package tests;
2.
3. import [Link];
4. import [Link];
5. import [Link];
6.
7. import [Link];
8. import [Link];
9.
10. import [Link];
11. import [Link];
12. import [Link];
13. import [Link];
14. import [Link];
15. import [Link];
16. import [Link];
17. import [Link].junit4.SpringJUnit4ClassRunner;
18. import [Link];
19. import [Link];
20. import [Link];
21.
22. import [Link];
23. import [Link];
24. import [Link];
25. import [Link];
26. import [Link];
27. import [Link];
28. import [Link];
29. import [Link];
30.
31. @SpringApplicationConfiguration(classes = [Link])
32. @RunWith([Link])
33. public class JUnitTestJpa {
34.
35. // contexte de persistance Spring
36. @PersistenceContext
37. private EntityManager em;
38. // gestionnaire de transactions Spring
39. @Autowired
40. private PlatformTransactionManager transactionManager;
41. private TransactionStatus status;
42.
43. @SuppressWarnings("unchecked")
44. @Before
45. public void beforeTest() {
46. // début transaction
47. status = [Link](new DefaultTransactionDefinition());
48. // on vide les tables liées aux clients et à leurs achats
49. // la table Customer est vidée - les autres le seront par cascade
50. List<Customer> customers = [Link]("select c from Customer c").getResultList();
51. for (Customer c : customers) {
52. [Link](c);
53. }
54. }
55.
56. @After
57. public void endTest() {
58. // fin transaction
59. [Link](status);
60. }
61.
62. @Test
63. public void doNothing() {
64.
65. }
66.
67. @SuppressWarnings("unchecked")
68. @Test
69. public void placeOrder() {
70. // liste des catégories
71. List<Category> categories = [Link]("select c from Category c").getResultList();
72. [Link](4, [Link]());
73. // liste des produits catégorie 0
74. List<Product> products = em
75. .createQuery("select p from Product p where [Link]=:categoryId")
76. .setParameter("categoryId", [Link](0).getId()).getResultList();
77. // remplissage chariot
78. ShoppingCart chariot = new ShoppingCart();
79. // 2 produits 0
80. [Link]([Link](0), 2);
81. // 3 produits 1
82. [Link]([Link](1), 3);
83. // on force le calcul du prix à payer
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
385/489
84. [Link](0);
85. // persistance client
86. Customer customer = new Customer("1", "2", "3", "4", "5", "6");
87. [Link](customer);
88. // persistance ordre d'achats
89. CustomerOrder order = new CustomerOrder();
90. [Link](customer);
91. [Link]([Link]([Link]()));
92. Random random = new Random();
93. int i = [Link](999999999);
94. [Link](i);
95. [Link](order);
96. // persistance articles achetés
97. for (ShoppingCartItem scItem : [Link]()) {
98. // OrderedProduct
99. OrderedProduct orderedProduct = new OrderedProduct();
100. [Link](order);
101. [Link]([Link]());
102. Product product=[Link]();
103. [Link](product);
104. [Link]([Link]());
105. [Link]([Link]());
106. // persistance
107. [Link](orderedProduct);
108. }
109.
110. // vérifications
111. Customer customer2 = [Link]([Link], [Link]());
112. [Link]("1", [Link]());
113. [Link]("2", [Link]());
114. [Link]("3", [Link]());
115. [Link]("4", [Link]());
116. [Link]("5", [Link]());
117. [Link]("6", [Link]());
118. CustomerOrder customerOrder2 = (CustomerOrder) em
119. .createQuery("select co from CustomerOrder co where [Link]=:id")
120. .setParameter("id", [Link]().longValue()).getResultList().get(0);
121. [Link](10.57, [Link]().doubleValue(), 1e-6);
122. List<OrderedProduct> orderedProducts2 = em
123. .createQuery("select op from OrderedProduct op where [Link]=:id")
124. .setParameter("id", [Link]().longValue()).getResultList();
125. [Link]([Link](0).getName(), [Link](0).getProduct().getName());
126. [Link]([Link](1).getName(), [Link](1).getProduct().getName());
127. [Link](2, [Link](0).getQuantity());
128. [Link](3, [Link](1).getQuantity());
129. }
130. }
Nous ne commenterons que le code nouveau par rapport à ce qui a été vu en TD. Le reste est laissé à votre compréhension ;
• lignes 31-32 : intégration JUnit / Spring. C'est cette intégration qui nous permet d'injecter dans le test des beans Spring
(lignes 36-37, 39-40) ;
• ligne 31 : l'environnement Spring est configuré par la classe [ConfigJpa] que nous avons décrite (paragraphe 5.6.3, page
379) ;
• lignes 36-37 : injection de l'[EntityManager] qui gère l'accès à la couche JPA défini à partir des informations de la classe
[ConfigJpa] ;
• lignes 39-40 : injection du gestionnaire de transactions défini dans la classe [ConfigJpa] :
1. // Transaction manager
2. @Bean
3. public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
4. JpaTransactionManager txManager = new JpaTransactionManager();
5. [Link](entityManagerFactory);
6. return txManager;
7. }
Ce gestionnaire de transactions permet de faire tous nos tests dans une transaction :
• ligne 47 : la transaction démarre dans la méthode annotée [@Before] ;
• ligne 59 : elle se termine dans la méthode annotée [@After] ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
386/489
Couche Couche Spring Data Jpa Couche Pilote
[web / [metier] [Repositories] [JPA] [JDBC] SGBD
jSON]
Spring
Repository Table
CategoryRepository Categories
CustomerRepository Customers
CustomerOrderRepository CustomerOrders
OrderedProductRepository OrderedProducts
ProductRepository Products
1. package [Link];
2.
3. import [Link];
4.
5. import [Link];
6.
7. import [Link];
8.
9. public interface ProductRepository extends CrudRepository<Product, Long>{
10. // les produits d'une catégorie [Category] dont on donne la clé primaire
11. List<Product> productsByCategory(long categoryId);
12. }
Question 2 : compléter le code des deux [repositories] précédents. On s'inspirera de la classe [JUnitTestJpa].
Conseils et notes :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
387/489
• dans le résultat de la méthode [[Link]], chaque élément [OrderedProduct] doit être
rendu avec son champ [[Link]] initialisé. Il faut se rappeler que ce champ a normalement (si vous ne
l'avez pas oubliée) une annotation [@ManyToOne] avec un mode de recherche Lazy ;
• dans le résultat de la méthode [[Link]], chaque élément [Product] doit être rendu avec
son champ [[Link]] non initialisé (mode lazy loading).
Spring
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
388/489
55. public void beforeTest() {
56. // début transaction
57. status = [Link](new DefaultTransactionDefinition());
58. // on vide les tables liées aux clients et à leurs achats
59. [Link]();
60. }
61.
62. @After
63. public void endTest() {
64. // fin transaction
65. [Link](status);
66. }
67.
68. @Test
69. public void doNothing() {
70.
71. }
72.
73. @Test
74. public void placeOrder() {
75.
76. // liste des catégories
77. List<Category> categories = [Link]([Link]());
78. [Link](4, [Link]());
79. // liste des produits catégorie 0
80. List<Product> products = [Link]([Link](0).getId());
81. // remplissage chariot
82. ShoppingCart chariot = new ShoppingCart();
83. // 2 produits 0
84. [Link]([Link](0), 2);
85. // 3 produits 1
86. [Link]([Link](1), 3);
87. // on force le calcul du prix à payer
88. [Link](0);
89. // persistance client
90. Customer customer = new Customer("1", "2", "3", "4", "5", "6");
91. [Link](customer);
92. // persistance ordre d'achats
93. CustomerOrder order = new CustomerOrder();
94. [Link](customer);
95. [Link]([Link]([Link]()));
96. Random random = new Random();
97. int i = [Link](999999999);
98. [Link](i);
99. [Link](order);
100. // persistance articles achetés
101. for (ShoppingCartItem scItem : [Link]()) {
102. // OrderedProduct
103. OrderedProduct orderedProduct = new OrderedProduct();
104. [Link](order);
105. [Link]([Link]());
106. Product product=[Link]();
107. [Link](product);
108. [Link]([Link]());
109. [Link]([Link]());
110. // persistance
111. [Link](orderedProduct);
112. }
113.
114. // vérifications
115. Customer customer2 = [Link]([Link]());
116. [Link]("1", [Link]());
117. [Link]("2", [Link]());
118. [Link]("3", [Link]());
119. [Link]("4", [Link]());
120. [Link]("5", [Link]());
121. [Link]("6", [Link]());
122. CustomerOrder customerOrder2 = [Link]([Link]());
123. [Link](10.57, [Link]().doubleValue(), 1e-6);
124. List<OrderedProduct> orderedProducts2 = [Link]([Link]());
125. [Link]([Link](0).getName(), [Link](0).getProduct().getName());
126. [Link]([Link](1).getName(), [Link](1).getProduct().getName());
127. [Link](2, [Link](0).getQuantity());
128. [Link](3, [Link](1).getQuantity());
129. }
130. }
• ligne 35 : le test JUnit / Spring est configuré par la classe [MetierDaoConfig] suivante :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
389/489
6. import [Link];
7.
8. @EnableJpaRepositories(basePackages = { "[Link]" })
9. @Configuration
10. @ComponentScan(basePackages = { "[Link]" })
11. @Import({ [Link] })
12. public class MetierDaoConfig {
13.
14. }
• ligne 11 : on importe les beans de la classe [ConfigJpa] utilisée dans le test précédent ;
• ligne 8 : on indique le dossier où se trouvent les [repositories] ;
• ligne 10 : cette configuration n'est pas utilisée dans ce test. Elle le sera ultérieurement ;
Spring
La classe [Metier] qui implémente cette interface est pour l'instant la suivante :
1. package [Link];
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
390/489
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
12.
13. @Transactional
14. @Component
15. public class Metier implements IMetier {
16.
17. private static final long serialVersionUID = 1L;
18.
19. @Override
20. public List<OrderedProduct> getOrderDetails(long orderId) {
21. throw new UnsupportedOperationException("Not supported yet.");
22. }
23.
24. @Override
25. public CustomerOrder placeOrder(Customer customer, ShoppingCart cart) {
26. throw new UnsupportedOperationException("Not supported yet.");
27. }
28.
29. @Override
30. public List<Category> getCategories() {
31. throw new UnsupportedOperationException("Not supported yet.");
32. }
33.
34. @Override
35. public List<Product> getProductsByCategory(long categoryId) {
36. throw new UnsupportedOperationException("Not supported yet.");
37. }
38.
39. @Override
40. public void resetDatabase() {
41. throw new UnsupportedOperationException("Not supported yet.");
42. }
43. }
Spring
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
391/489
19. import [Link];
20. import [Link];
21. import [Link];
22.
23. @SpringApplicationConfiguration(classes = [Link])
24. @RunWith([Link])
25. public class JUnitTestMetier {
26.
27. @Autowired
28. private IMetier metier;
29.
30. @Before
31. public void clean() {
32. // on vide les tables liées aux clients et à leurs achats
33. [Link]();
34. }
35.
36. @Test
37. public void placeOrder() {
38. // liste des catégories
39. List<Category> categories = [Link]();
40. [Link](4, [Link]());
41. // liste des produits catégorie 0
42. List<Product> products = [Link]([Link](0).getId());
43. // remplissage chariot
44. ShoppingCart chariot = new ShoppingCart();
45. // 2 produits 0
46. [Link]([Link](0), 2);
47. // 3 produits 1
48. [Link]([Link](1), 3);
49. // on force le calcul du prix à payer
50. [Link](0);
51. // enregistrement commande
52. CustomerOrder order = [Link](new Customer("1", "2", "3", "4", "5", "6"), chariot);
53.
54. // vérifications détails de la commande
55. Customer customer = [Link]();
56. List<OrderedProduct> orderedProducts = [Link]([Link]());
57. List<Product> produits = [Link]().map(OrderedProduct::getProduct).collect([Link]());
58. [Link]("1", [Link]());
59. [Link]("2", [Link]());
60. [Link]("3", [Link]());
61. [Link]("4", [Link]());
62. [Link]("5", [Link]());
63. [Link]("6", [Link]());
64. [Link](10.57, [Link]().doubleValue(), 1e-6);
65. [Link]([Link](0).getName(), [Link](0).getName());
66. [Link]([Link](1).getName(), [Link](1).getName());
67. [Link](2, [Link](0).getQuantity());
68. [Link](3, [Link](1).getQuantity());
69. }
70. }
Spring
[Link] Configuration
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
392/489
3.
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
12. import [Link];
13. import [Link];
14. import [Link];
15.
16. import [Link];
17. import [Link];
18. import [Link];
19.
20. @Configuration
21. @EnableWebMvc
22. public class WebConfig extends WebMvcConfigurerAdapter {
23.
24. // -------------------------------- configuration couche [web]
25. @Autowired
26. private ApplicationContext context;
27.
28. @Bean
29. public DispatcherServlet dispatcherServlet() {
30. DispatcherServlet servlet = new DispatcherServlet((WebApplicationContext) context);
31. return servlet;
32. }
33.
34. @Bean
35. public ServletRegistrationBean servletRegistrationBean(DispatcherServlet dispatcherServlet) {
36. return new ServletRegistrationBean(dispatcherServlet, "/*");
37. }
38.
39. @Bean
40. public EmbeddedServletContainerFactory embeddedServletContainerFactory() {
41. return new TomcatEmbeddedServletContainerFactory("", 8080);
42. }
43.
44. // -------------------------------- configuration filtres [json]
45.
46. @Bean
47. ObjectMapper jsonMapperShortProduct() {
48. ObjectMapper jsonMapper = new ObjectMapper();
49. [Link](new SimpleFilterProvider().addFilter("jsonFilterProduct",
50. [Link]("category")));
51. return jsonMapper;
52. }
53.
54. @Bean
55. ObjectMapper jsonMapperShortOrderedProduct() {
56. ObjectMapper jsonMapper = new ObjectMapper();
57. [Link](new SimpleFilterProvider().addFilter("jsonFilterOrderedProduct",
58. [Link]("customerOrder")).addFilter("jsonFilterProduct",
59. [Link]("category")));
60. return jsonMapper;
61. }
62.
63. @Bean
64. ObjectMapper jsonMapperCustomerOrder() {
65. ObjectMapper jsonMapper = new ObjectMapper();
66. [Link](new SimpleFilterProvider().addFilter("jsonFilterCustomerOrder",
67. [Link]("")).addFilter("jsonFilterCustomer",
68. [Link]("")).addFilter("jsonFilterOrderedProduct",
69. [Link]("customerOrder")).addFilter("jsonFilterProduct",
70. [Link]("category")));
71. return jsonMapper;
72. }
73.
74. @Bean
75. ObjectMapper jsonMapperPostPlaceOrder() {
76. ObjectMapper jsonMapper = new ObjectMapper();
77. [Link](new SimpleFilterProvider().addFilter("jsonFilterCustomer",
78. [Link]("id", "version")).addFilter("jsonFilterProduct",
79. [Link]("id", "version")).addFilter("jsonFilterShoppingCart",
80. [Link]("items", "total")));
81. return jsonMapper;
82. }
83. }
Ces mappeurs jSON servent à configurer les filtres jSON posés sur certaines entités JPA et métier :
1. @JsonFilter("jsonFilterCustomer")
2. public class Customer extends AbstractCoreEntity {
3.
4. @JsonFilter("jsonFilterCustomerOrder")
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
393/489
5. public class CustomerOrder extends AbstractCoreEntity {
6.
7. @JsonFilter("jsonFilterOrderedProduct")
8. public class OrderedProduct extends AbstractCoreEntity {
9.
10. @JsonFilter("jsonFilterProduct")
11. public class Product extends AbstractCoreEntity {
12.
13. @JsonFilter("jsonFilterShoppingCart")
14. public class ShoppingCart implements Serializable {
La classe [Boot] est la classe exécutable du projet. Elle déploie le serveur web / jSON sur le serveur Tomcat défini dans la classe
[WebConfig] :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5.
6. public class Boot {
7.
8. public static void main(String[] args) {
9. [Link]([Link], args);
10. }
11. }
• ligne 10 : l'application Spring lancée est configurée par la classe [AppConfig]. Celle-ci est la suivante :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6.
7. @Configuration
8. @ComponentScan(basePackages = {"[Link]"})
9. @Import({[Link], [Link]})
10. public class AppConfig {
11.
12. }
• la première chose à voir est que la classe [AppConfig] importe les beans définis dans les classes [MetierDaoConfig] et
[WebConfig] (ligne 9), ç-à-d tous les beans qu'on a pu rencontrer depuis le début de cet exercice ;
• à ces beans, elle rajoute ceux qui se trouvent dans le dossier [[Link]]. Il s'agit du dossier où se trouve le contrôleur
Spring [AfbController] ;
[Link] Le contrôleur
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
394/489
La couche [web / jSON] présente l'interface [IAfbController] suivante :
1. package [Link];
2.
3. import [Link];
4.
5.
6. public interface IAfbController {
7.
8. // ---------------------------- Métier
9. // détails d'une commande dont on donne la clé primaire
10. Response<String> getOrderDetails(long orderId);
11.
12. // faire une commande
13. Response<String> placeOrder(HttpServletRequest servletRequest);
14.
15. // liste des catégories de produits
16. Response<String> getCategories();
17.
18. // liste des produits d'une catégorie dont on donne la clé primaire
19. Response<String> getProductsByCategory(long categoryId);
20.
21. // reset de la base
22. Response<Void> resetDatabase();
23.
24. }
La partie intéressante du serveur web / jSON est le champ [body]. Mais le serveur peut rencontrer une exception lors du traitement
d'une requête client. L'objet [Response] permet d'encapsuler et la réponse [body] et un éventuel message d'erreur [exception]. Le
champ [status] 1, 2, ... permet d'indiquer le type de l'erreur.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
395/489
1. package [Link];
2.
3. import [Link];
4. import [Link];
5.
6. import [Link];
7.
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
12. import [Link];
13. import [Link];
14. import [Link];
15.
16. import [Link];
17. import [Link];
18. import [Link];
19. import [Link];
20.
21. import [Link];
22. import [Link];
23. import [Link];
24.
25. @Controller
26. public class AfbController {
27.
28. // couche métier
29. @Autowired
30. private IMetier metier;
31.
32. // mappeurs jSON
33. @Autowired
34. private ObjectMapper jsonMapperShortOrderedProduct;
35. @Autowired
36. private ObjectMapper jsonMapperCustomerOrder;
37. @Autowired
38. private ObjectMapper jsonMapperPostPlaceOrder;
39. @Autowired
40. private ObjectMapper jsonMapperShortProduct;
41.
42. // local
43. private final String simpleClassName = getClass().getSimpleName();
44.
45. @RequestMapping(value = "/getOrderDetails/{orderId}", method = [Link])
46. @ResponseBody
47. public Response<String> getOrderDetails(@PathVariable("orderId") long orderId) {
48. throw new RuntimeException("méthode à implémenter");
49. }
50.
51. @RequestMapping(value = "/placeOrder", method = [Link])
52. @ResponseBody
53. public Response<String> placeOrder(HttpServletRequest request) {
54. ...
55. }
56.
57. @RequestMapping(value = "/getCategories", method = [Link])
58. @ResponseBody
59. public Response<List<Category>> getCategories() {
60. throw new RuntimeException("méthode à implémenter");
61. }
62.
63. @RequestMapping(value = "/getProductsByCategory/{categoryId}", method = [Link])
64. @ResponseBody
65. public Response<String> getProductsByCategory(@PathVariable("categoryId") long categoryId) {
66. throw new RuntimeException("méthode à implémenter");
67. }
68.
69. @RequestMapping(value = "/resetDatabase", method = [Link])
70. @ResponseBody
71. public Response<Void> resetDatabase() {
72. try {
73. [Link]();
74. return new Response<>(0, null, null);
75. } catch (UncheckedException e1) {
76. return new Response<>(1, [Link](), null);
77. } catch (Exception e2) {
78. return new Response<>(2, new UncheckedException(1004, e2, simpleClassName).toString(), null);
79. }
80. }
81.
82. }
Nous allons étudier la méthode [placeOrder] des lignes 51-55. Vous aurez à écrire les autres méthodes.
1. @RequestMapping(value = "/placeOrder", method = [Link])
2. @ResponseBody
3. public Response<String> placeOrder(HttpServletRequest request) {
4. // test :
5. // {"customer":{"name":"1","email":"2","phone":"3","address":"4","cityRegion":"5","ccNumber":"6"},"cart":
{"items":[{"product":{"id":1,"version":1},"quantity":2},{"product":{"id":2,"version":1},"quantity":3}],"total":10.57}}
6. try {
7. // on récupère la valeur postée
8. String body = [Link]([Link]());
9. // on la désérialise
10. PostPlaceOrder placeOrder = [Link](body, new TypeReference<PostPlaceOrder>() {
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
396/489
11. });
12. // on enregistre la commande
13. CustomerOrder customerOrder = [Link]([Link](), [Link]());
14. // filtre jSON de la réponse
15. return new Response<>(0, null, [Link](customerOrder));
16. } catch (UncheckedException e1) {
17. return new Response<>(1, [Link](), null);
18. } catch (IOException | BeansException e2) {
19. return new Response<>(2, new UncheckedException(1001, e2, simpleClassName).toString(), null);
20. }
21. }
• ligne 1 : la méthode traite l'URL [/placeOrder] et celle-ci doit être demandée avec une commande HTTP POST ;
• ligne 2 : la méthode délivre elle-même la réponse au client, ici un type [Response<String>]. Cette réponse est
automatiquement sérialisée en jSON avant d'être envoyée au client. Elle est sérialisée par un mappeur jSON créé par
défaut par Spring. Ce mappeur est un mappeur sans filtres. Cela pose un problème pour les entités manipulées par
l'application. Ainsi par exemple, la classe [OrderedProduct] a un filtre jSON :
@JsonFilter("jsonFilterOrderedProduct")
public class OrderedProduct extends AbstractCoreEntity {
• ligne 5 : on a mis un exemple de la chaîne jSON attendue dans le POST. Il s'agit de la chaîne jSON d'un ordre de
commande [CustomerOrder] où on n'a gardé que les champs strictement nécessaires. Ainsi le champ [items] qui décrit les
achats du client contient normalement une liste d'éléments de type [ShoppingCartItem] où chacun des éléments est un
type [Product product, int quantity]. Ici, on a décidé de ne pas poster la totalité du produit (id, version, nom, ...) mais
seulement ses champs [id,version]. Pour désérialiser la chaîne postée, on a alors besoin de dire quels champs sont dans la
valeur jSON postée et quels champs n'y sont pas. C'est grâce aux filtres jSON qu'on y arrive ;
• ligne 10 : la valeur postée est désérialisée avec le mappeur jSON [ jsonMapperPostPlaceOrder]. La désérialisation jSON
vise à construire l'objet [PlaceOrder] suivant :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6.
7. @JsonFilter("jsonFilterPostPlaceOrder")
8. public class PostPlaceOrder {
9.
10. // le client
11. private Customer customer;
12. // son panier d'achats
13. private ShoppingCart cart;
14.
15. // getters et setters
16. ...
17. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
397/489
La chaîne à désérialiser est la suivante :
{"customer":{"name":"1","email":"2","phone":"3","address":"4","cityRegion":"5","ccNumber":"6"},"cart":{"items":[{"product":
{"id":1,"version":1},"quantity":2},{"product":{"id":2,"version":1},"quantity":3}],"total":10.57}}
• ligne 13 : une fois l'objet [PlaceOrder] obtenu par la désérialisation de l'objet posté, il est persisté par la couche [métier].
Celle-ci rend un objet [CustomerOrder] :
1. @JsonFilter("jsonFilterCustomerOrder")
2. public class CustomerOrder extends AbstractCoreEntity {
3.
4. private static final long serialVersionUID = 1L;
5.
6. private BigDecimal amount;
7.
8. private Date dateCreated;
9.
10. private int confirmationNumber;
11.
12. private Long customerId;
13.
14. private Customer customer;
• ligne 1 : la classe a un filtre jSON. On ne peut donc renvoyer un type [Response<CustomerOrder>] comme il aurait été
souhaitable. En effet, le mappeur jSON utilisé par Spring a une configuration sans filtres. Aussi, lorsqu'à la sérialisation il
rencontre le filtre [jsonFilterCustomerOrder], il lance une exception. On va remplacer le type [CustomerOrder] par le type
[String] de sa sérialisation en jSON. Le type du résultat devient donc [Response<String>] (ligne 3) ;
• ligne 15 : la valeur jSON du type [CustomerOrder] est produite par le mappeur jSON [jsonMapperCustomerOrder]
suivant :
1. @Bean
2. ObjectMapper jsonMapperCustomerOrder() {
3. ObjectMapper jsonMapper = new ObjectMapper();
4. [Link](new SimpleFilterProvider().addFilter("jsonFilterCustomerOrder",
5. [Link]("")).addFilter("jsonFilterCustomer",
6. [Link]("")));
7. return jsonMapper;
8. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
398/489
◦ ligne 5 : on configure le filtre [jsonFilterCustomer] qui filtre le type [Customer] ;
◦ ligne 6 : on sérialise tous ses champs ;
Question 4 : écrire la méthode [getCategories] qui rend la liste de toutes les catégories.
Pour cette raison, on peut rendre un type [Response<List<Category>>]. Le mappeur jSON utilisé par Spring pour sérialiser la
réponse ne rencontrera pas de filtres jSON.
Pour lancer votre serveur web / jSON, exécutez la classe [Boot] du projet :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
399/489
On obtient alors le résultat suivant :
Question 5 : écrire la méthode [getProductsByCategory] qui rend les produits de la catégorie dont on passe la clé primaire.
aussi rend-on un type [Response<String>] où [String] est le type de la valeur jSON d'un type [List<Product>]. On choisira le
mappeur jSON parmi ceux proposés. Vous pouvez également créer le vôtre.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
400/489
On obtient alors le résultat suivant :
Le champ [body] est de type [String] (le 1er caractère est un double guillemets, ce qui n'était pas le cas dans l'exemple précédent).
Cette chaîne de caractères est la chaîne jSON d'un type [List<Product>].
Question 6 : écrire la méthode [getOrderDetails] qui rend la chaîne jSON d'un type [CustomerOrder] dont on passe la clé
primaire.
aussi rend-on un type [Response<String>] où [String] est le type de la valeur jSON d'un type [List<OrderedProduct>]. On choisira
le mappeur jSON parmi ceux proposés. Vous pouvez également créer le vôtre.
Pour tester l'URL, choisissez une commande dans la base de données et notez sa clé primaire :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
401/489
On obtient alors le résultat suivant :
Le champ [body] est de type [String] (le 1er caractère est un double guillemets, ce qui n'était pas le cas dans le 1er exemple). Cette
chaîne de caractères est la chaîne jSON d'un type [List<OrderedProduct>].
Spring
Le serveur web / jSON peut être testé avec un client distant. Celui-ci est implémenté par le projet Netbeans suivant :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
402/489
On ne cherchera pas à expliquer le code de ce client. Il a été écrit pour communiquer avec le serveur web / jSON. Le test
[JunitTestMetier] utilisé côté serveur (paragraphe 5.6.8, page 391) a été porté côté client :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6.
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
12. import [Link];
13. import [Link].junit4.SpringJUnit4ClassRunner;
14.
15. import [Link];
16. import [Link];
17. import [Link];
18. import [Link];
19. import [Link];
20. import [Link];
21. import [Link];
22. import [Link];
23.
24. @SpringApplicationConfiguration(classes = [Link])
25. @RunWith([Link])
26. public class JUnitTestMetier {
27.
28. @Autowired
29. private IMetier metier;
30.
31. public JUnitTestMetier() {
32. }
33.
34. @Before
35. public void clean() {
36. // on vide les tables liées aux clients et à leurs achats
37. [Link]();
38. }
39.
40. @Test
41. public void doNothing() {
42.
43. }
44.
45. @Test
46. public void placeOrder() throws IOException {
47. // liste des catégories
48. List<Category> categories = [Link]();
49. [Link](4, [Link]());
50. //liste des produits catégorie 0
51. List<Product> products = [Link]([Link](0).getId());
52. // remplissage chariot
53. ShoppingCart chariot = new ShoppingCart();
54. // 2 produits 0
55. [Link]([Link](0), 2);
56. // 3 produits 1
57. [Link]([Link](1), 3);
58. // on force le calcul du prix à payer
59. [Link](0);
60. // enregistrement commande
61. CustomerOrder order = [Link](new Customer("1", "2", "3", "4", "5", "6"), chariot);
62.
63. // vérifications détails de la commande
64. Customer customer = [Link]();
65. List<OrderedProduct> orderedProducts = [Link]([Link]());
66. List<Product> produits = new ArrayList<>();
67. for (OrderedProduct op : orderedProducts) {
68. [Link]([Link]());
69. }
70. [Link]("1", [Link]());
71. [Link]("2", [Link]());
72. [Link]("3", [Link]());
73. [Link]("4", [Link]());
74. [Link]("5", [Link]());
75. [Link]("6", [Link]());
76. [Link](10.57, [Link]().doubleValue(), 1e-6);
77. [Link]([Link](0).getName(), [Link](0).getName());
78. [Link]([Link](1).getName(), [Link](1).getName());
79. [Link](2, [Link](0).getQuantity());
80. [Link](3, [Link](1).getQuantity());
81. }
82. }
étape : lancez le serveur web / jSON et vérifiez que le test ci-dessus du client passe.
5.7 Conclusion
Vous avez là un exemple typique d'application client / serveur en jSON réalisée à l'aide de l'écosystème Spring. Vous pouvez vous
en inspirer pour des applications bâties sur ce principe.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
403/489
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
404/489
6 Étude de cas n° 6 : gestion de bilans de formation
6.2 Le problème
Nous allons étudier une application de commerce électronique dans un environnement JSF2 / Spring / Hibernate / Tomcat :
7 Spring / Tomcat
Cette application est utilisée sur internet comme tutoriel JEE. On a gardé les codes quoique ceux-ci soient parfois étonnants. On se
place du point de vue du développeur qui doit maintenir une application qu'il n'a pas écrite lui-même.
La difficulté principale de l'application réside dans son ergonomie. Celle-ci est contrôlée par une feuille de style imposante qui ne
sera pas du tout abordée ici. On se souviendra seulement que l'ergonomie est un aspect très important d'une application web qui
demande des compétences réelles dans ce domaine. On se souciera ici seulement de code et de son architecture.
Nous allons diviser cette étude en deux. Nous allons d'abord présenter les couches basses de l'application, celles qui ne nécessitent
pas la présence du serveur Tomcat :
Spring
puis nous étudierons la couche haute, la couche web implémentée avec le framework JSF2 :
Couche
[web / METIER - DAO - JPA - JDBC BD
JSF]
Spring / Tomcat
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
405/489
Le code de l'ensemble de l'application est assez complexe. Nous nous situerons dans la situation où le code a été écrit et où il faut
lui ajouter des fonctionnalités. C'est ce qu'on appelle la maintenance applicative, une fonction courante dans les ENS. Il faut
comprendre le code existant et lui ajouter des fonctionnalités ou le corriger.
• on y vend des produits enregistrés dans la table [product]. Un produit appartient à une catégorie enregistrée dans la table
[category]. Le lien entre les deux tables se fait via la clé étrangère [product].category_id ;
• les trois autres tables [customer, customer_order, ordered_product] sont au départ vides ;
• un client qui fait un achat le fait via une page web de confirmation où il entre des informations le concernant. Celles-ci
seront enregistrées dans la table [customer]. Sa commande est enregistrée dans la table [customer_order]. Celle-ci a un lien
vers la table [customer] via la clé étrangère [customer_order].customer_id. Chaque produit acheté fait l'objet d'un
enregistrement dans la table [ordered_product]. La clé étrangère [ordered_product].product_id fait un lien vers le
produit acheté, la clé étrangère [ordered_product].customer_order_id faisant elle un lien vers la table [customer_order]
et donc vers la table [customer].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
406/489
1. CREATE TABLE `categories` (
2. `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
3. `versioning` BIGINT(20) DEFAULT NULL,
4. `name` VARCHAR(45) COLLATE utf8_unicode_ci NOT NULL DEFAULT '1',
5. PRIMARY KEY (`id`) USING BTREE,
6. UNIQUE KEY `name` (`name`) USING BTREE
7. ) ENGINE=InnoDB
8. AUTO_INCREMENT=5 CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci'
9. COMMENT='contains product categories, e.g., dairy, meats, etc.'
10. ;
11.
12. CREATE TABLE `customers` (
13. `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
14. `versioning` BIGINT(20) DEFAULT 1,
15. `name` VARCHAR(45) COLLATE utf8_unicode_ci NOT NULL,
16. `email` VARCHAR(45) COLLATE utf8_unicode_ci NOT NULL,
17. `phone` VARCHAR(45) COLLATE utf8_unicode_ci NOT NULL,
18. `address` VARCHAR(45) COLLATE utf8_unicode_ci NOT NULL,
19. `city_region` VARCHAR(2) COLLATE utf8_unicode_ci DEFAULT NULL,
20. `cc_number` VARCHAR(19) COLLATE utf8_unicode_ci NOT NULL,
21. PRIMARY KEY (`id`) USING BTREE,
22. UNIQUE KEY `name` (`name`) USING BTREE,
23. UNIQUE KEY `cc_number` (`cc_number`) USING BTREE
24. ) ENGINE=InnoDB
25. AUTO_INCREMENT=249 CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci'
26. COMMENT='maintains customer details'
27. ;
28.
29. CREATE TABLE `customers_orders` (
30. `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
31. `versioning` BIGINT(20) DEFAULT 1,
32. `amount` DECIMAL(6,2) NOT NULL,
33. `date_created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
34. `confirmation_number` INTEGER(10) UNSIGNED NOT NULL,
35. `customer_id` BIGINT(20) UNSIGNED NOT NULL,
36. PRIMARY KEY (`id`) USING BTREE,
37. KEY `fk_customer_order_customer` (`customer_id`) USING BTREE,
38. CONSTRAINT `customers_orders_fk1` FOREIGN KEY (`customer_id`) REFERENCES `customers` (`id`) ON DELETE CASCADE ON UPDATE
CASCADE
39. ) ENGINE=InnoDB
40. AUTO_INCREMENT=19 CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci'
41. COMMENT='maintains customer order details'
42. ;
43.
44. CREATE TABLE `products` (
45. `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
46. `versioning` BIGINT(20) DEFAULT 1,
47. `name` VARCHAR(45) COLLATE utf8_unicode_ci NOT NULL,
48. `price` DECIMAL(5,2) NOT NULL,
49. `description` TINYTEXT COLLATE utf8_unicode_ci,
50. `last_update` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
51. `category_id` BIGINT(20) UNSIGNED NOT NULL,
52. PRIMARY KEY (`id`) USING BTREE,
53. UNIQUE KEY `name` (`name`) USING BTREE,
54. KEY `fk_product_category` (`category_id`) USING BTREE,
55. CONSTRAINT `products_fk1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
56. ) ENGINE=InnoDB
57. AUTO_INCREMENT=17 CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci'
58. COMMENT='contains product details'
59. ;
60.
61. CREATE TABLE `ordered_products` (
62. `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
63. `versioning` BIGINT(20) DEFAULT 1,
64. `customer_order_id` BIGINT(20) UNSIGNED NOT NULL,
65. `product_id` BIGINT(20) UNSIGNED NOT NULL,
66. `quantity` SMALLINT(5) UNSIGNED NOT NULL DEFAULT 1,
67. PRIMARY KEY (`id`) USING BTREE,
68. UNIQUE KEY `customer_order_id` (`customer_order_id`, `product_id`) USING BTREE,
69. KEY `fk_ordered_product_customer_order` (`customer_order_id`) USING BTREE,
70. KEY `fk_ordered_product_product` (`product_id`) USING BTREE,
71. CONSTRAINT `ordered_products_fk1` FOREIGN KEY (`customer_order_id`) REFERENCES `customers_orders` (`id`) ON DELETE
CASCADE ON UPDATE CASCADE,
72. CONSTRAINT `ordered_products_fk2` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE
CASCADE
73. ) ENGINE=InnoDB
74. AUTO_INCREMENT=37 CHARACTER SET 'utf8' COLLATE 'utf8_unicode_ci'
75. COMMENT='matches products with customer orders and records their quantity'
76. ;
• on notera que toutes les clés primaires sont en mode [AUTO_INCREMENT], ç-à-d générées par le SGBD ;
• on notera lignes 38, 55, 71, 72 les attributs [ON DELETE CASCADE] et [ON UPDATE CASCADE] sur les clés
étrangères ;
• ligne 38, l'attibut [ON DELETE CASCADE] sur la clé étrangère [CustomerOrders.customer_id --> [Link]] fait
que lorsqu'on supprime un client [Customer], tous les ordres [CustomerOrder] qui dépendent de lui sont également
supprimés ;
• ligne 71, l'attibut [ON DELETE CASCADE] sur la clé étrangère [OrderedProducts.customer_order_id -->
[Link]] fait que lorsqu'on supprime une commande [CustomerOrder], tous les produits [OrderedProduct] de
cette commande sont également supprimés ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
407/489
• au final, les deux règles précédentes font que lorsqu'on supprime un client [Customer], tous les ordres [CustomerOrder] et
les produits [OrderedProduct] qui dépendent de lui sont également supprimés. C'est important à comprendre car le code
qui suit utilise cette particularité ;
[category]
[product]
[customer] [customer_order]
[ordered_product]
Spring
Les entités JPA correspondent aux lignes des tables de la base de données.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
408/489
La classe [AbstractCoreEntity] est la classe parente de toutes les entités JPA. Elle sert à définir la clé primaire [id] et le champ de
version [version] de chacune d'elles :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
12. import [Link];
13. import [Link];
14. import [Link];
15. import [Link];
16. import [Link];
17. import [Link];
18.
19. @MappedSuperclass
20. public class AbstractCoreEntity implements Serializable {
21.
22. private static final long serialVersionUID = 1L;
23.
24. public enum EntityType {
25.
26. PROXY, POJO
27. }
28.
29. // clé primaire
30. @Id
31. @GeneratedValue(strategy = [Link])
32. @Basic(optional = false)
33. @Column(name = ConfigJdbc.TAB_JPA_ID)
34. protected Long id;
35.
36. // versionning
37. @Basic(optional = false)
38. @Column(name = ConfigJdbc.TAB_JPA_VERSIONING)
39. @Version
40. protected Long version;
41.
42. @Transient
43. protected EntityType entityType = [Link];
44.
45. // deux entités ayant le même id doivent avoir le même hashcode
46. @Override
47. public int hashCode() {
48. int hash = 0;
49. hash += (id != null ? [Link]() : 0);
50. return hash;
51. }
52.
53. // deux entités sont égales si ce sont des instances de la même classe
54. // et si elles ont la même clé primaire
55. @Override
56. public boolean equals(Object object) {
57. if (! (object instanceof AbstractCoreEntity) && ![Link]().equals([Link]())) {
58. return false;
59. }
60. AbstractCoreEntity other = (AbstractCoreEntity) object;
61. return !(([Link] == null && [Link] != null) || ([Link] != null && ));
62. }
63.
64. // signature jSON
65. @Override
66. public String toString() {
67. String signature;
68. try {
69. signature = new ObjectMapper().writeValueAsString(this);
70. } catch (JsonProcessingException ex) {
71. [Link](getClass().getName()).log([Link], null, ex);
72. signature = [Link]();
73. }
74. return signature;
75. }
76.
77. // getters et setters
78. ...
79. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
409/489
• lignes 30-34 : la clé primaire des entités ;
• lignes 37-40 : la version des entités ;
• lignes 42-43 : un champ qu'on peut oublier dans tout cet exercice ;
• lignes 45-62 : les méthodes [equals] et [hashCode] sont redéfinies pour dire que deux entités sont égales si elles ont même
clé primaire ;
• lignes 65-75 : la méthode [toString] rend la version jSON des entités ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
410/489
40. public Customer() {
41. }
42.
43. public Customer(String name, String email, String phone, String address, String cityRegion, String ccNumber) {
44. [Link] = name;
45. [Link] = email;
46. [Link] = phone;
47. [Link] = address;
48. [Link] = cityRegion;
49. [Link] = ccNumber;
50. }
51.
52. // getters et setters
53. ...
54. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
411/489
génère un nombre entier aléatoire entre 1 et N.
• lignes 37-39 : le client à l'origine de la commande – ce champ n'est pas toujours initialisé (lazy loading) ;
• lignes 41-42 : la liste des produits achetés par cette commande - ce champ n'est pas toujours initialisé (lazy loading) ;
• lignes 22-24 : le produit acheté - ce champ n'est pas toujours initialisé (lazy loading) ;
• lignes 16-18 : la quantité achetée ;
• lignes 26-28 : la commande qui a acheté ce produit - ce champ n'est pas toujours initialisé (lazy loading) ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
412/489
42. @ManyToOne(optional = false, fetch = [Link])
43. private Category category;
44.
45. @OneToMany(mappedBy = "product", fetch = [Link])
46. private List<OrderedProduct> orderedProducts;
47.
48. // constructeurs
49. public Product() {
50. }
51.
52. public Product(String name, String description, BigDecimal price, Date lastUpdate) {
53. [Link] = name;
54. [Link]=description;
55. [Link] = price;
56. [Link] = lastUpdate;
57. }
58.
59. // getters et setters
60. …
61. }
7 Spring
Nous n'allons pas décrire la couche [DAO]. Elle a été implémentée avec le framework [Spring Data Jpa]. Nous ne décrirons que
l'interface de la couche [métier] car c'est avec elle que travaille la couche web à écrire. On décrit également les objets manipulés par
cette couche métier autres que les entités JPA déjà décrites.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
413/489
23. items = new ArrayList<>();
24. numberOfItems = 0;
25. total = 0;
26. }
27.
28. // ajoute un produit au chariot
29. // si le produit est déjà dans le chariot, sa qté est augmentée de 1
30. // sinon le produit est ajouté avec une qté=1
31. public void addItem(Product product) {
32.
33. boolean newItem = true;
34.
35. for (ShoppingCartItem scItem : items) {
36.
37. if ([Link]().getId().longValue() == [Link]()) {
38.
39. newItem = false;
40. [Link]();
41. }
42. }
43.
44. if (newItem) {
45. ShoppingCartItem scItem = new ShoppingCartItem(product);
46. [Link](scItem);
47. }
48. }
49.
50. // met à jour la qté achetée d'un produit
51. // si quantity=0, le produit est enlevé du panier
52. // si le produit n'existe pas, il est ajouté
53. public void update(Product product, String quantity) {
54.
55. short qty;
56.
57. // cast quantity as short
58. qty = [Link](quantity);
59.
60. if (qty >= 0) {
61.
62. ShoppingCartItem item = null;
63.
64. for (ShoppingCartItem scItem : items) {
65.
66. if ([Link]().getId().longValue() == [Link]()) {
67. item = scItem;
68. break;
69. }
70. }
71.
72. if (item == null) {
73. // add to cart
74. ShoppingCartItem newItem = new ShoppingCartItem(product);
75. [Link](qty);
76. [Link](newItem);
77. } else {
78. if (qty == 0) {
79. [Link](item);
80. } else {
81. [Link](qty);
82. }
83. }
84. }
85. }
86.
87. // ajout / modification d'un article de chariot
88. public void update(ShoppingCartItem cartItem) {
89. update([Link](), [Link]([Link]()));
90. }
91.
92. // getters et setters
93. public List<ShoppingCartItem> getItems() {
94.
95. return items;
96. }
97.
98. // rend le nombre total d'articles du chariot
99. public int getNumberOfItems() {
100.
101. numberOfItems = 0;
102.
103. for (ShoppingCartItem scItem : items) {
104.
105. numberOfItems += [Link]();
106. }
107.
108. return numberOfItems;
109. }
110.
111. // rend le prix de tous les articles du chariot
112. public double getSubtotal() {
113.
114. double amount = 0;
115.
116. for (ShoppingCartItem scItem : items) {
117.
118. Product product = (Product) [Link]();
119. amount += ([Link]() * [Link]().doubleValue());
120. }
121.
122. return amount;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
414/489
123. }
124.
125. // calcule le prix total du chariot = prix des produits + taxe (surcharge)
126. public void calculateTotal(String surcharge) {
127. [Link] = [Link](surcharge);
128. total = getSubtotal() + [Link];
129. }
130.
131. // rend le prix total du chariot
132. public double getTotal() {
133.
134. return total;
135. }
136.
137. // vide le chariot
138. public void clear() {
139. [Link]();
140. numberOfItems = 0;
141. total = 0;
142. }
143.
144. // setters
145. public void setItems(List<ShoppingCartItem> items) {
146. [Link] = items;
147. }
148.
149. public void setNumberOfItems(int numberOfItems) {
150. [Link] = numberOfItems;
151. }
152.
153. public void setTotal(double total) {
154. [Link] = total;
155. }
156.
157. public Double getSurcharge() {
158. return surcharge;
159. }
160. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
415/489
23. // liste des produits d'une catégorie
24. List<Product> getProductsByCategory(long categoryId);
25.
26. // reset de la base
27. void resetDatabase();
28. }
• ligne 27 : la méthode [resetDatabase] vide les tables [Customers, Customers_Orders, Ordered_Products] de la base de
données ;
Avec WampServer / PhpMyAdmin, exécutez le script SQL trouvé dans le dossier [database]. Le script crée la base et la remplit
avec des données. La base ne doit pas déjà exister.
Note : vous aurez probablement à changer le chemin du dossier Maven dans le script [[Link]].
Test
JUnit METIER - DAO - JPA - JDBC BD
Spring
7
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
416/489
Chargez le projet Maven du dossier [afb-spring-dao-metier-tests] dans Netbeans :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
417/489
66. // on force le calcul du prix à payer avec une taxe de livraison nulle
67. [Link]("0");
68. // enregistrement commande
69. CustomerOrder order = [Link](new Customer("1", "2", "3", "4", "5", "6"), chariot);
70.
71. // vérifications détails de la commande
72. Customer customer = [Link]();
73. List<OrderedProduct> orderedProducts = [Link]();
74. List<Product> produits = [Link]().map(OrderedProduct::getProduct).collect([Link]());
75. [Link]("1", [Link]());
76. [Link]("2", [Link]());
77. [Link]("3", [Link]());
78. [Link]("4", [Link]());
79. [Link]("5", [Link]());
80. [Link]("6", [Link]());
81. [Link](10.57, [Link]().doubleValue(), 1e-6);
82. [Link]([Link](0).getName(), [Link](0).getName());
83. [Link]([Link](1).getName(), [Link](1).getName());
84. [Link](2, [Link](0).getQuantity());
85. [Link](3, [Link](1).getQuantity());
86. }
87. }
On rappelle que l'interface [IMetier] testée a été décrite au paragraphe [Link], page 415.
• comprenez ce code, notamment celui de la méthode [placeOrder] de la ligne 51. Lorsque vous aurez besoin d'utiliser la
couche [métier], revenez sur ce test ;
• vérifiez que le test passe ;
• vérifiez le contenu de la base après le test ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
418/489
Couche
[web / METIER - DAO - JPA - JDBC BD
JSF]
Serveur Tomcat
7 8
2 3 6
4
5
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
419/489
7 8 9
• en [8] : le fichier [[Link]] de configuration du projet web, et [[Link]] celui du framework JSF ;
• en [9], les ressources du projet : feuilles de style [dossier css], images [dossier images] et Javascript [dossier js] ;
• en [9], les fichiers des messages pour l'internationalisation (anglais / français).
Les images ci-dessus sont des liens. L'utilisateur choisit l'une des catégories de produits en cliquant dessus :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
420/489
Il ajoute différents articles à son panier :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
421/489
Lorsqu'il a fini, il passe au paiement :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
422/489
1
Il peut revenir aux achats en cliquant sur [1] qui est une image cliquable.
[Link] [[Link]]
Le fichier [[Link]] qui configure le projet Maven de la couche [web] est le suivant :
1. <project xmlns="[Link] xmlns:xsi="[Link]
2. xsi:schemaLocation="[Link] [Link]
3. <modelVersion>4.0.0</modelVersion>
4.
5. <groupId>[Link]</groupId>
6. <artifactId>afb-jsf-spring-skel</artifactId>
7. <version>1.0-SNAPSHOT</version>
8. <packaging>jar</packaging>
9.
10. <name>afb-jsf-spring-skel</name>
11.
12. <properties>
13. <[Link]>UTF-8</[Link]>
14. <[Link]>1.8</[Link]>
15. <[Link]>1.8</[Link]>
16. <[Link]>1.8</[Link]>
17. </properties>
18.
19. <parent>
20. <groupId>[Link]</groupId>
21. <artifactId>spring-boot-starter-parent</artifactId>
22. <version>1.3.0.M1</version>
23. </parent>
24.
25. <dependencies>
26. <!-- JSF -->
27. <dependency>
28. <groupId>[Link]</groupId>
29. <artifactId>[Link]</artifactId>
30. <version>2.2.11</version>
31. </dependency>
32. <!-- JSR-330 -->
33. <dependency>
34. <groupId>[Link]</groupId>
35. <artifactId>[Link]</artifactId>
36. <version>1</version>
37. </dependency>
38. <!-- JSP API -->
39. <dependency>
40. <groupId>[Link]</groupId>
41. <artifactId>tomcat-embed-jasper</artifactId>
42. </dependency>
43. <!-- Spring web -->
44. <dependency>
45. <groupId>[Link]</groupId>
46. <artifactId>spring-web</artifactId>
47. </dependency>
48. <!-- couches métier, dao -->
49. <dependency>
50. <groupId>[Link]</groupId>
51. <artifactId>afb-spring-metier-dao-jpa-with-dependencies</artifactId>
52. <version>1.0</version>
53. </dependency>
54. </dependencies>
55.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
423/489
56. <build>
57. <outputDirectory>src/main/webapp/WEB-INF/classes</outputDirectory>
58. <plugins>
59. <plugin>
60. <artifactId>maven-compiler-plugin</artifactId>
61. <configuration>
62. <source>1.8</source>
63. <target>1.8</target>
64. </configuration>
65. </plugin>
66. <plugin>
67. <groupId>[Link]</groupId>
68. <artifactId>maven-surefire-plugin</artifactId>
69. </plugin>
70. </plugins>
71. </build>
72.
73. <repositories>
74. <repository>
75. <id>[Link]</id>
76. <name>Spring Milestone Repository</name>
77. <url>[Link]
78. </repository>
79. </repositories>
80.
81. </project>
• lignes 49-53 : la dépendance sur les couches [METIER – DAO – JPA – JDBC]. Il s'agit de l'archive que vous avez
installée dans le dépôt Maven de votre machine ;
• lignes 27-31 : dépendances sur les bibliothèques d'implémentation de JSF ;
• lignes 33-37 : pour gérer les annotations telles que @Named ;
• lignes 39-42 : pas clair – trouvé sur internet – en son absence, la ligne suivante de [JsfConfig] ne compile pas :
[Link] [JsfConfig]
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
424/489
25. @Bean
26. public ServletRegistrationBean servletRegistrationBean() {
27. FacesServlet servlet = new FacesServlet();
28. ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(servlet, "*.xhtml");
29. return servletRegistrationBean;
30. }
31.
32. @Bean
33. public EmbeddedServletContainerFactory servletContainer() {
34. TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
35. [Link](8080);
36. [Link](10, [Link]);
37. return factory;
38. }
39. }
• ligne 23 : la classe [JsfConfig] étend la classe [SpringBootServletInitializer] qui permet de configurer la couche web d'une
application Spring ;
• lignes 25-30 : définissent le bean [servletRegistrationBean] qui enregistre la servlet qui va traiter les demandes des clients à
la couche web. Cette servlet est la classe [FacesServlet] de la ligne 27. C'est la servlet du framework JSF ;
• ligne 28 : fait deux choses :
◦ enregistre [FacesServlet] comme étant la servlet gérant les demandes à l'application web ;
◦ indique que cette servlet gérera les URL se terminant par le suffixe [.xhtml] ;
• lignes 32-38 : configurent le serveur Tomcat embarqué ;
• ligne 34 : le serveur Tomcat embarqué ;
• ligne 35 : il travaillera sur le port 8080 ;
• ligne 36 : la durée d'inactivité de l'utilisateur au cours d'une session est fixée à 10 minutes. Au-delà, sa session est
supprimée. Une nouvelle sera créée lorsqu'il reprendra son activité ;
[Link] [[Link]]
Le fichier [[Link]] est le fichier qui configure le framework JSF. A cause de l'intégration Spring / JSF faite ici, un certain
nombre de configurations faites d'ordinaire dans [[Link]] sont déplacées dans la configuration Spring. C'est le cas
notamment des beans JSF qui sont désormais traités comme des beans Spring. C'est le sens de l'annotation :
@ComponentScan({"[Link]"})
trouvée dans la classe [JsfConfig]. Les classes trouvées dans le package [[Link]] sont normalement des beans JSF. On en fait
également des beans Spring. Le fichier [[Link]] est réduit au code suivant :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
425/489
1. <?xml version='1.0' encoding='UTF-8'?>
2. <faces-config version="2.2"
3. xmlns="[Link]
4. xmlns:xsi="[Link]
5. xsi:schemaLocation="[Link] [Link]
facesconfig_2_2.xsd">
6. <application>
7. <!-- pour que Spring puisse gérer les beans JSF -->
8. <el-resolver>[Link]</el-resolver>
9. <!-- le fichier des messages -->
10. <resource-bundle>
11. <base-name>
12. messages
13. </base-name>
14. <var>msg</var>
15. </resource-bundle>
16. <message-bundle>messages</message-bundle>
17. </application>
18. </faces-config>
• ligne 8 : pour que Spring gère les beans JSF comme des beans Spring ;
• lignes 10-17 : définissent le fichier des messages internationalisés de l'application JSF ;
[Link] [[Link]]
Toute application web Java est configurée par le fichier [[Link]]. De nouveau, Spring prend en charge certaines configurations qui
disparaissent alors du fichier [[Link]]. Ainsi la classe de configuration [JsfConfig] fait des configurations faites normalement par le
fichier [[Link]]. Celui-ci est alors réduit à ceci :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <web-app xmlns="[Link]
3. xmlns:xsi="[Link]
4. xsi:schemaLocation="[Link]
5. [Link]
6. version="2.5">
7.
8. <servlet>
9. <servlet-name>Faces Servlet</servlet-name>
10. <servlet-class>[Link]</servlet-class>
11. </servlet>
12. </web-app>
• lignes 8-11 : définissent [FacesServlet] comme la servlet de l'application web. Nous avons fait cette configuration
également dans [JsfConfig ) :
1. @Bean
2. public ServletRegistrationBean servletRegistrationBean() {
3. FacesServlet servlet = new FacesServlet();
4. ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(servlet, "*.xhtml");
5. return servletRegistrationBean;
6. }
Ces deux configurations semblent redondantes mais lorsqu'on supprime l'une d'elles, l'application ne marche plus.
[Link] [[Link]]
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
426/489
Le fichier des messages français est le suivant :
1. greeting=Bienvenue \u00e0 l'Epicerie Verte
2. introText=Notre service de livraison \u00e0 domicile vous livre des produits frais, des produits laitiers, des viandes, des
pains et autres produits d\u00e9licieux et sains.
3.
4. # categories
5. dairy=produits laitiers
6. meats=viandes
7. bakery=boulangerie
8. fruit\ &\ veg=fruits et l\u00e9gumes
9.
10. # products
11. milk=lait
12. cheese=fromage
13. butter=beurre
14. free\ range\ eggs=oeufs fermiers
15.
16. organic\ meat\ patties=p\u00e2t\u00e9s biologiques
17. parma\ ham=jambon de Parme
18. chicken\ leg=cuisse de poulet
19. sausages=saucisses
20.
21. sunflower\ seed\ loaf=pain aux graines de tournesol
22. sesame\ seed\ bagel=bagel aux graines de s\u00e9same
23. pumpkin\ seed\ bun=brioche aux graines de citrouille
24. chocolate\ cookies=cookies au chocolat
25.
26. corn\ on\ the\ cob=\u00e9pi de ma\u00efs
27. red\ currants=groseilles rouges
28. broccoli=broccoli
29. seedless\ watermelon=past\u00e8que sans p\u00e9pins
30.
31. # product descriptions
32. milkDescription=demi \u00e9cr\u00e9m\u00e9 (1L)
33. cheeseDescription=cheddar doux (330g)
34. butterDescription=non sal\u00e9 (250g)
35. free\ range\ eggsDescription=taille moyenne (6 oeufs)
36.
37. organic\ meat\ pattiesDescription=garniture d'herbes fra\u00eeches<br>2 p\u00e2t\u00e9s (250g)
38. parma\ hamDescription=vieux, biologique (70g)
39. chicken\ legDescription=\u00e9lev\u00e9 en plein air (250g)
40. sausagesDescription=graisse r\u00e9duite, porc<br>3 saucisses (350g)
41.
42. sunflower\ seed\ loafDescription=600g
43. sesame\ seed\ bagelDescription=4 bagels
44. pumpkin\ seed\ bunDescription=4 brioches
45. chocolate\ cookiesDescription=avec cacahu\u00e8tes<br>(3 cookies)
46.
47. corn\ on\ the\ cobDescription=2 pi\u00e8ces
48. red\ currantsDescription=150g
49. broccoliDescription=500g
50. seedless\ watermelonDescription=250g
51.
52. # shopping cart
53. addToCart=ajouter au chariot
54. item=article
55. items2-4=articles
56. items=articles
57. unit=unit\u00e9
58.
59. # cart page
60. name=nom
61. price=prix
62. product=article
63. quantity=quantit\u00e9
64. updateCart=valider
65. subtotal=sous-total
66. yourCartContains=Votre chariot contient
67. yourCartEmpty=Votre chariot est vide
68. clearCart=vider le chariot
69. continueShopping=continuer les achats
70. proceedCheckout=passer au paiement
71.
72. # checkout page
73. checkout=paiement
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
427/489
74. checkoutText=Pour le paiement de vos achats, veuillez nous donner les informations suivantes :
75. customerName=nom
76. email=adresse \u00e9lectronique
77. phone=t\u00e9l\u00e9phone
78. address=adresse
79. creditCard=n\u00b0 de carte de cr\u00e9dit
80. submit=valider
81. surcharge=co\u00fbt livraison
82. total=total
83. errorHeader=L'erreur suivante s'est produite (Pensez \u00e0 vider la base ou \u00e0 changer d'utilisateur)
84.
85. # conditions
86. nextDayGuarantee=Livraison J+1 garantie
87. deliveryFee1=A
88. deliveryFee2=le surco\u00fbt de livraison est appliqu\u00e9 \u00e0 tous les articles
89.
90. # confirmation
91. successMessage=Votre commande a \u00e9t\u00e9 trait\u00e9e et sera livr\u00e9e dans les 24 h.
92. confirmationNumberMessage=Veuillez conserver votre n\u00b0 de confirmation :
93. contactMessage=Si vous avez une question concernant votre commande, veuillez nous <a href="#">contacter</a>.
94. thankYouMessage=Merci de votre visite \u00e0 l'Epicerie Verte.
95. orderSummary=r\u00e9sum\u00e9 de la commande
96. deliveryAddress=adresse de livraison
97. dateProcessed=date de traitement
98.
99. # header
100. cart=chariot
101. title=L'Epicerie Verte
102.
103. # footer
104. privacy=Politique interne
105. contact=Contact
106.
107. # error
108. forbidden=Vous ne pouvez acc\u00e9der \u00e0 cette page.<br><br>Retour \u00e0 la page d'<a
href="/AffableBean/[Link]">accueil</a>.
109. notFound=La page que vous avez demand\u00e9e n'est pas disponible.<br><br>Retour \u00e0 la page d'<a
href="/AffableBean/[Link]">accueil</a>.
110. internalError=Oops! Une erreur interne s'est produite.<br><br>Retour \u00e0 la page d'<a
href="/AffableBean/[Link]">accueil</a>.
111. orderFailureError=Nous n'avons pu traiter votre commande. Veuillez essayer de nouveau !
112.
113. #field validation error
114. [Link]=Donn\u00e9e requise !
115.
116. # divers
117. affablebean=L\'\u00e9picerie verte
118. resetDatabase=Vider la base
• ligne 9 : la méthode statique [[Link]] est exécutée. C'est une méthode du projet [Spring Boot] (ligne 3). Elle
lance l'application Spring configurée par son premier argument, ici la classe [JsfConfig]. Le serveur Tomcat embarqué dans
les dépendances va alors être lancé sur le port 8080 ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
428/489
Les logs sur la console Netbeans ressemblent à ceci :
1. . ____ _ __ _ _
2. /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
3. ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
4. \\/ ___)| |_)| | | | | || (_| | ) ) ) )
5. ' |____| .__|_| |_|_| |_\__, | / / / /
6. =========|_|==============|___/=/_/_/_/
7. :: Spring Boot :: (v1.3.0.M1)
8.
9. [Link].639 [main] DEBUG [Link] - Adding [servletConfigInitParams] PropertySource with
lowest search precedence
10. [Link].642 [main] DEBUG [Link] - Adding [servletContextInitParams] PropertySource with
lowest search precedence
11. [Link].643 [main] DEBUG [Link] - Adding [systemProperties] PropertySource with lowest
search precedence
12. [Link].643 [main] DEBUG [Link] - Adding [systemEnvironment] PropertySource with lowest
search precedence
13. [Link].643 [main] DEBUG [Link] - Initialized StandardServletEnvironment with
PropertySources [servletConfigInitParams,servletContextInitParams,systemProperties,systemEnvironment]
14. [Link].676 [main] INFO [Link] - Starting Boot on Gportpers3 with PID 10048 (D:\data\istia-1516\projets\
affablebean\jee\cc-full\afb-jsf-spring\src\main\webapp\WEB-INF\classes started by usrlocal in D:\data\istia-1516\projets\
affablebean\jee\cc-full\afb-jsf-spring)
15. [Link].711 [main] INFO [Link] - Refreshing
[Link]@32464a14: startup date [Tue Oct 06
[Link] CEST 2015]; root of context hierarchy
16. [Link].075 [main] DEBUG [Link] - Adding [applicationConfig:
[classpath:/[Link]]] PropertySource with search precedence immediately lower than
[applicationConfigurationProperties]
17. [Link].075 [main] DEBUG [Link] - Removing [applicationConfigurationProperties]
PropertySource
18. [Link].075 [main] DEBUG [Link] - Removing [defaultProperties] PropertySource
19. [Link].092 [main] INFO [Link] - JSR-330 '[Link]' annotation found
and supported for autowiring
20. [Link].377 [main] INFO [Link] - Tomcat initialized with port(s): 8080 (http)
21. [Link].568 [main] INFO [Link] - Starting service Tomcat
22. [Link].569 [main] INFO [Link] - Starting Servlet Engine: Apache Tomcat/8.0.23
23. [Link].964 [localhost-startStop-1] INFO [Link] - At least one JAR was scanned for TLDs yet
contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were
found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
24. [Link].988 [localhost-startStop-1] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded
WebApplicationContext
25. [Link].988 [localhost-startStop-1] DEBUG [Link] - Published root WebApplicationContext as
ServletContext attribute with name [[Link]]
26. [Link].988 [localhost-startStop-1] INFO [Link] - Root WebApplicationContext: initialization
completed in 1280 ms
27. [Link].029 [localhost-startStop-1] INFO [Link] - Mapping servlet: 'facesServlet' to [*.xhtml]
28. [Link].081 [localhost-startStop-1] INFO [Link] - Initialisation de Mojarra 2.2.11
( 20150505-0732 [Link] pour le contexte «»
29. [Link].270 [localhost-startStop-1] INFO [Link] - JSF1048 : Présence d’annotations
PostConstruct/PreDestroy Les méthodes de beans gérés marquées avec ces annotations auront des annotations dites traitées.
30. [Link].807 [main] DEBUG [Link] - Replacing [servletContextInitParams] PropertySource with
[servletContextInitParams]
31. [Link].958 [main] INFO [Link] - Building JPA container EntityManagerFactory for
persistence unit 'default'
32. [Link].984 [main] INFO [Link] - HHH000204: Processing PersistenceUnitInfo [
33. name: default
34. ...]
35. [Link].073 [main] INFO [Link] - HHH000412: Hibernate Core {[Link]}
36. [Link].075 [main] INFO [Link] - HHH000206: [Link] not found
37. [Link].076 [main] INFO [Link] - HHH000021: Bytecode provider name : javassist
38. [Link].323 [main] INFO [Link] - HCANN000001: Hibernate Commons Annotations {[Link]}
39. [Link].867 [main] INFO [Link] - HHH000400: Using dialect: [Link]
40. [Link].164 [main] INFO [Link] - HHH000397: Using ASTQueryTranslatorFactory
41. [Link].496 [main] INFO [Link] - HHH000228: Running hbm2ddl schema update
42. [Link].496 [main] INFO [Link] - HHH000102: Fetching database metadata
43. [Link].498 [main] INFO [Link] - HHH000396: Updating schema
44. [Link].552 [main] INFO [Link] - HHH000261: Table found: [Link]
45. [Link].552 [main] INFO [Link] - HHH000037: Columns: [versioning, name, id]
46. [Link].552 [main] INFO [Link] - HHH000108: Foreign keys: []
47. [Link].552 [main] INFO [Link] - HHH000126: Indexes: [name, primary]
48. [Link].589 [main] INFO [Link] - HHH000261: Table found: [Link]
49. [Link].590 [main] INFO [Link] - HHH000037: Columns: [city_region, versioning, address, phone,
name, id, cc_number, email]
50. [Link].590 [main] INFO [Link] - HHH000108: Foreign keys: []
51. [Link].590 [main] INFO [Link] - HHH000126: Indexes: [name, cc_number, primary]
52. [Link].612 [main] INFO [Link] - HHH000261: Table found: e-magasin.customers_orders
53. [Link].612 [main] INFO [Link] - HHH000037: Columns: [confirmation_number, amount, versioning,
date_created, id, customer_id]
54. [Link].612 [main] INFO [Link] - HHH000108: Foreign keys: [customers_orders_fk1]
55. [Link].612 [main] INFO [Link] - HHH000126: Indexes: [fk_customer_order_customer, primary]
56. [Link].653 [main] INFO [Link] - HHH000261: Table found: e-magasin.ordered_products
57. [Link].653 [main] INFO [Link] - HHH000037: Columns: [versioning, quantity, product_id,
customer_order_id, id]
58. [Link].653 [main] INFO [Link] - HHH000108: Foreign keys: [ordered_products_fk2,
ordered_products_fk1]
59. [Link].653 [main] INFO [Link] - HHH000126: Indexes: [fk_ordered_product_product,
customer_order_id, primary, fk_ordered_product_customer_order]
60. [Link].673 [main] INFO [Link] - HHH000261: Table found: [Link]
61. [Link].673 [main] INFO [Link] - HHH000037: Columns: [versioning, category_id, price,
last_update, name, description, id]
62. [Link].673 [main] INFO [Link] - HHH000108: Foreign keys: [products_fk1]
63. [Link].673 [main] INFO [Link] - HHH000126: Indexes: [name, fk_product_category, primary]
64. [Link].709 [main] INFO [Link] - HHH000261: Table found: [Link]
65. [Link].710 [main] INFO [Link] - HHH000037: Columns: [versioning, name, id]
66. [Link].710 [main] INFO [Link] - HHH000108: Foreign keys: []
67. [Link].710 [main] INFO [Link] - HHH000126: Indexes: [uk_lpdecwdy9vmiwufr3k4a2quus, primary]
68. [Link].745 [main] INFO [Link] - HHH000261: Table found: [Link]
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
429/489
69. [Link].745 [main] INFO [Link] - HHH000037: Columns: [password, versioning, name, id, login]
70. [Link].746 [main] INFO [Link] - HHH000108: Foreign keys: []
71. [Link].746 [main] INFO [Link] - HHH000126: Indexes: [uk_l3c3ahdulnjx8bt2ivgyvh1ss, primary]
72. [Link].765 [main] INFO [Link] - HHH000261: Table found: e-magasin.users_roles
73. [Link].765 [main] INFO [Link] - HHH000037: Columns: [versioning, role_id, user_id, id]
74. [Link].765 [main] INFO [Link] - HHH000108: Foreign keys: [fk_vt6s6gnj0o1ocigu9mwf0x9d,
fk_777tey2suldskv0nuy69r2q54]
75. [Link].766 [main] INFO [Link] - HHH000126: Indexes: [fk_vt6s6gnj0o1ocigu9mwf0x9d,
fk_777tey2suldskv0nuy69r2q54, primary]
76. [Link].767 [main] INFO [Link] - HHH000232: Schema update complete
77. [Link].639 [main] INFO [Link].http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8080"]
78. [Link].649 [main] INFO [Link].http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]
79. [Link].674 [main] INFO [Link] - Using a shared selector for servlet write/read
80. [Link].709 [main] INFO [Link] - Tomcat started on port(s): 8080 (http)
81. [Link].712 [main] INFO [Link] - Started Boot in 5.288 seconds (JVM running for 5.855)
Vérifications :
• vérifiez que vous obtenez bien la page précédente. L'image [1] est un lien qui appartient à l'entête de toutes les vues de
cette application. Il permet de revenir à la page [[Link]].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
430/489
• ligne 6 : l'annotation [@Scope] est une annotation Spring (ligne 3). Elle fait du bean JSF [ApplicationData] un bean de
portée [Application]. Il est instancié une seule fois et son contenu est accessible à toutes les requêtes HTTP de tous les
utilisateurs de l'application ;
• ligne 7 : la classe [ApplicationData] implémente l'interface [IMetier] des couches [METIER-DAO-JPA-JDBC]. Le bean
[Form] utilisera les méthodes exposées par le bean [ApplicationData] ;
Vue Bean
Serveur Tomcat
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
431/489
66. public void setCategories(List<Category> categories) {
67. [Link] = categories;
68. }
69.
70. public Double getDeliverySurcharge() {
71. return deliverySurcharge;
72. }
73.
74. public void setDeliverySurcharge(Double deliverySurcharge) {
75. [Link] = deliverySurcharge;
76. }
77.
78. @Override
79. public void resetDatabase() {
80. [Link]();
81. }
82. }
Conseils :
• utilisez le bean [metier] des lignes 29-30 ;
• on ne gèrera pas l'éventuelle exception ;
• pour l'instant, il n'y a pas de vérifications à faire avec l'application web. Cela viendra plus tard ;
Le bean [SessionData] est de portée Session. Son rôle est de mémoriser des informations utilisables par différentes requêtes d'un
même utilisateur. C'est la mémoire de l'utilisateur.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
432/489
Le bean [SessionData] est un simple conteneur d'informations. On donne ici un contenu de départ minimal et déjà référencé dans
certaines pages de l'application. Il vous restera à déterminer quelles autres informations devront aller en session. On rappelle que
dès que la requête Rn+1 a besoin d'informations construites par la requête précédente Rn, cette dernière doit mettre ces informations
en session. Vous ne devez mettre dans la session que le strict nécessaire, ç-à-d ce qui est réellement de portée session. Ce qui est de
portée requête doit être placé dans le bean [Form] qui suit.
Le bean [Form] est de portée [request]. Il est créé au début de chaque requête de l'utilisateur et supprimé à la fin. Il détient tous les
gestionnaires d'événements traitant les différents POST des navigateurs clients. Il mémorise de plus les valeurs postées. Son code
est le suivant :
1. package [Link];
2.
3. import [Link];
4.
5. import [Link];
6. import [Link];
7.
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
12. import [Link];
13.
14. @Named
15. @Scope("request")
16. public class Form {
17.
18. // autres beans
19. @Autowired
20. private SessionData sessionData;
21. @Autowired
22. private ApplicationData applicationData;
23.
24. public Form(){
25. [Link]("Form");
26. }
27.
28. // produits d'une catégorie
29. public String getProductsFromCategory(Category category) {
30. throw new RuntimeException("méthode pas encore implémentée");
31. // ....
32. // page des produits
33. }
34.
35. // ajout d'un article au panier
36. public String addToCart(Product product) {
37. throw new RuntimeException("méthode pas encore implémentée");
38. // ....
39. // même page
40.
41. }
42.
43. // afficher le panier
44. public String viewCart(Boolean clear) {
45. throw new RuntimeException("méthode pas encore implémentée");
46. // ...
47.
48. // page cart
49.
50. }
51.
52. // revenir aux derniers produits sélectionnés
53. public String continueShopping() {
54. return "products";
55. }
56.
57. // passer au paiement
58. public String proceedCheckout() {
59. throw new RuntimeException("méthode pas encore implémentée");
60. // ...
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
433/489
61.
62. // page paiement
63.
64. }
65.
66. // enregistrement de l'ordre d'achats
67. public String checkout() {
68. // ...
69.
70. // page de confirmation
71.
72. throw new RuntimeException("méthode pas encore implémentée");
73. }
74.
75. // mise à jour de la qté d'un article acheté
76. public String updateCart(ShoppingCartItem cartItem) {
77. // ...
78.
79. // même page
80.
81. }
82.
83. // duplication du panier
84. private ShoppingCart cloneCart() {
85. throw new RuntimeException("méthode pas encore implémentée");
86. }
87.
88. public void resetDatabase() {
89. [Link]();
90. }
91.
92. // getters et setters
93. ...
94. }
Le bean [Form] peut contenir d'autres données que celles indiquées ici. Ce sera à vous de les déterminer. Votre travail va consister à
écrire les différentes méthodes de ce bean en lieu et place des exceptions actuellement lancées. Les commentaires sont là pour vous
aider.
Les différentes vues de l'application sont construites à partir du modèle [[Link]] suivant :
1. <?xml version='1.0' encoding='UTF-8' ?>
2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[Link]
3. <f:view xmlns="[Link] xmlns:f="[Link]
4. xmlns:h="[Link] xmlns:ui="[Link] locale="#{[Link]}">
5. <!-- entete -->
6. <ui:include src="[Link]" />
7. <h:body>
8. <!-- corps -->
9. <ui:insert name="content"/>
10. <!-- bas de page -->
11. <ui:include src="[Link]" />
12. </h:body>
13. </f:view>
• ligne 4 : chaque vue sera affichée avec la langue mémorisée dans la session ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
434/489
• le modèle inclut un entête prédéfini (ligne 6), un corps qui va changer avec chaque vue (ligne 9), un bas de page prédéfini
(ligne 11).
Votre travail sur les vues va consister à compléter celles qui vous sont données et qui seront affichées en [2].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
435/489
Les pages XHTML de cette application sont essentiellement constituées de mises en forme à l'aide d'une feuille de style. On
supposera que celle-ci a été faite par un designer. Il a laissé des zones blanches pour les développeurs (ligne 18). Notre travail est de
remplir celles-ci.
Il faut se repérer dans le code. En lignes 8-31, on a un formulaire. Celui-ci est posté par l'un des liens de la ligne 20.
Ci-dessus, la page [[Link]] affiche les catégories d'articles en vente au moyen d'images. Chaque image est un lien. Elles sont
générées par le code suivant :
1. <ui:repeat var="category" value="#{null}">
2. <div class="categoryBox">
3. <h:commandLink id="lnk#{[Link]}" action="#{[Link](category)}">
4. <span class="categoryLabel"/>
5. <span class="categoryLabelText">
6. #{msg[[Link]]}
7. </span>
8. <img src="#{[Link]}/resources/images/categories/#{[Link]}.jpg"
9. alt="#{msg[[Link]]}" class="categoryImage"/>
10. </h:commandLink>
11. </div>
12. </ui:repeat>
• ligne 1 : la balise <ui:repeat> fonctionne comme une boucle. Elle parcourt la collection de catégories définie par l'attribut
value. Dans une itération, la catégorie courante est définie par l'attribut var (ligne 1). Ainsi ligne 6, [Link] désigne le
nom de la catégorie de l'itération courante. Pour l'instant, l'attribut [value="#{null}"] fait que les lignes 2-11 sont
exécutées 0 fois ;
• ligne 3 : lorsque l'un des liens est cliqué, la méthode #{[Link](category)} est exécutée. C'est une
possibilité de JSF2 qu'on n'a pas vue en cours : la possibilité de transmettre un argument à un gestionnaire d'événements,
ici la catégorie correspondant au lien cliqué, un objet de type [Category].
Question 2 : donner la valeur de l'attribut value de la ligne 1, sachant que celle-ci doit être la liste des catégories de produits en
vente.
Vérifiez que maintenant, vous obtenez la page suivante pour l'URL [[Link] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
436/489
Lorsque l'une des images ci-dessus est cliquée, la méthode #{[Link](category)} est exécutée.
1. public String getProductsFromCategory(Category category) {
2. // ...
3.
4. // page des produits
5.
6. }
Conseils :
• l'objet [Category category] de la ligne 1 ci-dessus est l'objet [category] de la ligne 1 ci-dessous. Il désigne l'objet [Category]
dont on a cliqué l'image ;
1. <ui:repeat var="category" value="...">
2. <div class="categoryBox">
3. <h:commandLink id="lnk#{[Link]}" action="#{[Link](category)}">
4. <span class="categoryLabel"/>
5. <span class="categoryLabelText">
6. #{msg[[Link]]}
7. </span>
8. <img src="#{[Link]}/resources/images/categories/#{[Link]}.jpg"
9. alt="#{msg[[Link]]}" class="categoryImage"/>
10. </h:commandLink>
11. </div>
12. </ui:repeat>
• le paramètre [Category category] de la méthode [getProductsFromCategory] a été obtenu par une requête JPQL qui n'a pas
ramené le champ [List<Product> products] de cet objet (mode lazy loading). Vous ne pouvez donc l'utiliser. Si vous le
faites, vous aurez une exception ;
• à ce stade, il n'y a pas de vérifications à faire avec l'application web ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
437/489
1
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
438/489
58. </f:view>
Vérifications : vérifiez que vous êtes maintenant capable d'obtenir les produits des catégories lorsque vous cliquez sur l'image de
l'une d'elles :
Ligne 51 de [[Link]], on voit qu'un clic sur le bouton provoque l'exécution de la méthode #{[Link](product)}. Celle-
ci est la suivante :
1. // ajout d'un article (product) au panier
2. public String addToCart(Product product) {
3. ...
4. // on reste sur la même page
5. ...
6. }
Conseils :
• le paramètre [Product product] vient de la ligne 19 ci-dessous :
1. <div id="categoryRightColumn">
2. <p id="categoryTitle">#{msg[[Link]]}</p>
3. <h:dataTable var="product" value="..." rowClasses="lightBlue, white">
4. <h:column>
5. <img src="#{[Link]}/resources/images/products/#{[Link]}.png" alt="#{msg[[Link]]}" />
6. </h:column>
7. <h:column>
8. #{msg[[Link]]}
9. <br />
10. <c:set var="description" value="#{[Link]}Description" />
11. <h:outputText class="smallText" value="#{msg[description]}" escape="false" />
12. </h:column>
13. <h:column>
14. <h:outputText value="#{[Link]}">
15. <f:convertNumber type="currency" currencySymbol="€" />
16. </h:outputText>
17. </h:column>
18. <h:column>
19. <h:commandButton id="cmd#{[Link]}" action="#{[Link](product)}" value="#{msg['addToCart']}" />
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
439/489
20. </h:column>
21. </h:dataTable>
22. </div>
La ligne 19 référence l'objet [product] de la ligne 3. Il s'agit du produit dont l'image a été cliquée ;
Vérifications :
• il n'y a pas de vérifications à faire à ce stade ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
440/489
1
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
441/489
57. </h:outputText>
58. <br/>
59. <span class="smallText">(
60. <h:outputText value="...">
61. <f:convertNumber type="currency" currencySymbol="€ "/>
62. </h:outputText>
63. #{msg['unit']})
64. </span>
65. </h:column>
66.
67. <h:column>
68. <f:facet name="header">#{msg['quantity']}</f:facet>
69. <h:form>
70. <h:commandButton id="cmd#{[Link]}" action="#{[Link](cartItem)}" value="#{msg['updateCart']}"/>
71. <h:inputText id="quantity#{[Link]}" maxlength="2" size="2" value="#{[Link]}"
style="margin:5px"/>
72. </h:form>
73. </h:column>
74. </h:dataTable>
75. </ui:fragment>
76. </div>
77. </ui:define>
78. </ui:composition>
79. </f:view>
Question 7 : indiquez les valeurs des attributs value des lignes 34, 39, 55 et 60.
Vérifications :
• vérifiez que vous pouvez maintenant afficher le contenu du chariot. Par ailleurs, le lien [continuer les achats] est
normalement opérationnel ;
Ligne 70 ci-dessus ([Link]), on voit que le clic sur un bouton exécute la méthode #{[Link](cartItem)}. Celle-ci est la
suivante :
1. // mise à jour de la qté d'un article acheté
2. public String updateCart(ShoppingCartItem cartItem) {
3.
4. // on reste sur la même page
5. }
Question 8 : écrire la méthode updateCart. Revoir les définitions de [ShoppingCart] page 413, et [ ShoppingCartItem] page 415.
Conseils :
• le paramètre [ShoppingCartItem cartItem] de la ligne 2 ci-dessus, est l'objet [cartItem] des lignes 1 et 32 ci-dessous. Il
désigne le produit du panier dont l'utilisateur a cliqué le bouton [valider] :
1. <h:dataTable var="cartItem" value="..." rowClasses="lightBlue, white" headerClass="header">
2.
3. <h:column>
4. <f:facet name="header">#{msg['product']}</f:facet>
5. <img src="#{[Link]}/resources/images/products/#{[Link]}.png"
6. alt="#msg[[Link]]}"/>
7. </h:column>
8.
9. <h:column>
10. <f:facet name="header">#{msg['name']}</f:facet>
11. #{msg[[Link]]}
12. </h:column>
13. <td></td>
14.
15. <h:column>
16. <f:facet name="header">#{msg['price']}</f:facet>
17. <h:outputText value="...">
18. <f:convertNumber type="currency" currencySymbol="€ "/>
19. </h:outputText>
20. <br/>
21. <span class="smallText">(
22. <h:outputText value="...">
23. <f:convertNumber type="currency" currencySymbol="€ "/>
24. </h:outputText>
25. #{msg['unit']})
26. </span>
27. </h:column>
28.
29. <h:column>
30. <f:facet name="header">#{msg['quantity']}</f:facet>
31. <h:form>
32. <h:commandButton id="cmd#{[Link]}" action="#{[Link](cartItem)}" value="#{msg['updateCart']}"/>
33. <h:inputText id="quantity#{[Link]}" maxlength="2" size="2" value="#{[Link]}"
style="margin:5px"/>
34. </h:form>
35. </h:column>
36. </h:dataTable>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
442/489
Vérifications :
• vérifiez que vous êtes maintenant capable de changer la quantité achetée d'un produit grâce au bouton [valider] :
Conseils :
• ligne 3 ci-dessus, le total à payer pour le panier doit inclure la taxe de livraison définie dans le bean [ApplicationData] ;
• il n'y a pas de vérifications à faire à ce stade ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
443/489
4. xmlns:h="[Link] xmlns:ui="[Link]
5.
6. <ui:composition template="[Link]">
7. <ui:define name="content">
8. <div id="singleColumn">
9. <h2>#{msg["checkout"]}</h2>
10. <p>#{msg["checkoutText"]}</p>
11. <h:panelGrid columns="2">
12. <h:panelGroup>
13. <h:form id="checkoutForm">
14. <h:panelGrid columns="2" id="checkoutTable" style="width: 405px;padding: 10px;background-color:
#f5eabe;float: left;border-radius: 4px;">
15. <h:outputLabel for="name" value="#{msg['customerName']}:"/>
16. <h:inputText size="31" maxlength="45" id="name" value="..." required="true"
requiredMessage="#{msg['[Link]']}" styleClass="inputField"/>
17. <h:panelGroup/>
18. <h:message for="name" styleClass="error"/>
19. <h:outputLabel for="email" value="#{msg['email']}:"/>
20. <h:inputText size="31" maxlength="45" id="email" value="..." required="true"
requiredMessage="#{msg['[Link]']}" styleClass="inputField"/>
21. <h:panelGroup/>
22. <h:message for="email" styleClass="error"/>
23. <h:outputLabel for="phone" value="#{msg['phone']}:"/>
24. <h:inputText size="31" maxlength="16" id="phone" value="..." required="true"
requiredMessage="#{msg['[Link]']}" styleClass="inputField"/>
25. <h:panelGroup/>
26. <h:message for="phone" styleClass="error"/>
27. <h:outputLabel for="address" value="#{msg['address']}:"/>
28. <h:inputText size="31" maxlength="45" id="address" value="..." required="true"
requiredMessage="#{msg['[Link]']}" styleClass="inputField"/>
29. <h:panelGroup/>
30. <h:message for="address" styleClass="error"/>
31. <h:outputLabel for="creditcard" value="#{msg['creditCard']}:"/>
32. <h:inputText size="31" maxlength="19" id="creditcard" class="creditcard" value="..." required="true"
requiredMessage="#{msg['[Link]']}" styleClass="inputField"/>
33. <h:panelGroup/>
34. <h:message for="creditcard" styleClass="error"/>
35. <h:commandButton id="valider" value="#{msg['submit']}" action="#{[Link]()}"/>
36. </h:panelGrid>
37. </h:form>
38. </h:panelGroup>
39. <h:panelGroup>
40. <ul style="text-align: left">
41. <li>#{msg["nextDayGuarantee"]}</li>
42. <li>#{msg["deliveryFee1"]}
43. <h:outputText value="...">
44. <f:convertNumber type="currency" currencySymbol="€ " />
45. </h:outputText>
46. #{msg["deliveryFee2"]}</li>
47. </ul>
48. <h:panelGrid columns="2" style="padding: 10px;border: 1px solid #c3e3e0;background-color: #c3e3e0;border-
radius: 4px;">
49. #{msg["subtotal"]}:
50. <h:outputText value="..." styleClass="checkoutPriceColumn">
51. <f:convertNumber type="currency" currencySymbol="€ " />
52. </h:outputText>
53. #{msg["surcharge"]}:
54. <h:outputText value="..." styleClass="checkoutPriceColumn">
55. <f:convertNumber type="currency" currencySymbol="€ " />
56. </h:outputText>
57. <h:outputText value="#{msg['total']}:" styleClass="total"/>
58. <h:outputText value="..." styleClass="total checkoutPriceColumn">
59. <f:convertNumber type="currency" currencySymbol="€ " />
60. </h:outputText>
61. </h:panelGrid>
62. </h:panelGroup>
63. </h:panelGrid>
64. <ui:fragment rendered='#{[Link]}'>
65. <p id="errorText">
66. <strong>#{msg['errorHeader']}</strong>
67. <br/><br/>
68. <h:outputText value="#{[Link]}" escape="false"/>
69. </p>
70. </ui:fragment>
71. </div>
72. </ui:define>
73. </ui:composition>
74. </f:view>
Faites le lien entre ce code et ce qu'il affiche. Notez que lignes 13-37, il y a un formulaire JSF. Il y a donc des valeurs postées. Vous
devez prévoir des champs dans le bean [Form] pour accueillir ces valeurs postées.
Question 10 : indiquez les valeurs des attributs value des lignes 16, 20, 24, 28, 32. Indiquez les valeurs des attributs value des lignes
43, 50, 54, 58. Revoir la définition du bean [Form], page 433.
Vérifications :
• vérifiez qu'après avoir acheté quelques produits, vous êtes capable d'afficher la page de paiement ;
Ligne 35 ci-dessus, un clic sur le bouton provoque l'exécution de la méthode #{[Link]()}. Celle-ci est la suivante :
1. // enregistrement en base de la commande
2. public String checkout() {
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
444/489
3. ...
4. // page de confirmation ou affichage d'une erreur
5. ...
6. }
• ligne 4 : dans la table [Customers], le nom du client a une contrainte d'unicité. Si donc, on fait deux fois des achats avec le
même client, on aura une exception qui doit être gérée. Dans la page [[Link]], la zone concernée pour l'affichage
de l'erreur est la suivante :
1. <ui:fragment rendered='#{[Link]}'>
2. <p id="errorText">
3. <strong>#{msg['errorHeader']}</strong>
4. <br/><br/>
5. <h:outputText value="#{[Link]}" escape="false"/>
6. </p>
7. </ui:fragment>
Conseils :
• après l'enregistrement de la commande, le panier de l'utilisateur doit être vidé ;
• il n'y a pas de vérifications à faire à ce stade ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
445/489
2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[Link]
3. <f:view xmlns="[Link] xmlns:f="[Link]
4. xmlns:h="[Link] xmlns:ui="[Link]
5.
6. <ui:composition template="[Link]">
7.
8. <ui:define name="content">
9. <div id="singleColumn">
10.
11. <p id="confirmationText">
12. <strong>#{msg['successMessage']}</strong>
13. <br/><br/>
14. #{msg['confirmationNumberMessage']}
15. <strong>...</strong>
16. <br/>
17. <h:outputText value="#{msg['contactMessage']}" escape="false"/>
18. <br/><br/>
19. #{msg['thankYouMessage']}
20. </p>
21.
22. <div class="summaryColumn" >
23.
24. <table id="orderSummaryTable" class="detailsTable">
25. <tr class="header">
26. <th colspan="3">#{msg['orderSummary']}</th>
27. </tr>
28.
29. <tr class="tableHeading">
30. <td>#{msg['product']}</td>
31. <td>#{msg['quantity']}</td>
32. <td>#{msg['price']}</td>
33. </tr>
34.
35. <ui:repeat var="cartItem" value="..." varStatus="iter">
36.
37. <tr class="${(([Link] % 2) != 0) ? 'lightBlue' : 'white'}">
38. <td>
39. #{msg[[Link]]}
40. </td>
41. <td class="quantityColumn">
42. #{[Link]}
43. </td>
44. <td class="confirmationPriceColumn">
45. <h:outputText value="...">
46. <f:convertNumber type="currency" currencySymbol="€ "/>
47. </h:outputText>
48. </td>
49. </tr>
50. </ui:repeat>
51.
52. <tr class="lightBlue"><td colspan="3" style="padding: 0 20px"><hr/></td></tr>
53.
54. <tr class="lightBlue">
55. <td colspan="2" id="deliverySurchargeCellLeft"><strong>#{msg['surcharge']}:</strong></td>
56. <td id="deliverySurchargeCellRight">
57. <h:outputText value="...">
58. <f:convertNumber type="currency" currencySymbol="€ "/>
59. </h:outputText>
60. </td>
61. </tr>
62.
63. <tr class="lightBlue">
64. <td colspan="2" id="totalCellLeft"><strong>#{msg['total']}:</strong></td>
65. <td id="totalCellRight">
66. <h:outputText value="...">
67. <f:convertNumber type="currency" currencySymbol="€ "/>
68. </h:outputText>
69. </td>
70. </tr>
71.
72. <tr class="lightBlue"><td colspan="3" style="padding: 0 20px"><hr/></td></tr>
73.
74. <tr class="lightBlue">
75. <td colspan="3" id="dateProcessedRow"><strong>#{msg['dateProcessed']}:</strong>
76. <h:outputText value="...">
77. <f:convertDateTime type="both" dateStyle="short" timeStyle="short"/>
78. </h:outputText>
79. </td>
80. </tr>
81. </table>
82.
83. </div>
84.
85. <div class="summaryColumn" >
86. <h:panelGrid id="deliveryAddressTable" class="detailsTable" headerClass="header" columns="1"
columnClasses="lightBlue" >
87. <f:facet name="header">#{msg['deliveryAddress']}</f:facet>
88. ...
89. <br/>
90. ...
91. <br/>
92. <hr/>
93. <strong>#{msg['email']}:</strong> ...
94. <br/>
95. <strong>#{msg['phone']}:</strong> ...
96. </h:panelGrid>
97. </div>
98. </div>
99. </ui:define>
100. </ui:composition>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
446/489
101. </f:view>
Question 12 : compléter le code des lignes 15, 42, 45, 57, 66, 76, 88, 90, 93, 95.
Vérifications :
• les informations affichées pour l'acheteur sont son nom et son adresse ;
• vérifiez que vous obtenez désormais une page de confirmation correcte ;
• vérifiez que vous obtenez un message d'erreur si vous faites des achats deux fois avec le même client (même nom) ;
Question 13 : gérez les liens de l'application qui n'ont pas encore été gérés (lien 1 ci-dessus).
qui permet de passer de la page [[Link]] à la page [[Link]]. L'inconvénient de la ligne 5 est qu'elle ne change pas l'URL
affichée par le navigateur. Celui-ci va continuer à afficher [[Link]]. Si l'utilisateur fait [F5] pour recharger la page, ce n'est pas la
page [[Link]] qui va alors être redemandée mais le POST issu de la page [[Link]] qui va être rejoué. Très souvent, ce
comportement n'est pas souhaitable. On peut alors utiliser une redirection :
L'intérêt de cette technique est que le navigateur va charger la page [[Link]] et que c'est cette URL qu'il va afficher. Par
ailleurs, si l'utilisateur fait [F5] pour recharger la page, c'est une opération GET vers la page [[Link]] qui va être réexécutée.
Le précédent POST n'est pas rejoué. Pour obtenir cette redirection, le code précédent évolue de la façon suivante :
1. // gestionnaires d'évts
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
447/489
2. public String getProductsFromCategory(Category category) throws IOException {
3. ...
4. // page des produits
5. [Link]().getExternalContext().redirect("[Link]");
6. return null;
7. }
Question 14 : faites en sorte que toutes les méthodes de la classe [Form] traitant un POST, assurent une redirection vers la page
suivante. Testez la nouvelle application et vérifiez les nouvelles URL affichées par le navigateur.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
448/489
7 Étude de cas n° 7
7.2 Le problème
Nous nous proposons d'écrire une application web Java EE permettant l'affichage des notes d'une classe de collège. L'application
présentera trois vues JSF (Java Server Faces) :
La vue JSF [[Link]] est une vue de démonstration. Elle présente la liste des classes du collège :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
449/489
La vue JSF [[Link]] permet d'avoir les notes d'une classe :
Application web
couche [web]
1
Faces Servlet
2
SGBD
4 [Link] Form [métier] [dao] [jpa] [jdbc]
3
JSF2 Spring
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
450/489
L'application est constituée de plusieurs couches. De droite à gauche :
Les couches [métier, dao, jpa] vous sont données. Seule la couche web est à construire.
• [04-ecole-metier-dao-jpa-test] est un projet de test des couches [métier, dao, jpa, jdbc] ;
• [04-ecole-jsf-skel] est le projet Netbeans de la couche [web]. Il est incomplet et votre travail consiste à le compléter pour
obtenir les résultats présentés précédemment.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
451/489
• ID : N° de la classe, clé primaire
• VERSION : n° de version de l'enregistrement
• NOM : nom de la classe
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
452/489
• ID : N° de l'élève, clé primaire
• VERSION : n° de version de l'enregistrement
• NOM : nom de l'élève
• PRENOM : prénom de l'élève
• ID_CLASSE : n° de sa classe, clé étrangère sur la
colonne ID de la table CLASSES
La table [NOTES] mémorise les notes des élèves dans les différentes matières :
Travail n° 1 : la base [[Link]] est déjà présente sur vos machines. Visualisez-la en utilisant WampServer et PhpMyAdmin.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
453/489
Application web
couche [web]
1
SGBD
4 [métier] [dao] [jpa] [Jdbc]
JSF2 Spring
Vous allez écrire la couche [web] de l'application. Celle-ci va communiquer avec la couche [métier] qui se trouve dans l'archive [04-
[Link]] [2]. Cette archive est déjà présente dans le dépôt local Maven de vos machines.
Pour vérifier la bonne installation de l'archive, nous allons utiliser le projet [04-ecole-metier-dao-jpa-test]. Celui-ci définit les
dépendances suivantes dans le fichier [[Link]] :
1. <dependencies>
2. <!-- JSF -->
3. <dependency>
4. <groupId>[Link]</groupId>
5. <artifactId>jsf-api</artifactId>
6. <version>2.2.6</version>
7. </dependency>
8. <dependency>
9. <groupId>[Link]</groupId>
10. <artifactId>jsf-impl</artifactId>
11. <version>2.2.6</version>
12. </dependency>
13. <!-- JSR-330 -->
14. <dependency>
15. <groupId>[Link]</groupId>
16. <artifactId>[Link]</artifactId>
17. <version>1</version>
18. </dependency>
19. <!-- Spring -->
20. <dependency>
21. <groupId>[Link]</groupId>
22. <artifactId>spring-web</artifactId>
23. <version>[Link]</version>
24. </dependency>
25. <dependency>
26. <groupId>[Link]</groupId>
27. <artifactId>04-ecole-metier-dao-jpa-with-dependencies</artifactId>
28. <version>1.0.0</version>
29. </dependency>
30. </dependencies>
• lignes 25-29 : la dépendance sur l'archive des couches [métier, dao, jpa] ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
454/489
1. Liste des classes
2. Classe[1, 6e A]
3. Classe[2, 6e B]
4. Classe[3, 5e A]
5. Classe[4, 5e B]
6. Classe[5, 4e A]
7. Classe[6, 4e B]
8. Classe[7, 3E A]
9. Classe[8, 3e B]
10. Liste des matières
11. Matière[1, Anglais]
12. Matière[2, Espagnol]
13. Matière[3, Français]
14. Matière[4, Mathématiques]
15. Matière[5, Histoire Géographie]
16. Matière[6, Sciences Naturelles]
17. Liste des élèves dans la classe n° 1
18. Elève[1, Julien, Boisseau, Classe[1, 6e A]]
19. Elève[2, Valérie, Charlet, Classe[1, 6e A]]
20. Elève[3, Gabriel, Monfort, Classe[1, 6e A]]
21. Elève[4, Emilie, Parton, Classe[1, 6e A]]
22. Liste des notes de la classe 1 dans la matière 2
23. Note[5, 10,00, Elève[2, Valérie, Charlet, Classe[1, 6e A]], Matière[2, Espagnol]]
24. Statistiques de l'élève n° 2 : Elève[2, Valérie, Charlet, Classe[1, 6e A]], [14,00 10,00 12,00 14,00 15,00 11,00 ], 12,75,
15,00, 10,00]
Application web
couche [web]
1
SGBD
4 [métier] [dao] [jpa] [Jdbc]
JSF2 Spring
Les entités [Classe, Eleve, Matiere, Note] sont définies dans le package [[Link]] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
455/489
3
La classe [AbstractEntity] est la classe parent des entités [Classe, Eleve, Matiere, Note]. Sa définition est la suivante :
1. package [Link];
2.
3. import [Link];
4.
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11.
12. @MappedSuperclass
13. public class AbstractEntity implements Serializable {
14.
15. private static final long serialVersionUID = 1L;
16. @Id
17. @GeneratedValue(strategy = [Link])
18. @Column(name = "ID")
19. protected Long id;
20. @Version
21. @Column(name = "VERSION")
22. protected Long version;
23. ...
24.
25. // getters et setters
26. ...
27.
28. }
• une instance de type [Classe] représente une ligne de la table [CLASSES] de la base de données ;
• ligne 9 : l'entité [Classe] dérive de la classe [AbstractEntity] ;
• ligne 15 : le nom de la classe ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
456/489
7. import [Link];
8.
9. @Entity
10. @Table(name = "matieres")
11. public class Matiere extends AbstractEntity implements Serializable {
12.
13. private static final long serialVersionUID = 1L;
14. // nom
15. @Column(name = "NOM")
16. private String nom;
17. // coefficient
18. @Column(name = "COEFFICIENT")
19. private int coefficient;
20.
21. // constructeurs
22. public Matiere() {
23. }
24.
25. public Matiere(String nom, int coefficient) {
26. [Link] = nom;
27. [Link] = coefficient;
28. }
29.
30. // gettes et setters
31. ...
32. // toString
33. @Override
34. public String toString() {
35. return [Link]("Matière[%s, %s]", id, nom);
36. }
37.
38. }
• une instance de type [Matiere] représente une ligne de la table [MATIERES] de la base de données ;
• ligne 11 : l'entité [Classe] dérive de la classe [AbstractEntity] ;
• ligne 16 : le nom de la matière ;
• ligne 19 : son coefficient ;
• une instance de type [Eleve] représente une ligne de la table [ELEVES] de la base de données ;
• ligne 13 : l'entité [Eleve] dérive de la classe [AbstractEntity] ;
• ligne 18 : le nom de l'élève ;
• ligne 21 : son prénom ;
• ligne 25 : sa classe ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
457/489
Enfin, l'entité [Note] est la suivante :
1. package [Link];
2.
3. import [Link];
4.
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10.
11. @Entity
12. @Table(name = "notes")
13. public class Note extends AbstractEntity implements Serializable {
14.
15. private static final long serialVersionUID = 1L;
16. // note
17. @Column(name = "NOTE")
18. private double note;
19. // matière
20. @ManyToOne
21. @JoinColumn(name = "ID_MATIERE")
22. private Matiere matiere;
23. // élève
24. @ManyToOne
25. @JoinColumn(name = "ID_ELEVE")
26. private Eleve eleve;
27.
28. // constructeurs
29. public Note() {
30. }
31.
32. public Note(double note) {
33. [Link] = note;
34. }
35.
36. // getters et setters
37. ...
38.
39. // toString
40. @Override
41. public String toString() {
42. return [Link]("Note[%s, %5.2f, %s, %s]", id, note, eleve,
43. matiere);
44. }
45.
46. }
• une instance de type [Note] représente une ligne de la table [NOTES] de la base de données ;
• ligne 13 : l'entité [Classe] dérive de la classe [AbstractEntity] ;
• ligne 18 : la note de l'élève ;
• ligne 22 : la matière concernée ;
• ligne 26 : l'élève concerné ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
458/489
20. private double min;
21.
22. // constructeurs
23. public StatsForEleve() {
24. }
25.
26. public StatsForEleve(Eleve eleve, List<Note> notes, List<Matiere> matieres) {
27. ...
28. }
29.
30. @Override
31. public String toString() {
32. if ([Link]() == 0) {
33. return [Link]("Eleve=%s, notes=[]", eleve);
34. }
35. // passage des notes en String
36. String strNotes = "";
37. for (int i = 0; i < [Link](); i++) {
38. strNotes += [Link]("%5.2f ", [Link](i).getNote());
39. }
40. return [Link]("%s, [%s], %5.2f, %5.2f, %5.2f]", eleve, strNotes, moyennePonderee, max, min);
41. }
42.
43. // getters et setters
44. ...
45. }
Un programme de test de la couche [métier] vous est donné dans le projet [04-ecole-metier-dao-jpa-test] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
459/489
25. display("Liste des élèves dans la classe n° 1", mé[Link](1L));
26. // liste des notes de la classe 1 dans la matière 2
27. display("Liste des notes de la classe 1 dans la matière 2", mé[Link](2, 1));
28. // stats de l'élève 2
29. [Link]("Statistiques de l'élève n° 2 : " + mé[Link](2));
30. }
31.
32. // méthode utilitaire - affiche les éléments d'une collection
33. static private void display(String message, Iterable<?> elements) {
34. [Link](message);
35. for (Object element : elements) {
36. [Link](element);
37. }
38. }
39. }
L'interface [IMetier] est testée aux lignes 21, 23, 25, 27 et 29. Les résultats obtenus sont les suivants :
1. . ____ _ __ _ _
2. /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
3. ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
4. \\/ ___)| |_)| | | | | || (_| | ) ) ) )
5. ' |____| .__|_| |_|_| |_\__, | / / / /
6. =========|_|==============|___/=/_/_/_/
7. :: Spring Boot :: ([Link])
8.
9. [2014-05-30 [Link].577] - 3392 WARN [main] --- [Link]: HHH015016: Encountered a
deprecated [Link] [[Link]]; use
[[Link]] instead.
10. [2014-05-30 [Link].605] - 3392 INFO [main] --- [Link]: HHH000204: Processing
PersistenceUnitInfo [
11. name: default
12. ...]
13. ...
14. Liste des classes
15. Classe[1, 6e A]
16. Classe[2, 6e B]
17. Classe[3, 5e A]
18. Classe[4, 5e B]
19. Classe[5, 4e A]
20. Classe[6, 4e B]
21. Classe[7, 3E A]
22. Classe[8, 3e B]
23. Liste des matières
24. Matière[1, Anglais]
25. Matière[2, Espagnol]
26. Matière[3, Français]
27. Matière[4, Mathématiques]
28. Matière[5, Histoire Géographie]
29. Matière[6, Sciences Naturelles]
30. Liste des élèves dans la classe n° 1
31. Elève[1, Julien, Boisseau, Classe[1, 6e A]]
32. Elève[2, Valérie, Charlet, Classe[1, 6e A]]
33. Elève[3, Gabriel, Monfort, Classe[1, 6e A]]
34. Elève[4, Emilie, Parton, Classe[1, 6e A]]
35. Liste des notes de la classe 1 dans la matière 2
36. Note[5, 10,00, Elève[2, Valérie, Charlet, Classe[1, 6e A]], Matière[2, Espagnol]]
37. Statistiques de l'élève n° 2 : Elève[2, Valérie, Charlet, Classe[1, 6e A]], [14,00 10,00 12,00 14,00 15,00 11,00 ], 12,75,
15,00, 10,00]
38. ------------------------------------------------------------------------
39. BUILD SUCCESS
40. ------------------------------------------------------------------------
41. Total time: 4.262s
42. Finished at: Fri May 30 [Link] CEST 2014
43. Final Memory: 7M/154M
A partir de maintenant, vous utiliserez cette couche [métier] sans la modifier. Pour savoir comment votre couche [web] doit appeler
la couche [métier], reportez-vous au programme console [Metier] ci-dessus. La couche [métier] peut lancer des exceptions de type
[EcoleException]. Pour simplifier les choses, dans tout ce qui suit, on se placera dans le cas où il n'y a pas d'exceptions.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
460/489
Application web
couche [web]
1
Faces Servlet
2
SGBD
4 [Link] Form [métier] [dao] [jpa] [Jdbc]
3
JSF2 Spring
Nous n'allons nous intéresser qu'aux pages JSF [[Link]] et [[Link]] ainsi qu'à leurs beans associés [[Link]] et
[[Link]]. Le reste vous est donné et est expliqué dans ce qui suit. Attendez les questions avant de faire quoique ce soit.
Le projet web est un projet Maven configuré par le fichier [[Link]]. Les dépendances du projet sont les suivantes :
1. <dependencies>
2. <!-- JSF -->
3. <dependency>
4. <groupId>[Link]</groupId>
5. <artifactId>jsf-api</artifactId>
6. <version>2.2.6</version>
7. </dependency>
8. <dependency>
9. <groupId>[Link]</groupId>
10. <artifactId>jsf-impl</artifactId>
11. <version>2.2.6</version>
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
461/489
12. </dependency>
13. <!-- JSR-330 -->
14. <dependency>
15. <groupId>[Link]</groupId>
16. <artifactId>[Link]</artifactId>
17. <version>1</version>
18. </dependency>
19. <!-- Spring -->
20. <dependency>
21. <groupId>[Link]</groupId>
22. <artifactId>spring-web</artifactId>
23. <version>[Link]</version>
24. </dependency>
25. <dependency>
26. <groupId>${[Link]}</groupId>
27. <artifactId>04-ecole-metier-dao-jpa</artifactId>
28. <version>0.0.1-SNAPSHOT</version>
29. </dependency>
30. </dependencies>
Les lignes 25-29 créent une dépendance sur le projet Netbeans des couches [métier, dao, jpa]. Ainsi la couche [web] a-t-elle accès à
la couche [métier] que nous venons de décrire.
• lignes 14-18 : définissent la servlet qui va gérer les différentes URL de l'application. Cette servlet est celle du framework
JSF ;
• lignes 19-22 : définissent les URL traitées par l'application. Ce sont toutes les URL de la forme [/faces/*] ;
• ligne 29 : définit la page d'accueil de l'application. Ce sera la page JSF [[Link]] ;
• lignes 4-8 : définissent un listener. Un listener est une classe respectant certaines spécifications et qui est chargée lorsque
démarre l'application web. Ici ce code est une classe de Spring permettant de gérer les beans JSF comme des beans de
Spring ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
462/489
Le fichier [[Link]] configure les beans de l'application JSF :
1. <?xml version='1.0' encoding='UTF-8'?>
2. <faces-config version="2.2"
3. xmlns="[Link]
4. xmlns:xsi="[Link]
5. xsi:schemaLocation="[Link] [Link]
facesconfig_2_2.xsd">
6. <application>
7. <!-- pour que Spring puisse gérer les beans JSF -->
8. <el-resolver>[Link]</el-resolver>
9. </application>
10. </faces-config>
Nous avons vu que le fichier [[Link]] était configuré pour qu'un listener Spring soit chargé en même temps que la servlet JSF. Le
fichier [[Link]] délègue à ce listener la gestion des beans JSF.
Le bean [ApplicationModel] est un bean de portée [application]. Il n'existe qu'en un seul exemplaire et est accessible en lecture par
toutes les requêtes de tous les utilisateurs. Son code est le suivant :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11.
12. @Named
13. @Scope("application")
14. public class ApplicationModel {
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
463/489
15. // la couche [métier]
16. @Autowired
17. private IMetier métier;
18.
19. // les classes
20. private List<Classe> classes;
21. // les matières
22. private List<Matiere> matières;
23.
24. @PostConstruct
25. public void init(){
26. // on initialise classes et matières
27. classes=mé[Link]();
28. matières=mé[Link]();
29. }
30. // getters et setters
31. ...
32. }
• lignes 12-13 : ces deux annotations font de la classe [ApplicationModel] un bean JSF de portée [application] ;
• lignes 16-17 : la référence sur la couche [métier] sera injectée par Spring dans ce champ juste après la construction de
l'instance ;
• ligne 24 : désigne une méthode qui doit être exécutée juste après l'instanciation du bean ;
• lignes 27-28 : la liste des classes et la liste des matières sont demandées à la couche [métier]. Leurs références sont stockées
dans les champs privés des lignes 20 et 22 ;
On retiendra que lorsqu'un bean de la couche JSF a besoin de la liste des classes, de la liste des matières ou d'une référence sur la
couche [métier], il les trouvera dans le bean [ApplicationModel].
Le bean [SessionModel] est un bean JSF de portée [session], c'est à dire un bean accessible en lecture et écriture à toutes les requêtes
d'un même utilisateur. C'est la mémoire de l'utilisateur. Son code est le suivant :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7.
8. @Named
9. @Scope("session")
10. public class SessionModel {
11. // liste des élèves d'une classe
12. private List<Eleve> élèves;
13.
14. // getters et setters
15. ...
16. }
L'application web doit être configurée pour s'exécuter sur un serveur Tomcat.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
464/489
7.8 La vue [[Link]]
La vue [[Link]] est la vue affichée lorsqu'on lance l'application web (cf [[Link]]). Son code est le suivant :
1. <?xml version='1.0' encoding='UTF-8' ?>
2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[Link]
3. <html xmlns="[Link]
4. xmlns:h="[Link]
5. xmlns:f="[Link]
6. xmlns:ui="[Link]
7. <h:head>
8. <title>CC3 Java - EI5 AGI 1415</title>
9. </h:head>
10. <h:body>
11. <h2>CC3 Java - EI5 AGI 1415</h2>
12. <h3>Liste des classes</h3>
13. <ul>
14. <ui:repeat var="classe" value="#{[Link]}">
15. <li>#{[Link]}</li>
16. </ui:repeat>
17. </ul>
18. </h:body>
19. </html>
• les lignes 14-16 parcourent la liste des classes pour afficher leur nom. Cette liste est donnée par l'expression JSF
#{[Link]}. Cette expression est traduite par [Exo01].getClasses() où [Exo01] est le bean JSF suivant :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10.
11. @Named
12. @Scope("request")
13. public class Exo01 {
14.
15. // le bean de portée [application]
16. @Autowired
17. private ApplicationModel application;
18. // le bean de portée [session]
19. @Autowired
20. private SessionModel session;
21. // les valeurs postées
22. // id de la classe sélectionnée
23. private String idClasse;
24. // id de l'élève sélectionné
25. private String idEleve;
26. // statistiques de l'élève
27. private StatsForEleve stats;
28.
29. // -------------------------------------méthodes
30.
31. // liste des classes
32. public List<Classe> getClasses(){
33. return [Link]();
34. }
35.
36.
37. // getters et setters
38. ...
39. }
• lignes 11-12 : ces deux annotations font du bean [Exo01], un bean JSF de portée [request] : le bean est instancié au début
d'une requête et détruit à la fin de celle-ci. On ne peut donc y mémoriser quelque chose pour les requêtes suivantes. Il faut
utiliser le bean [SessionModel] pour cela ;
• lignes 16-17 : la référence du bean [ApplicationModel] est injectée ici ;
• lignes 19-20 : la référence du bean [SessionModel] est injectée ici ;
• lignes 32-34 : définissent la méthode [getClasses] utilisée par la vue [[Link]]. On voit que cette méthode rend bien la
liste des classes qu'elle va chercher dans le bean [ApplicationModel].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
465/489
Travail n° 2 : lancez l'application web pour afficher la vue [[Link]].
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
466/489
1
2
Question 1 : complétez la vue [[Link]] et son bean [[Link]] pour obtenir le fonctionnement des vues 1 à 3 ci-dessus.
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
467/489
La vue JSF [[Link]] doit générer les vues web suivantes :
1
2
Question 2 : Complétez la vue [[Link]] et son bean [[Link]] pour obtenir le fonctionnement des vues 1 à 2 ci-dessus.
Note : L'écart type d'une série de N notes Notei est donné par la formule : racine carrée de (S2/N – S12/N2) avec :
S2 =Note12 + Note22 + ... + NoteN2
S1 = Note1 + Note2 + ... + NoteN
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
468/489
8 Étude de cas n° 8 : gestion d'élections
8.2 Le problème
On désire écrire un programme qui, au soir d'élections, puisse calculer le nombre de sièges obtenus par les différentes listes en
présence. On trouvera un peu plus loin, le mode de calcul des sièges pour une élection proportionnelle à la plus forte moyenne, tel
qu'expliqué dans un article du journal Ouest-France (OF) du 15 mars 1986 :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
469/489
8.3 L'architecture de l'application
L'application à écrire a l'architecture en couches suivante :
Serveur7Tomcat 7
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
470/489
8.4 La base de données
Le script de génération de la base de données de l'application vous est donné [1] :
Exécutez ce script avec [PhpMyAdmin]. On obtient la base MySQL [dbelections2] [2] ayant les deux tables suivantes :
La table [conf] n'a qu'une ligne qui rassemble des éléments de configuration de l'élection :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
471/489
• [nom] : nom de la liste - unique
• [voix] : le nombre de voix qu'elle a obtenues ;
• [sieges] : le nombre de sièges qu'elle a obtenus ;
• [elimine] : à 1 si la liste est en-dessous du seuil électoral, à 0 sinon ;
Lorsque l'application démarre, seuls les champs [id], [version] et [nom] sont significatifs. Pour les autres ;
• le champ [voix] est renseigné par saisie de l'utilisateur ;
• les champs [sieges] et [elimine] sont renseignés par calcul de l'application ;
Serveur7Tomcat 7
L'implémentation des couches [métier, DAO, JPA] vous est donnée ainsi qu'un programme de test de celle-ci :
étape 1 [1]
Il vous faut tout d'abord mettre l'archive [[Link]] dans le dépôt Maven local. Ceci est fait
par le script [[Link]]. Double-cliquez dessus pour l'exécuter.
Note : vous aurez probablement à changer le chemin du dossier Maven dans le script d'installation.
étape 2 [2]
Ouvrez le projet Netbeans [elections-metier-tests] et exécutez le test [JUnitMetier]. Le test doit réussir. Cela veut dire que :
• la base de données est correctement installée ;
• l'archive [[Link]] est correctement installée dans le dépôt Maven local ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
472/489
Couche
[web / Métier BD
JSF]
Serveur7Tomcat 7
Le bloc [Métier] est désormais une boîte noire dont vous devez connaître l'interface pour l'exploiter. Cette interface est la suivante :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5.
6. import [Link];
7.
8. public interface IMetier {
9.
10. // configuration de l'élection en BD
11. public ElectionsConfig getElectionsConfig();
12. // les listes en compétition de la BD
13. public List<ListeElectorale> getListesElectorales();
14. // sauvegarde en BD des listes en compétition
15. public void setListesElectorales(List<ListeElectorale> listesElectorales);
16. // calcul des sièges obtenus
17. public void calculerSieges(List<ListeElectorale> listesElectorales);
18. }
La classe [ElectionsConfig] encapsule l'unique ligne de la table [conf] de la base de données. Elle dérive de la classe [ AbstractEntity]
suivante :
1. package [Link];
2.
3. import [Link].*;
4. import [Link];
5.
6. @MappedSuperclass
7. public class AbstractEntity implements Serializable {
8.
9. private static final long serialVersionUID = 1L;
10. @Id
11. @GeneratedValue(strategy = [Link])
12. protected Long id;
13. @Version
14. protected Long version;
15.
16. // getters et setters
17. ...
18. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
473/489
10.
11. // le nom de la liste
12. @Column(name = "nom")
13. private String nom;
14. // son nombre de voix
15. @Column(name = "voix")
16. private long voix;
17. // son nombre de sièges
18. @Column(name = "sieges")
19. private int sieges;
20. // son statut éliminé ou non
21. @Column(name = "elimine")
22. private boolean elimine;
23.
24. // constructeurs
25. public ListeElectorale() {
26.
27. }
28.
29. public ListeElectorale(String nom, int voix, int sieges, boolean elimine) {
30. setNom(nom);
31. setVoix(voix);
32. setSieges(sieges);
33. setElimine(elimine);
34. }
35.
36. // getters et setters
37. ...
38.
39. public String toString() {
40. return "[" + id + "," + nom + "," + voix + "," + sieges + "," + elimine + "]";
41. }
42. }
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
474/489
63. [Link](0).setVoix(32000);
64. [Link](1).setVoix(25000);
65. [Link](2).setVoix(16000);
66. [Link](3).setVoix(12000);
67. [Link](4).setVoix(8000);
68. [Link](5).setVoix(4500);
69. [Link](6).setVoix(2500);
70. // on calcule les sièges obtenus par chacune des listes
71. mé[Link](listes);
72. // on vérifie les résultats
73. [Link](2, [Link](0).getSieges());
74. [Link]([Link](0).isElimine());
75. [Link](2, [Link](1).getSieges());
76. [Link]([Link](1).isElimine());
77. [Link](1, [Link](2).getSieges());
78. [Link]([Link](2).isElimine());
79. [Link](1, [Link](3).getSieges());
80. [Link]([Link](3).isElimine());
81. [Link](0, [Link](4).getSieges());
82. [Link]([Link](4).isElimine());
83. [Link](0, [Link](5).getSieges());
84. [Link]([Link](5).isElimine());
85. [Link](0, [Link](6).getSieges());
86. [Link]([Link](6).isElimine());
87. }
88.
89. /**
90. * vérification 3 méthode de calcul des sièges on provoque une exception
91. */
92. @Test
93. public void calculSieges3() {
94. // on crée un tableau de 24 listes candidates avec chacune 1 voix
95. ListeElectorale[] listes = new ListeElectorale[25];
96. // les 25 listes auront le même nombre de voix (4%)
97. for (int i = 0; i < [Link]; i++) {
98. listes[i] = new ListeElectorale("Liste" + (i + 1), 1, 0, false);
99. }
100. // calcul des sièges - normalement on doit avoir une ElectionsException
101. // avec un seuil électoral de 5%
102. boolean erreur = false;
103. try {
104. mé[Link]([Link](listes));
105. } catch (ElectionsException ex) {
106. [Link](ex);
107. erreur = true;
108. }
109. // on vérifie les résultats
110. [Link](erreur);
111. }
112.
113. /**
114. * enregistrement des résultats de l'élection
115. */
116. @Test
117. public void ecritureResultatsElections() {
118. // on demande les listes candidates
119. List<ListeElectorale> listes = mé[Link]();
120. // on fixe en dur les voix
121. [Link](0).setVoix(32000);
122. [Link](1).setVoix(25000);
123. [Link](2).setVoix(16000);
124. [Link](3).setVoix(12000);
125. [Link](4).setVoix(8000);
126. [Link](5).setVoix(4500);
127. [Link](6).setVoix(2500);
128. // on calcule les sièges obtenus par chacune des listes
129. mé[Link](listes);
130. // on affiche les résultats
131. for (int i = 0; i < [Link](); i++) {
132. [Link]([Link](i));
133. }
134. // on enregistre les résultats dans la base de données
135. mé[Link](listes);
136. // on vérifie les résultats
137. listes = mé[Link]();
138. // on affiche les résultats
139. for (int i = 0; i < [Link](); i++) {
140. [Link]([Link](i));
141. }
142. // on vérifie les résultats
143. [Link](2, [Link](0).getSieges());
144. [Link]([Link](0).isElimine());
145. [Link](2, [Link](1).getSieges());
146. [Link]([Link](1).isElimine());
147. [Link](1, [Link](2).getSieges());
148. [Link]([Link](2).isElimine());
149. [Link](1, [Link](3).getSieges());
150. [Link]([Link](3).isElimine());
151. [Link](0, [Link](4).getSieges());
152. [Link]([Link](4).isElimine());
153. [Link](0, [Link](5).getSieges());
154. [Link]([Link](5).isElimine());
155. [Link](0, [Link](6).getSieges());
156. [Link]([Link](6).isElimine());
157. }
158. }
• ligne 22 : injection d'une référence sur la couche [métier, DAO, JPA] de type [IMetier] ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
475/489
• ligne 36 : la méthode [[Link]] transforme un tableau en liste ;
Couche
[web / Métier BD
JSF]
Serveur7Tomcat 7
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
476/489
L'application [1] n'a qu'une page JSF [[Link]] associée au bean [[Link]]. Votre travail est de compléter ces deux éléments.
Les autres éléments du projet ne doivent pas être modifiés. Bien que l'application soit une application web, elle s'exécute comme
une application console [2-3] :
• en [4], on voit qu'un serveur Tomcat a été lancé. C'est lui qui exécute l'application web ;
Si vous obtenez cette page et qu'il n'y a pas d'exception dans la console [4], alors l'application est correctement installée. En effet, au
démarrage, le bean [Application] de portée [application] est instancié :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6.
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
12.
13. @Named
14. @Scope("application")
15. public class Application implements IMetier {
16.
17. // injections
18. @Autowired
19. private IMetier métier;
20.
21. // modèle
22. private ElectionsConfig electionsConfig;
23.
24. // initialisation
25. @PostConstruct
26. public void init() {
27. electionsConfig = mé[Link]();
28. }
29.
30. // implémentation couche [métier]
31.
32. @Override
33. public ElectionsConfig getElectionsConfig() {
34. return electionsConfig;
35. }
36.
37. @Override
38. public List<ListeElectorale> getListesElectorales() {
39. return mé[Link]();
40. }
41.
42. @Override
43. public void setListesElectorales(List<ListeElectorale> listes) {
44. mé[Link](listes);
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
477/489
45. }
46.
47. @Override
48. public void calculerSieges(List<ListeElectorale> listes) {
49. mé[Link](listes);
50. }
51. }
• lignes 18-19 : une référence sur la couche [métier - DAO - JPA] est injectée ;
• lignes 25-28 : une fois le constructeur exécuté et l'injection faite, la méthode annotée [@PostConstruct] est exécutée. On
voit qu'elle demande une instance [ElectionsConfig] à la couche [métier - DAO - JPA]. On sait que cela consiste à
récupérer l'unique ligne de la table [conf] de la BD. Si donc, il n'y a pas eu d'exception, alors cela veut dire que cette ligne a
bien été récupérée et que donc que l'application est correctement configurée ;
• ligne 15 : la classe [Application] implémente l'interface [IMetier]. Le bean [Exo01] utilisera les méthodes exposées par le
bean [Application]. On rappelle qu'un bean de portée [application] est présent en mémoire pendant toute la durée de vie
de l'application ;
Nous allons construire l'application étape par étape. A chaque fois, il s'agit de modifier le fichier JSF [[Link]] et le bean
[Exo01]. Pour tester les modifications faites, arrêtez l'exécution courante [1], puis relancez-la [2] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
478/489
8.6.2 Étape 1
Modifiez l'application pour obtenir la page suivante au démarrage :
8.6.3 Étape 2
Modifiez l'application pour obtenir la page suivante au démarrage :
• en [1], la liste déroulante a été remplie avec les noms des listes ;
Conseils :
• utilisez une source de type List<ListeElectorale> pour alimenter la liste [1] ;
8.6.4 Étape 3
Modifiez l'application pour qu'on puisse sélectionner les listes qui participent au calcul des résultats de l'élection :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
479/489
2
3
1
4
Conseils :
• utilisez une source de type List<ListeElectorale> pour alimenter la liste [2] ;
• lorsqu'on clique sur un lien de la page, toutes les saisies de la page sont postées au serveur. Pensez à prévoir les
champs du bean [Exo01] pour accueillir ces valeurs postées. Pour des raisons trop longues à expliquer ici, donner le type
[String] à ces champs évite bien des soucis ;
• lorsqu'on a vidé la liste [4] et qu'on clique sur le bouton [Ajouter], l'application ne doit pas planter. Deux solutions sont
possibles :
1. cacher le lien [Ajouter] lorsque la liste [4] est vide en utilisant l'attribut [rendered]. C'est la solution préconisée ;
2. prévoir le cas où aucune valeur n'est postée pour la liste [4]. C'est le cas lorsque la liste est vide. Il y a une
difficulté ici, car comme le bean [Exo01] est de portée [session], les valeurs saisies lors du traitement précédent
restent pour le traitement courant. S'il n'y a pas de valeur postée, le champ du bean [Exo01] concerné par le
POST garde sa valeur précédente. Pour reconnaître l'absence de valeur postée, on peut remettre à null tous les
champs des valeurs postées après chaque traitement de n'importe quel lien. Si lors du traitement suivant, on
trouve une valeur postée restée à null, c'est qu'il n'y a pas eu de POST pour cette valeur ;
8.6.5 Étape 4
Modifiez l'application pour qu'elle vérifie la validité des saisies :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
480/489
Conseils :
• n'utilisez pas un validateur du type <f:validateLongRange> car alors il serait utilisé quelque soit le lien cliqué (il va y avoir
d'autres liens). Or cette validation est propre à l'ajout de listes. Construisez le message d'erreur dans la méthode qui traite
le lien [Ajouter] ;
8.6.6 Étape 5
Modifiez l'application pour qu'on puisse retirer une liste qu'on a ajoutée dans la liste des participants à l'élection :
2
1
3 4
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
481/489
• l'élement sélectionné en [1] quitte la liste [1] pour la liste [2] (cf [3]) ;
• lorsque la liste [1] est vide, le lien [Supprimer] disparaît [4] ;
Conseils :
• dans le texte du lien [<-- Supprimer], l'écriture du caractère < pose problème car c'est un caractère réservé du XML. Il y a
moyen de résoudre ce problème mais vous n'essaierez pas. N'écrivez donc pas ce caractère ;
• lorsqu'on clique sur le lien [Supprimer] alors qu'aucune liste n'a été sélectionnée pour la suppression, l'application ne doit
pas planter. On est de nouveau dans le cas où la liste [1] ne poste pas de valeur. On peut alors utiliser la solution de la mise
à null de toutes les valeurs postées préconisée page 480 ;
8.6.7 Étape 6
Modifiez l'application pour qu'on puisse lancer le calcul des résultats de l'élection :
1
3
2 4
Conseils :
• contrôlez l'affichage des liens avec l'attribut [rendered] ;
• utilisez une source de type [List<ListeElectorale>] pour alimenter la liste [3] ;
8.6.8 Étape 7
Implémentez le traitement des liens [Effacer] et [Enregistrer] :
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
482/489
2
1
• en [1], on clique sur le lien [Enregistrer]. Les résultats sont enregistrés en base de données. Vérifiez-le ;
• en [2], les résultats sont effacés. Seul reste le lien [Calculer] qui permet de relancer le calcul ;
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
483/489
Table of Contents
1 LE COURS ET L'ÉTUDE DE CAS......................................................................................................................................10
1.1 ARCHITECTURE D'UNE APPLICATION JAVA EN COUCHES.................................................................................................................10
1.2 LES OUTILS DU DOCUMENT.........................................................................................................................................................13
1.2.1 MAVEN..................................................................................................................................................................................13
[Link] Introduction......................................................................................................................................................................13
[Link] Exécution du projet..........................................................................................................................................................16
[Link] Le système de fichiers d'un projet Maven.......................................................................................................................18
[Link] Le dépôt Maven local......................................................................................................................................................18
[Link] Chercher un artifact avec Maven.....................................................................................................................................19
1.3 JPA EN RÉSUMÉ........................................................................................................................................................................24
1.3.1 LA PLACE DE JPA DANS UNE ARCHITECTURE EN COUCHES.............................................................................................................24
1.3.2 JPA – EXEMPLES.....................................................................................................................................................................24
[Link] Exemple 1 - Représentation objet d'une table unique.....................................................................................................24
[Link].1 La table [personne].......................................................................................................................................................24
[Link].2 L'entité [Personne]........................................................................................................................................................25
[Link] Configuration de la couche JPA.......................................................................................................................................27
[Link] Exemple 2 : relation un-à-plusieurs.................................................................................................................................30
[Link].1 Le schéma de la base de données.................................................................................................................................30
[Link].2 Les objets @Entity représentant la base de données....................................................................................................30
1.3.3 L'API DE LA COUCHE JPA.......................................................................................................................................................32
1.3.4 LES REQUÊTES JPQL..............................................................................................................................................................35
[Link] La table [MEDECINS]....................................................................................................................................................35
[Link] La table [CLIENTS]........................................................................................................................................................35
[Link] La table [CRENEAUX]...................................................................................................................................................36
[Link] La table [RV]...................................................................................................................................................................36
[Link] Génération de la base.......................................................................................................................................................37
[Link] La couche [JPA]...............................................................................................................................................................38
[Link] Le projet Netbeans...........................................................................................................................................................39
[Link] Génération de la couche [JPA].........................................................................................................................................39
[Link].1 Création d'une connexion Netbeans à la base de données............................................................................................39
[Link].2 Création d'une unité de persistance..............................................................................................................................40
[Link].3 Génération des entités JPA............................................................................................................................................43
[Link].4 Les entités JPA générées...............................................................................................................................................45
[Link] Le code d'accès aux données...........................................................................................................................................52
1.3.5 LIENS ENTRE CONTEXTE DE PERSISTANCE ET SGBD.....................................................................................................................54
[Link] L'architecture de l'application..........................................................................................................................................54
[Link] La base de données..........................................................................................................................................................54
[Link] Le projet Maven [mv-personnes]....................................................................................................................................55
[Link] Configuration Maven.......................................................................................................................................................55
[Link] Configuration de la couche [jpa].....................................................................................................................................55
[Link] L'entité JPA [Personne]....................................................................................................................................................56
[Link] Création de la table [PERSONNES]................................................................................................................................57
[Link] Tests des liens entre contexte de persistance et base de données....................................................................................58
[Link].1 La méthode [test1]........................................................................................................................................................59
[Link].2 La méthode [test2]........................................................................................................................................................61
1.4 VERSION 1 : ARCHITECTURE SPRING / JPA................................................................................................................................63
1.4.1 LA BASE DE DONNÉES..............................................................................................................................................................63
1.4.2 MODE DE CALCUL DU SALAIRE D'UNE ASSISTANTE MATERNELLE......................................................................................................64
1.4.3 FONCTIONNEMENT DE L'APPLICATION CONSOLE.............................................................................................................................65
1.4.4 FONCTIONNEMENT DE L'APPLICATION GRAPHIQUE..........................................................................................................................66
1.4.5 CRÉATION DE LA BASE DE DONNÉES............................................................................................................................................66
1.4.6 IMPLÉMENTATION JPA.............................................................................................................................................................68
[Link] Couche JPA / Hibernate...................................................................................................................................................68
[Link].1 Configuration de la couche JPA....................................................................................................................................69
[Link].2 Les dépendances...........................................................................................................................................................72
[Link].3 Les entités JPA..............................................................................................................................................................73
[Link].4 Le code de la classe principale.....................................................................................................................................74
[Link].5 Tests..............................................................................................................................................................................75
[Link] Couche JPA / EclipseLink................................................................................................................................................79
[Link] Travail à faire...................................................................................................................................................................85
[Link] Lazy ou Eager ?...............................................................................................................................................................85
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
484/489
[Link] Travail à faire...................................................................................................................................................................89
1.4.7 LE PROJET MAVEN [MV-PAM-SPRING-HIBERNATE].........................................................................................................................95
[Link] Configuration Maven.......................................................................................................................................................95
[Link] La couche [JPA]...............................................................................................................................................................96
[Link] Les interfaces des couches [métier] et [DAO]...............................................................................................................100
[Link] La classe [PamException]..............................................................................................................................................106
1.4.8 LA COUCHE [DAO] DE L'APPLICATION [PAM].........................................................................................................................108
[Link] Implémentation..............................................................................................................................................................108
[Link] Configuration Spring / JPA............................................................................................................................................109
[Link] Tests...............................................................................................................................................................................110
[Link].1 JUnitInitDB.................................................................................................................................................................110
[Link].2 Mise en oeuvre des tests.............................................................................................................................................112
[Link].3 JUnitDao.....................................................................................................................................................................114
1.4.9 LA COUCHE [MÉTIER] DE L'APPLICATION [PAM]........................................................................................................................118
[Link] L'interface Java [IMetier]...............................................................................................................................................118
[Link] La classe [FeuilleSalaire]...............................................................................................................................................118
[Link] La classe d'implémentation [Metier] de la couche [metier]...........................................................................................119
[Link] Tests de la couche [metier]............................................................................................................................................120
1.4.10 LA COUCHE [UI] DE L'APPLICATION [PAM] – VERSION CONSOLE................................................................................................123
[Link] La classe [[Link]].........................................................................................................................................123
[Link] Exécution.....................................................................................................................................................................124
1.4.11 LA COUCHE [UI] DE L'APPLICATION [PAM] – VERSION GRAPHIQUE.............................................................................................126
[Link] Un rapide tutoriel.........................................................................................................................................................127
[Link] L'interface graphique [PamJFrame].............................................................................................................................129
[Link] Les événements de l'interface graphique.....................................................................................................................130
[Link] Initialisation de l'interface graphique...........................................................................................................................131
[Link] Gestionnaires d'événements.........................................................................................................................................133
[Link] Exécution de l'interface graphique...............................................................................................................................133
1.4.12 IMPLÉMENTATION DE LA COUCHE JPA AVEC ECLIPSELINK.........................................................................................................134
[Link] Le projet Netbeans.......................................................................................................................................................134
[Link] Mise en oeuvre des tests..............................................................................................................................................137
[Link].1 JUnitDao...................................................................................................................................................................138
[Link] Les autres tests.............................................................................................................................................................138
[Link] Travail à faire...............................................................................................................................................................138
1.5 VERSION 2 : ARCHITECTURE OPENEJB / JPA.........................................................................................................................139
1.5.1 INTRODUCTION AUX PRINCIPES DU PORTAGE...............................................................................................................................139
[Link] Les nouvelles architectures............................................................................................................................................139
[Link] Les bibliothèques des projets.........................................................................................................................................139
[Link] Configuration de la couche JPA / EclipseLink / OpenEJB............................................................................................139
[Link] Implémentation de la couche [DAO] par des EJB.........................................................................................................140
[Link] Implémentation de la couche [metier] par un EJB........................................................................................................142
[Link] Les clients des EJB........................................................................................................................................................142
1.5.2 TRAVAIL PRATIQUE.................................................................................................................................................................143
[Link] Configuration initiale du projet Netbeans [mv-pam-openejb-eclipselink]....................................................................144
[Link] Portage de la couche [DAO]..........................................................................................................................................146
[Link].1 L'EJB [CotisationDao]................................................................................................................................................147
[Link].2 Les EJB [EmployeDao] et [IndemniteDao]................................................................................................................147
[Link].3 La classe [PamException]...........................................................................................................................................148
[Link].4 Entités sérialisables.....................................................................................................................................................148
[Link].5 Tests de la couche [DAO]...........................................................................................................................................149
[Link] Portage de la couche [metier]........................................................................................................................................154
[Link].1 L'EJB [Metier]............................................................................................................................................................154
[Link].2 Tests de la couche [metier].........................................................................................................................................155
[Link] Portage de la couche [console]......................................................................................................................................156
[Link] Portage de la couche [swing].........................................................................................................................................159
1.5.3 CONCLUSION........................................................................................................................................................................159
1.6 VERSION 3 : PORTAGE DE L'APPLICATION PAM SUR UN SERVEUR D'APPLICATIONS GLASSFISH......................................................160
1.6.1 LA PARTIE SERVEUR DE L'APPLICATION CLIENT / SERVEUR PAM...................................................................................................160
[Link] L'architecture de l'application........................................................................................................................................160
[Link].1 Le projet Netbeans......................................................................................................................................................161
[Link].2 Configuration Maven..................................................................................................................................................162
[Link].3 Configuration de la couche de persistance.................................................................................................................164
[Link].4 Insertion des couches [JPA, DAO, metier].................................................................................................................168
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
485/489
[Link].5 Déploiement de l'application sur le serveur Glassfish................................................................................................168
[Link] Client console - version 1..............................................................................................................................................170
[Link] Client console - version 2..............................................................................................................................................175
[Link] Le client Swing..............................................................................................................................................................179
[Link] Portage des tests unitaires..............................................................................................................................................179
1.7 VERSION 4 – CLIENT / SERVEUR DANS UNE ARCHITECTURE DE SERVICE WEB SOAP......................................................................183
1.7.1 SERVICE WEB SOAP IMPLÉMENTÉ PAR UN EJB........................................................................................................................184
[Link] La partie serveur............................................................................................................................................................184
[Link] La partie cliente.............................................................................................................................................................187
[Link].1 Le projet Netbeans du client console..........................................................................................................................187
[Link].2 Le client console du service web Metier....................................................................................................................189
[Link].3 Le client swing du service web Metier.......................................................................................................................191
1.7.2 SERVICE WEB SOAP IMPLÉMENTÉ PAR UNE APPLICATION WEB.....................................................................................................192
[Link] La partie serveur............................................................................................................................................................192
[Link] La partie cliente.............................................................................................................................................................196
1.7.3 SERVICE WEB SOAP IMPLÉMENTÉ AVEC SPRING, HIBERNATE ET TOMCAT.....................................................................................196
[Link] La partie serveur............................................................................................................................................................197
[Link] La partie cliente.............................................................................................................................................................202
1.7.4 SERVICE WEB SOAP IMPLÉMENTÉ AVEC SPRING, ECLIPSELINK ET TOMCAT..................................................................................204
1.7.5 CONCLUSION........................................................................................................................................................................210
1.8 INTRODUCTION À JAVA SERVER FACES......................................................................................................................................212
1.9 VERSION 5 - APPLICATION PAM WEB / JSF...........................................................................................................................213
1.9.1 ARCHITECTURE DE L'APPLICATION............................................................................................................................................213
1.9.2 FONCTIONNEMENT DE L'APPLICATION........................................................................................................................................215
1.9.3 LE PROJET NETBEANS............................................................................................................................................................216
[Link] Les fichiers de configuration.........................................................................................................................................221
[Link] La feuille de style...........................................................................................................................................................223
[Link] Le fichier des messages.................................................................................................................................................223
[Link] La portée des beans........................................................................................................................................................224
[Link] La couche [métier].........................................................................................................................................................224
1.9.4 LE FORMULAIRE [[Link]] ET SON MODÈLE [[Link]]....................................................................................................225
[Link] étape 1............................................................................................................................................................................226
[Link] étape 2............................................................................................................................................................................226
[Link] étape 3............................................................................................................................................................................227
[Link] étape 4............................................................................................................................................................................228
1.10 VERSION 6 - INTÉGRATION DE LA COUCHE WEB DANS UNE ARCHITECTURE 3 COUCHES JSF / EJB...............................................229
1.10.1 ARCHITECTURE DE L'APPLICATION..........................................................................................................................................229
1.10.2 LE PROJET NETBEANS DE LA COUCHE WEB..............................................................................................................................229
1.10.3 LE PROJET NETBEANS DE L'APPLICATION D'ENTREPRISE.............................................................................................................231
1.11 VERSION 7 - APPLICATION WEB PAM MULTI-VUES / MULTI-PAGES............................................................................................235
1.11.1 LES VUES DE L'APPLICATION..................................................................................................................................................236
1.11.2 LE PROJET NETBEANS DE LA COUCHE [WEB]............................................................................................................................237
[Link] Les fichiers de configuration.......................................................................................................................................238
[Link] La feuille de style.........................................................................................................................................................238
[Link] Le fichier des messages...............................................................................................................................................239
[Link] La couche [métier].......................................................................................................................................................240
1.11.3 LES BEANS DE L'APPLICATION................................................................................................................................................241
[Link] Le bean ApplicationData..............................................................................................................................................241
[Link] Le bean SessionData....................................................................................................................................................242
[Link] Le bean Form...............................................................................................................................................................243
1.11.4 LES PAGES DE L'APPLICATION.................................................................................................................................................244
[Link] [[Link]]...............................................................................................................................................................244
[Link] L'entête [[Link]].................................................................................................................................................245
1.11.5 LES CAS D'UTILISATION DE L'APPLICATION................................................................................................................................245
[Link] Affichage de la page d'accueil.....................................................................................................................................245
[Link] L'action [faireSimulation]............................................................................................................................................246
[Link] La gestion des erreurs..................................................................................................................................................248
[Link] L'action [effacerSimulation]........................................................................................................................................250
[Link] L'action [enregistrerSimulation]..................................................................................................................................251
[Link] L'action [retourSimulateur]..........................................................................................................................................255
[Link] L'action [voirSimulations]...........................................................................................................................................255
[Link] L'action [retirerSimulation]..........................................................................................................................................256
[Link] L'action [terminerSession]...........................................................................................................................................257
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
486/489
1.11.6 INTÉGRATION DE LA COUCHE WEB DANS UNE ARCHITECTURE 3 COUCHES JSF / EJB.....................................................................258
1.12 VERSIONS 8 : PORTAGES DE L'APPLICATION JSF MULTIPAGES..................................................................................................260
1.12.1 ENVIRONNEMENT SPRING / HIBERNATE / TOMCAT....................................................................................................................260
1.12.2 ENVIRONNEMENT SPRING / ECLIPSELINK / TOMCAT.................................................................................................................267
1.12.3 ENVIRONNEMENT SPRING / ECLIPSELINK / GLASSFISH..............................................................................................................267
[Link] Etape 1.........................................................................................................................................................................268
[Link] Etape 2.........................................................................................................................................................................271
[Link] Exécution.....................................................................................................................................................................274
1.13 VERSION 9 : IMPLÉMENTATION DE LA COUCHE WEB AVEC PRIMEFACES......................................................................................276
2 ÉTUDE DE CAS N° 2 : CLIENT / SERVEUR - EJB / SOAP..........................................................................................280
2.1 LES COMPÉTENCES MISES EN OEUVRE........................................................................................................................................280
2.2 LE PROBLÈME.........................................................................................................................................................................280
2.3 LES ÉLÉMENTS DE L'APPLICATION SPRING / HIBERNATE.............................................................................................................280
2.3.1 LA BASE DE DONNÉES............................................................................................................................................................281
[Link] La table [MEDECINS]..................................................................................................................................................281
[Link] La table [CLIENTS]......................................................................................................................................................281
[Link] La table [CRENEAUX].................................................................................................................................................282
[Link] La table [RV].................................................................................................................................................................282
2.3.2 L'ARCHITECTURE DE L'APPLICATION SPRING / HIBERNATE............................................................................................................283
2.3.3 LE PROJET NETBEANS DE L'APPLICATION SPRING / HIBERNATE.....................................................................................................283
2.4 TESTS DE L'APPLICATION SPRING / HIBERNATE..........................................................................................................................284
2.5 CRÉATION D'UN SERVEUR EJB.................................................................................................................................................285
2.6 CRÉATION D'UN CLIENT POUR LE SERVEUR EJB........................................................................................................................287
2.7 CRÉATION D'UN SERVICE WEB SOAP.......................................................................................................................................287
2.8 CRÉATION D'UN CLIENT POUR LE SERVICE WEB SOAP...............................................................................................................292
3 ÉTUDE DE CAS N° 3 : GESTION DE BILANS DE FORMATION...............................................................................294
3.1 LES COMPÉTENCES MISES EN OEUVRE........................................................................................................................................294
3.2 LE PROBLÈME.........................................................................................................................................................................294
3.3 L'ARCHITECTURE DE L'APPLICATION.........................................................................................................................................295
3.4 LA BASE DE DONNÉES..............................................................................................................................................................295
3.5 LES COUCHES [DAO, JPA]....................................................................................................................................................299
3.5.1 INSTALLATION ET TESTS..........................................................................................................................................................299
3.5.2 L'INTERFACE DE LA COUCHE [DAO].......................................................................................................................................300
3.5.3 VERSIONS COURTES ET LONGUES DES ENTITÉS............................................................................................................................307
3.6 LA COUCHE [WEB / JSF].........................................................................................................................................................308
3.6.1 CONFIGURATION DE L'APPLICATION [WEB / JSF]........................................................................................................................309
3.6.2 L'IMPLÉMENTATION DE DÉMARRAGE..........................................................................................................................................311
3.6.3 LE BEAN [SESSIONMODEL]....................................................................................................................................................316
3.7 IMPLÉMENTATION DE L'APPLICATION.........................................................................................................................................317
3.7.1 ÉTAPE 1...............................................................................................................................................................................318
3.7.2 ÉTAPE 2...............................................................................................................................................................................321
3.7.3 ÉTAPE 3...............................................................................................................................................................................323
3.7.4 ÉTAPE 4...............................................................................................................................................................................323
3.7.5 ÉTAPE 5...............................................................................................................................................................................324
3.7.6 ÉTAPE 6...............................................................................................................................................................................324
3.7.7 ÉTAPE 7...............................................................................................................................................................................326
3.7.8 ÉTAPE 8...............................................................................................................................................................................327
3.7.9 ÉTAPE 9...............................................................................................................................................................................328
3.7.10 ÉTAPE 10...........................................................................................................................................................................329
3.7.11 ÉTAPE 11...........................................................................................................................................................................330
3.7.12 ÉTAPE 12...........................................................................................................................................................................330
4 ÉTUDE DE CAS N° 4 : CLIENT - SERVEUR WEB / JSON...........................................................................................332
4.1 OBJECTIFS..............................................................................................................................................................................332
4.2 LE PROBLÈME.........................................................................................................................................................................332
4.3 INTRODUCTION À L'IDE INTELLIJ IDEA COMMUNITY EDITION................................................................................................333
4.4 MANIPULER DU JSON............................................................................................................................................................337
4.5 LE SERVEUR WEB/JSON..........................................................................................................................................................339
4.5.1 LE PROJET INTELLIJ IDEA DU SERVEUR...................................................................................................................................340
4.5.2 INTRODUCTION À SPRING DATA...............................................................................................................................................341
4.5.3 INTRODUCTION À SPRING MVC..............................................................................................................................................342
4.5.4 LE FICHIER [[Link]]...........................................................................................................................................................342
4.5.5 LES ENTITÉS JPA..................................................................................................................................................................344
4.5.6 LA COUCHE [DAO]..............................................................................................................................................................344
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
487/489
4.5.7 CONFIGURATION DE LA COUCHE DE PERSISTANCE........................................................................................................................345
4.5.8 TESTS DE LA COUCHE [DAO]................................................................................................................................................347
4.5.9 LA COUCHE [MÉTIER].............................................................................................................................................................348
4.5.10 TESTS JUNIT DE LA COUCHE [MÉTIER]...................................................................................................................................349
4.5.11 TESTS CONSOLE DE LA COUCHE [MÉTIER]................................................................................................................................351
4.5.12 LA COUCHE [WEB/JSON]....................................................................................................................................................352
[Link] L'architecture d'un service web/jSON implémenté par Spring MVC..........................................................................352
[Link] Les URL du service web/jSON....................................................................................................................................353
[Link] Les réponses jSON du service web/jSON...................................................................................................................354
[Link] L'interface avec la couche [métier]..............................................................................................................................355
[Link] Le contrôleur................................................................................................................................................................356
4.5.13 CONFIGURATION DE L'APPLICATION WEB..................................................................................................................................358
4.5.14 LA CLASSE EXÉCUTABLE DE L'APPLICATION WEB.......................................................................................................................360
4.5.15 CRÉATION D'UNE ARCHIVE EXÉCUTABLE..................................................................................................................................361
4.6 LE CLIENT..............................................................................................................................................................................363
4.6.1 DÉPENDANCES MAVEN...........................................................................................................................................................363
4.6.2 LES ENTITÉS MANIPULÉES PAR LE CLIENT...................................................................................................................................364
4.6.3 CONFIGURATION DE LA COUCHE [DAO]...................................................................................................................................364
4.6.4 LA COUCHE [DAO]..............................................................................................................................................................365
4.6.5 LES TESTS DE LA COUCHE [DAO]...........................................................................................................................................369
4.6.6 LA COUCHE [CONSOLE]..........................................................................................................................................................370
5 ÉTUDE DE CAS N° 5 : COUCHES BASSES D'UNE APPLICATION E-COMMERCE.............................................371
5.1 LES COMPÉTENCES MISES EN OEUVRE........................................................................................................................................371
5.2 LE PROBLÈME.........................................................................................................................................................................371
5.3 LA BASE DE DONNÉES..............................................................................................................................................................371
5.4 MISE EN PLACE DE L'ENVIRONNEMENT DE TRAVAIL.....................................................................................................................374
5.5 L'APPLICATION.......................................................................................................................................................................375
5.6 LE SERVEUR...........................................................................................................................................................................375
5.6.1 LE PROJET NETBEANS............................................................................................................................................................376
5.6.2 CONFIGURATION MAVEN........................................................................................................................................................378
5.6.3 CONFIGURATION JPA / JDBC................................................................................................................................................379
5.6.4 LES ENTITÉS JPA..................................................................................................................................................................382
5.6.5 TESTS DES ENTITÉS JPA.........................................................................................................................................................384
5.6.6 LES [REPOSITORIES]...............................................................................................................................................................386
5.6.7 LA COUCHE [MÉTIER].............................................................................................................................................................390
5.6.8 TESTS DE LA COUCHE [MÉTIER]...............................................................................................................................................391
5.6.9 LA COUCHE WEB / JSON.......................................................................................................................................................392
[Link] Configuration.................................................................................................................................................................392
[Link] Contexte d'exécution......................................................................................................................................................394
[Link] Le contrôleur..................................................................................................................................................................394
[Link] URL [/getCategories].....................................................................................................................................................399
[Link] URL [getProductsByCategory]......................................................................................................................................400
[Link] URL [getOrderDetails]..................................................................................................................................................401
5.6.10 TEST DU SERVEUR WEB /JSON..............................................................................................................................................402
5.7 CONCLUSION...........................................................................................................................................................................403
6 ÉTUDE DE CAS N° 6 : GESTION DE BILANS DE FORMATION...............................................................................405
6.1 LES COMPÉTENCES MISES EN OEUVRE........................................................................................................................................405
6.2 LE PROBLÈME.........................................................................................................................................................................405
6.3 PRÉSENTATION DES COUCHES BASSES.........................................................................................................................................406
6.3.1 LA BASE DE DONNÉES............................................................................................................................................................406
6.3.2 LES ENTITÉS JPA..................................................................................................................................................................408
6.3.3 LA COUCHE [MÉTIER].............................................................................................................................................................413
[Link] Les objets de la couche [métier]....................................................................................................................................413
[Link] La couche [métier].........................................................................................................................................................415
6.3.4 MISE EN PLACE DE L'ENVIRONNEMENT DE TRAVAIL.....................................................................................................................416
[Link] Installation de la base de données..................................................................................................................................416
[Link] Installation de l'archive des couches basses dans le dépôt Maven................................................................................416
[Link] Le test JUnit de la couche [métier]................................................................................................................................416
6.4 ECRITURE DE LA COUCHE WEB.................................................................................................................................................418
6.4.1 LE PROJET NETBEANS............................................................................................................................................................419
6.4.2 LES VUES DE L'APPLICATION WEB.............................................................................................................................................420
6.4.3 CONFIGURATION DU PROJET.....................................................................................................................................................423
[Link] [[Link]]......................................................................................................................................................................423
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
488/489
[Link] [JsfConfig].....................................................................................................................................................................424
[Link] [[Link]]..........................................................................................................................................................425
[Link] [[Link]].......................................................................................................................................................................426
[Link] [[Link]].....................................................................................................................................................426
6.4.4 EXÉCUTION DU PROJET...........................................................................................................................................................428
6.4.5 LE BEAN [APPLICATIONDATA].................................................................................................................................................430
6.4.6 LE BEAN [SESSIONDATA].......................................................................................................................................................432
6.4.7 LE BEAN [FORM]..................................................................................................................................................................433
6.4.8 LE MODÈLE DES VUES............................................................................................................................................................434
6.4.9 LA PAGE D'ACCUEIL [[Link]].........................................................................................................................................435
6.4.10 LA PAGE DES PRODUITS VENDUS [[Link]]................................................................................................................437
6.4.11 LA PAGE DU PANIER [[Link]]........................................................................................................................................440
6.4.12 LA PAGE DE PAIEMENT [[Link]].............................................................................................................................443
6.4.13 LA PAGE DE CONFIRMATION [[Link]].................................................................................................................445
6.4.14 LES DERNIERS LIENS............................................................................................................................................................447
6.4.15 GESTION DES REDIRECTIONS..................................................................................................................................................447
7 ÉTUDE DE CAS N° 7...........................................................................................................................................................449
7.1 LES COMPÉTENCES MISES EN OEUVRE........................................................................................................................................449
7.2 LE PROBLÈME.........................................................................................................................................................................449
7.3 L'ARCHITECTURE DE L'APPLICATION WEB..................................................................................................................................450
7.4 LES PROJETS NETBEANS DE L'APPLICATION...............................................................................................................................451
7.5 LA BASE DE DONNÉES..............................................................................................................................................................451
7.6 L'ARCHIVE DES COUCHES [METIER, DAO, JPA]............................................................................................................................453
7.7 IMPLÉMENTATION DE LA COUCHE [WEB]....................................................................................................................................460
7.7.1 LE PROJET NETBEANS............................................................................................................................................................461
7.7.2 CONFIGURATION DU PROJET WEB..............................................................................................................................................461
[Link] Configuration Maven.....................................................................................................................................................461
[Link] Configuration web.........................................................................................................................................................462
[Link] Configuration JSF..........................................................................................................................................................462
[Link] Configuration Spring.....................................................................................................................................................463
[Link] Configuration de l'exécution du projet Netbeans..........................................................................................................464
7.8 LA VUE [[Link]]........................................................................................................................................................465
7.9 LA VUE [[Link]]........................................................................................................................................................466
7.10 LA VUE [[Link]]......................................................................................................................................................467
8 ÉTUDE DE CAS N° 8 : GESTION D'ÉLECTIONS.........................................................................................................469
8.1 LES COMPÉTENCES MISES EN OEUVRE........................................................................................................................................469
8.2 LE PROBLÈME.........................................................................................................................................................................469
8.3 L'ARCHITECTURE DE L'APPLICATION.........................................................................................................................................470
8.4 LA BASE DE DONNÉES..............................................................................................................................................................471
8.5 LES COUCHES [MÉTIER, DAO, JPA].......................................................................................................................................472
8.5.1 INSTALLATION ET TESTS..........................................................................................................................................................472
8.5.2 L'INTERFACE DE LA COUCHE [MÉTIER]......................................................................................................................................472
8.6 LA COUCHE [WEB / JSF].........................................................................................................................................................476
8.6.1 L'IMPLÉMENTATION DE DÉMARRAGE..........................................................................................................................................476
8.6.2 ÉTAPE 1...............................................................................................................................................................................479
8.6.3 ÉTAPE 2...............................................................................................................................................................................479
8.6.4 ÉTAPE 3...............................................................................................................................................................................479
8.6.5 ÉTAPE 4...............................................................................................................................................................................480
8.6.6 ÉTAPE 5...............................................................................................................................................................................481
8.6.7 ÉTAPE 6...............................................................................................................................................................................482
8.6.8 ÉTAPE 7...............................................................................................................................................................................482
[Link] - Ce cours tutoriel écrit par Serge Tahé est mis à disposition du public selon les termes de la Licence Creative Commons Attribution – Pas
d’Utilisation Commerciale – Partage dans les Mêmes Conditions 3.0 non transposé.
489/489