0% ont trouvé ce document utile (0 vote)
494 vues290 pages

Jee

jee frameworks

Transféré par

el ima
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
494 vues290 pages

Jee

jee frameworks

Transféré par

el ima
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Introduction Java EE

par l'exemple

[Link] at [Link], septembre 2016

1/290

INTRODUCTION
Ce document reprend un prcdent document crit en 2010 et intitul "Introduction Java EE avec Netbeans 6.8 et le serveur
Glassfish v3". Celui-ci amne principalement les changements suivants :

la partie JSF (Java Server Faces) est traite dans un document part : " Introduction Java Server Faces, Primefaces et
Primefaces mobile " disponible l'URL [[Link] On y utilise des caractristiques
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 prcdent. J2EE dsigne les
technologies Java utilises pour crer des applications d'entreprise avec le JDK 1.4 ou antrieur. En mme temps que le JDK 1.5
amenait de nombreuses nouveauts dans le langage Java, Sun introduisait de nouvelles technologies s'appuyant sur ce langage
amlior afin de remdier des lacunes de ces mmes technologies dans J2EE. Le terme Java EE 5 a alors t utilis pour dsigner
l'ensemble des technologies qui concourent crer une application d'entreprise avec la plate-forme Java. Au moment de la mise
jour de ce document, la dernire version de Java EE est Java EE 6.
Les livres d'Antonio Goncalves :

Java EE 5 aux ditions Eyrolles ;


Beginning Java EE 6 Platform with Glassfish 3 aux ditions Apress ;

sont d'excellents livres pour dcouvrir les technologies de Java EE 5 et Java EE 6. Toutes les technologies importantes de Java EE y
sont passes en revue dans le contexte d'tudes de cas ralistes. L'auteur a un site [[Link] que le lecteur
est invit visiter.
Le document prsent tudie certaines des technologies de Java EE 5. Nous y crons une application basique trois couches
[prsentation, mtier, accs aux donnes] dcline en plusieurs versions :
Une application web avec les technologies suivantes :

Java Server Faces : pour la couche web ;

EJB3 ou Spring : pour la couche mtier ;

EJB3 ou Spring, JPA/Hibernate, JPA/EclipseLink : pour crer diffrentes couches d'accs aux donnes ;
Une application client / serveur avec les technologies suivantes :

Swing : pour la couche graphique cliente avec un support Spring ;

EJB3 ou service web : pour la couche serveur ;


Certaines technologies Java EE ne sont pas prsentes telles les MDB (Message Driven Bean) ou les EJB3 stateful. Pour les
dcouvrir, on lira les livres d'Antonio Goncalves.
Il existe d'autres technologies Open Source, autres que Java EE, disponibles pour crer des applications trois couches. Une tandem
trs populaire est Spring ([Link] / Hibernate ([Link] Afin de permettre au
lecteur de comparer les technologies EJB3 et Spring, l'application prcdente a des versions o Spring remplace les EJB3.
Ce document est un TD (Travail Dirig) utilis en 5ime anne de l'cole d'ingnieurs ISTIA de l'universit d'Angers
[[Link] Ce TD dcrit l'application construire, les technologies Java utiliser, les endroits o trouver de
l'information. La solution propose est souvent trs cadre. Le TD pose des questions dont il ne donne pas les rponses. C'est
l'tudiant de les trouver.
L'apprentissage Java EE propos ici ncessite 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 dcrits dans le dtail. Globalement,
le document donne les squelettes des solutions et il est demand l'tudiant d'en donner certains dtails. Le document peut tre
utile mme quelqu'un ne pouvant ou ne voulant pas s'investir autant. On peut s'intresser uniquement aux architectures dcrites et
dlaisser la partie code qui fait l'objet des questions.
Pour dvelopper et excuter l'application, nous utilisons l'IDE Netbeans. Netbeans est un produit assez lourd : prvoir 1 Go de
Ram pour travailler confortablement. On peut le tlcharger l'url [[Link]
Le document fait rfrence aux cours suivants :

2/290

1.
2.
3.
4.
5.

Persistance Java 5 par la pratique : [[Link] - donne les outils pour construire la couche
d'accs aux donnes avec JPA (Java Persistence API) ;
Introduction au langage Java [[Link] - pour les dbutants ;
Introduction par l'exemple Java Server Faces, Primefaces et Primefaces mobile
[[Link]
Introduction au langage Java et l'cosystme Spring au travers d'une tude de cas
[[Link]
Exploiter une base de donnes relationnelle avec l'cosystme Spring
[[Link]

Ces supports de cours sont par la suite rfrencs [ref1] [ref2] [ref3] [ref4] [ref5].
Ce document est accompagn d'un support de TD disponible l'adresse [[Link]
Serge Tah, septembre 2016.
Notes :

En utilisant ce document, vous devez faire preuve de souplesse. Vous allez peut-tre utiliser une version de Netbeans
diffrente de celle utilise pour l'criture de ce document. Ainsi certaines copies d'cran peuvent avoir chang ou disparu.
Il en est de mme pour les options de menu. A chaque fois, il faut vous adapter ;

3/290

Table des matires


1 ARCHITECTURE D'UNE APPLICATION JAVA EN COUCHES......................................................................................8
2 LES OUTILS DU DOCUMENT.............................................................................................................................................11
2.1 MAVEN.....................................................................................................................................................................................11
2.1.1 INTRODUCTION.......................................................................................................................................................................11
2.1.2 EXCUTION DU PROJET...........................................................................................................................................................14
2.1.3 LE SYSTME DE FICHIERS D'UN PROJET MAVEN........................................................................................................................16
2.1.4 LE DPT MAVEN LOCAL........................................................................................................................................................16
2.1.5 CHERCHER UN ARTIFACT AVEC MAVEN....................................................................................................................................17
3 JPA EN RSUM....................................................................................................................................................................22
3.1 LA PLACE DE JPA DANS UNE ARCHITECTURE EN COUCHES ......................................................................................................22
3.2 JPA EXEMPLES.....................................................................................................................................................................22
3.2.1 EXEMPLE 1 - REPRSENTATION OBJET D'UNE TABLE UNIQUE.....................................................................................................22
[Link] La table [personne]...........................................................................................................................................................22
[Link] L'entit [Personne]............................................................................................................................................................23
3.2.2 CONFIGURATION DE LA COUCHE JPA.......................................................................................................................................26
3.2.3 EXEMPLE 2 : RELATION UN--PLUSIEURS.................................................................................................................................28
[Link] Le schma de la base de donnes......................................................................................................................................28
[Link] Les objets @Entity reprsentant la base de donnes........................................................................................................28
3.3 L'API DE LA COUCHE JPA......................................................................................................................................................30
3.4 LES REQUTES JPQL..............................................................................................................................................................33
3.4.1 LA TABLE [MEDECINS].......................................................................................................................................................33
3.4.2 LA TABLE [CLIENTS]...........................................................................................................................................................34
3.4.3 LA TABLE [CRENEAUX]......................................................................................................................................................34
3.4.4 LA TABLE [RV]......................................................................................................................................................................35
3.4.5 GNRATION DE LA BASE........................................................................................................................................................35
3.4.6 LA COUCHE [JPA]..................................................................................................................................................................37
3.4.7 LE PROJET NETBEANS.............................................................................................................................................................37
3.4.8 GNRATION DE LA COUCHE [JPA].........................................................................................................................................38
[Link] Cration d'une connexion Netbeans la base de donnes................................................................................................38
[Link] Cration d'une unit de persistance...................................................................................................................................39
[Link] Gnration des entits JPA................................................................................................................................................42
[Link] Les entits JPA gnres...................................................................................................................................................44
3.4.9 LE CODE D'ACCS AUX DONNES.............................................................................................................................................51
3.5 LIENS ENTRE CONTEXTE DE PERSISTANCE ET SGBD...............................................................................................................53
3.5.1 L'ARCHITECTURE DE L'APPLICATION.........................................................................................................................................53
3.5.2 LA BASE DE DONNES.............................................................................................................................................................54
3.5.3 LE PROJET MAVEN [MV-PERSONNES].......................................................................................................................................54
3.5.4 CONFIGURATION MAVEN.........................................................................................................................................................54
3.5.5 CONFIGURATION DE LA COUCHE [JPA]......................................................................................................................................55
3.5.6 L'ENTIT JPA [PERSONNE].....................................................................................................................................................55
3.5.7 CRATION DE LA TABLE [PERSONNES]................................................................................................................................56
3.5.8 TESTS DES LIENS ENTRE CONTEXTE DE PERSISTANCE ET BASE DE DONNES...............................................................................57
[Link] La mthode [test1]............................................................................................................................................................59
[Link] La mthode [test2]............................................................................................................................................................61
4 VERSION 1 : ARCHITECTURE SPRING / JPA.................................................................................................................63
4.1 LA BASE DE DONNES...............................................................................................................................................................63
4.2 MODE DE CALCUL DU SALAIRE D'UNE ASSISTANTE MATERNELLE.............................................................................................64
4.3 FONCTIONNEMENT DE L'APPLICATION CONSOLE.....................................................................................................................65
4.4 FONCTIONNEMENT DE L'APPLICATION GRAPHIQUE.................................................................................................................66
4.5 CRATION DE LA BASE DE DONNES.........................................................................................................................................66
4.6 IMPLMENTATION JPA............................................................................................................................................................68
4.6.1 COUCHE JPA / HIBERNATE.....................................................................................................................................................68
[Link] Configuration de la couche JPA........................................................................................................................................69
[Link] Les dpendances...............................................................................................................................................................72
[Link] Les entits JPA..................................................................................................................................................................73
[Link] Le code de la classe principale.........................................................................................................................................74
[Link] Tests..................................................................................................................................................................................75
4.6.2 COUCHE JPA / ECLIPSELINK...................................................................................................................................................80
4.6.3 TRAVAIL FAIRE.....................................................................................................................................................................85
4.6.4 LAZY OU EAGER ?..................................................................................................................................................................85
4.6.5 TRAVAIL FAIRE.....................................................................................................................................................................91

4/290

4.7 LE PROJET MAVEN [MV-PAM-SPRING-HIBERNATE]..................................................................................................................96


4.7.1 CONFIGURATION MAVEN.........................................................................................................................................................97
4.7.2 LA COUCHE [JPA]..................................................................................................................................................................98
4.7.3 LES INTERFACES DES COUCHES [MTIER] ET [DAO]..............................................................................................................102
4.7.4 LA CLASSE [PAMEXCEPTION]................................................................................................................................................108
4.8 LA COUCHE [DAO] DE L'APPLICATION [PAM].....................................................................................................................110
4.8.1 IMPLMENTATION..................................................................................................................................................................111
4.8.2 CONFIGURATION SPRING / JPA..............................................................................................................................................111
4.8.3 TESTS..................................................................................................................................................................................112
[Link] JUnitInitDB.....................................................................................................................................................................113
[Link] Mise en oeuvre des tests..................................................................................................................................................114
[Link] JUnitDao.........................................................................................................................................................................116
4.9 LA COUCHE [MTIER] DE L'APPLICATION [PAM]..................................................................................................................120
4.9.1 L'INTERFACE JAVA [IMETIER]................................................................................................................................................120
4.9.2 LA CLASSE [FEUILLESALAIRE]..............................................................................................................................................121
4.9.3 LA CLASSE D'IMPLMENTATION [METIER] DE LA COUCHE [METIER].........................................................................................122
4.9.4 TESTS DE LA COUCHE [METIER].............................................................................................................................................123
4.10 LA COUCHE [UI] DE L'APPLICATION [PAM] VERSION CONSOLE.......................................................................................125
4.10.1 LA CLASSE [[Link]]..........................................................................................................................................126
4.10.2 EXCUTION........................................................................................................................................................................127
4.11 LA COUCHE [UI] DE L'APPLICATION [PAM] VERSION GRAPHIQUE....................................................................................129
4.11.1 UN RAPIDE TUTORIEL..........................................................................................................................................................130
4.11.2 L'INTERFACE GRAPHIQUE [PAMJFRAME]..............................................................................................................................132
4.11.3 LES VNEMENTS DE L'INTERFACE GRAPHIQUE.....................................................................................................................133
4.11.4 INITIALISATION DE L'INTERFACE GRAPHIQUE.........................................................................................................................134
4.11.5 GESTIONNAIRES D'VNEMENTS..........................................................................................................................................136
4.11.6 EXCUTION DE L'INTERFACE GRAPHIQUE..............................................................................................................................136
4.12 IMPLMENTATION DE LA COUCHE JPA AVEC ECLIPSELINK................................................................................................137
4.12.1 LE PROJET NETBEANS........................................................................................................................................................137
4.12.2 MISE EN OEUVRE DES TESTS...............................................................................................................................................140
[Link] JUnitDao.......................................................................................................................................................................141
4.12.3 LES AUTRES TESTS..............................................................................................................................................................141
4.12.4 TRAVAIL FAIRE.................................................................................................................................................................142
5 VERSION 2 : ARCHITECTURE OPENEJB / JPA...........................................................................................................143
5.1 INTRODUCTION AUX PRINCIPES DU PORTAGE.........................................................................................................................143
5.1.1 LES NOUVELLES ARCHITECTURES..........................................................................................................................................143
5.1.2 LES BIBLIOTHQUES DES PROJETS..........................................................................................................................................143
5.1.3 CONFIGURATION DE LA COUCHE JPA / ECLIPSELINK / OPENEJB............................................................................................143
5.1.4 IMPLMENTATION DE LA COUCHE [DAO] PAR DES EJB.........................................................................................................144
5.1.5 IMPLMENTATION DE LA COUCHE [METIER] PAR UN EJB........................................................................................................146
5.1.6 LES CLIENTS DES EJB..........................................................................................................................................................147
5.2 TRAVAIL PRATIQUE.................................................................................................................................................................148
5.2.1 CONFIGURATION INITIALE DU PROJET NETBEANS [MV-PAM-OPENEJB-ECLIPSELINK]..................................................................148
5.2.2 PORTAGE DE LA COUCHE [DAO]...........................................................................................................................................150
[Link] L'EJB [CotisationDao]....................................................................................................................................................151
[Link] Les EJB [EmployeDao] et [IndemniteDao]....................................................................................................................152
[Link] La classe [PamException]...............................................................................................................................................152
[Link] Entits srialisables.........................................................................................................................................................153
[Link] Tests de la couche [DAO]...............................................................................................................................................153
5.2.3 PORTAGE DE LA COUCHE [METIER]........................................................................................................................................158
[Link] L'EJB [Metier]................................................................................................................................................................159
[Link] Tests de la couche [metier]..............................................................................................................................................159
5.2.4 PORTAGE DE LA COUCHE [CONSOLE].....................................................................................................................................161
5.2.5 PORTAGE DE LA COUCHE [SWING]..........................................................................................................................................163
5.3 CONCLUSION..........................................................................................................................................................................164
6 VERSION 3 : PORTAGE DE L'APPLICATION PAM SUR UN SERVEUR D'APPLICATIONS GLASSFISH.......165
6.1 LA PARTIE SERVEUR DE L'APPLICATION CLIENT / SERVEUR PAM.........................................................................................165
6.1.1 L'ARCHITECTURE DE L'APPLICATION.......................................................................................................................................165
[Link] Le projet Netbeans..........................................................................................................................................................166
[Link] Configuration Maven......................................................................................................................................................167
[Link] Configuration de la couche de persistance......................................................................................................................169
[Link] Insertion des couches [JPA, DAO, metier].....................................................................................................................173
[Link] Dploiement de l'application sur le serveur Glassfish....................................................................................................173

5/290

6.1.2 CLIENT CONSOLE - VERSION 1...............................................................................................................................................175


6.1.3 CLIENT CONSOLE - VERSION 2...............................................................................................................................................180
6.1.4 LE CLIENT SWING.................................................................................................................................................................184
6.1.5 PORTAGE DES TESTS UNITAIRES.............................................................................................................................................184
7 VERSION 4 CLIENT / SERVEUR DANS UNE ARCHITECTURE DE SERVICE WEB SOAP..............................188
7.1 SERVICE WEB SOAP IMPLMENT PAR UN EJB...................................................................................................................189
7.1.1 LA PARTIE SERVEUR..............................................................................................................................................................189
7.1.2 LA PARTIE CLIENTE...............................................................................................................................................................192
[Link] Le projet Netbeans du client console..............................................................................................................................192
[Link] Le client console du service web Metier.........................................................................................................................193
[Link] Le client swing du service web Metier............................................................................................................................196
7.2 SERVICE WEB SOAP IMPLMENT PAR UNE APPLICATION WEB............................................................................................196
7.2.1 LA PARTIE SERVEUR..............................................................................................................................................................196
7.2.2 LA PARTIE CLIENTE...............................................................................................................................................................201
7.3 SERVICE WEB SOAP IMPLMENT AVEC SPRING, HIBERNATE ET TOMCAT..........................................................................201
7.3.1 LA PARTIE SERVEUR..............................................................................................................................................................202
7.3.2 LA PARTIE CLIENTE...............................................................................................................................................................207
7.4 SERVICE WEB SOAP IMPLMENT AVEC SPRING, ECLIPSELINK ET TOMCAT.......................................................................209
7.5 CONCLUSION..........................................................................................................................................................................215
8 INTRODUCTION JAVA SERVER FACES.....................................................................................................................217
9 VERSION 5 - APPLICATION PAM WEB / JSF................................................................................................................218
9.1 ARCHITECTURE DE L'APPLICATION........................................................................................................................................218
9.2 FONCTIONNEMENT DE L'APPLICATION...................................................................................................................................219
9.3 LE PROJET NETBEANS...........................................................................................................................................................221
9.3.1 LES FICHIERS DE CONFIGURATION..........................................................................................................................................226
9.3.2 LA FEUILLE DE STYLE...........................................................................................................................................................228
9.3.3 LE FICHIER DES MESSAGES....................................................................................................................................................228
9.3.4 LA PORTE DES BEANS..........................................................................................................................................................229
9.3.5 LA COUCHE [MTIER]...........................................................................................................................................................230
9.4 LE FORMULAIRE [[Link]] ET SON MODLE [[Link]]...........................................................................................231
9.4.1 TAPE 1...............................................................................................................................................................................231
9.4.2 TAPE 2...............................................................................................................................................................................232
9.4.3 TAPE 3...............................................................................................................................................................................232
9.4.4 TAPE 4...............................................................................................................................................................................233
10 VERSION 6 - INTGRATION DE LA COUCHE WEB DANS UNE ARCHITECTURE 3 COUCHES JSF / EJB. 235
10.1 ARCHITECTURE DE L'APPLICATION......................................................................................................................................235
10.2 LE PROJET NETBEANS DE LA COUCHE WEB.........................................................................................................................235
10.3 LE PROJET NETBEANS DE L'APPLICATION D'ENTREPRISE....................................................................................................237
11 VERSION 7 - APPLICATION WEB PAM MULTI-VUES / MULTI-PAGES...............................................................241
11.1 LES VUES DE L'APPLICATION................................................................................................................................................242
11.2 LE PROJET NETBEANS DE LA COUCHE [WEB].......................................................................................................................243
11.2.1 LES FICHIERS DE CONFIGURATION........................................................................................................................................244
11.2.2 LA FEUILLE DE STYLE.........................................................................................................................................................244
11.2.3 LE FICHIER DES MESSAGES..................................................................................................................................................245
11.2.4 LA COUCHE [MTIER].........................................................................................................................................................246
11.3 LES BEANS DE L'APPLICATION..............................................................................................................................................247
11.3.1 LE BEAN APPLICATIONDATA................................................................................................................................................247
11.3.2 LE BEAN SESSIONDATA......................................................................................................................................................248
11.3.3 LE BEAN FORM..................................................................................................................................................................249
11.4 LES PAGES DE L'APPLICATION..............................................................................................................................................250
11.4.1 [[Link]]................................................................................................................................................................250
11.4.2 L'ENTTE [[Link]].................................................................................................................................................251
11.5 LES CAS D'UTILISATION DE L'APPLICATION..........................................................................................................................252
11.5.1 AFFICHAGE DE LA PAGE D'ACCUEIL......................................................................................................................................252
11.5.2 L'ACTION [FAIRESIMULATION].............................................................................................................................................253
11.5.3 LA GESTION DES ERREURS...................................................................................................................................................255
11.5.4 L'ACTION [EFFACERSIMULATION].........................................................................................................................................257
11.5.5 L'ACTION [ENREGISTRERSIMULATION].................................................................................................................................258
11.5.6 L'ACTION [RETOURSIMULATEUR].........................................................................................................................................262
11.5.7 L'ACTION [VOIRSIMULATIONS]............................................................................................................................................262
11.5.8 L'ACTION [RETIRERSIMULATION].........................................................................................................................................263
11.5.9 L'ACTION [TERMINERSESSION]............................................................................................................................................265
11.6 INTGRATION DE LA COUCHE WEB DANS UNE ARCHITECTURE 3 COUCHES JSF / EJB.........................................................265

6/290

12 VERSIONS 8 : PORTAGES DE L'APPLICATION JSF MULTIPAGES......................................................................267


12.1 ENVIRONNEMENT SPRING / HIBERNATE / TOMCAT.............................................................................................................267
12.2 ENVIRONNEMENT SPRING / ECLIPSELINK / TOMCAT..........................................................................................................273
12.3 ENVIRONNEMENT SPRING / ECLIPSELINK / GLASSFISH......................................................................................................274
12.3.1 ETAPE 1.............................................................................................................................................................................274
12.3.2 ETAPE 2.............................................................................................................................................................................278
12.3.3 EXCUTION........................................................................................................................................................................281
13 VERSION 9 : IMPLMENTATION DE LA COUCHE WEB AVEC PRIMEFACES.................................................283

7/290

1 Architecture d'une application Java en couches


Une application java est souvent dcoupe en couches chacune ayant un rle bien dfini. Considrons une architecture courante,
celle trois couches :

utilisateur

Couche mtier
[metier]

Couche interface
utilisateur [ui]
1

Couche d'accs aux


donnes [DAO]

Donnes

la couche [1], appele 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 rle de fournir des donnes provenant de l'utilisateur la
couche [2] ou bien de prsenter l'utilisateur des donnes fournies par la couche [2] ;
la couche [2], appele ici [metier] est la couche qui applique les rgles dites mtier, c.a.d. la logique spcifique de
l'application, sans se proccuper de savoir d'o viennent les donnes qu'on lui donne, ni o vont les rsultats qu'elle
produit ;
la couche [3], appele ici [DAO] (Data Access Object) est la couche qui fournit la couche [2] des donnes prenregistres (fichiers, bases de donnes, services web, ...) et qui enregistre certains des rsultats fournis par la couche [2] ;

Il existe diffrentes possibilits pour implmenter la couche [DAO] en relation avec une base de donnes. Examinons-en quelquesunes :

utilisateur

Couche ui
[ui]
1

Couche mtier
[metier]
2

Couche
[JDBC]

Couche d'accs
aux donnes
[DAO]

Base de
Donnes

3
La couche [JDBC] ci-dessus est la couche standard utilise en Java pour accder des bases de donnes. Elle isole la couche [DAO]
du SGBD qui gre la base de donnes. On peut thoriquement changer de SGBD sans changer le code de la couche [DAO]. Malgr
cet avantage, l'API JDBC prsente certains inconvnients :

toutes les oprations sur le SGBD sont susceptibles de lancer l'exception contrle (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 compltement insensible au SGBD. Ceux-ci ont par exemple des mthodes propritaires quant
la gnration automatique de valeurs de cls 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 insrer
celui-ci ;

avec SQL Server, la couche [DAO] insre l'enregistrement qui se voit donner automatiquement une valeur de cl
primaire par le SGBD, valeur rendue la couche [DAO] ;
Ces diffrences peuvent tre gommes via l'utilisation de procdures stockes. Dans l'exemple prcdent, la couche [DAO]
appellera une procdure stocke dans Oracle ou SQL Server qui prendra en compte les particularits du SGBD. Celles-ci
seront caches la couche [DAO]. Nanmoins, si changer de SGBD n'impliquera pas de rcrire la couche [DAO], cela
implique quand mme de rcrire les procdures stockes. Cela peut ne pas tre considr comme rdhibitoire.

De multiples efforts ont t faits pour isoler la couche [DAO] des aspects propritaires des SGBD. Une solution ayant eu un vrai
succs dans ce domaine ces dernires annes, est celle d'Hibernate :

Couche d'accs aux


donnes [DAO]
3

4 Objets image
de la BD

Couche
[Hibernate]
5

Couche
[JDBC]
6

Base de
Donnes
7

8/290

La couche [Hibernate] vient se placer entre la couche [DAO] crite par le dveloppeur et la couche [JDBC]. Hibernate est un ORM
(Object Relational Mapper), un outil qui fait le pont entre le monde relationnel des bases de donnes et celui des objets manipuls
par Java. Le dveloppeur de la couche [DAO] ne voit plus la couche [JDBC] ni les tables de la base de donnes dont il veut exploiter
le contenu. Il ne voit que l'image objet de la base de donnes, image objet fournie par la couche [Hibernate]. Le pont entre les tables
de la base de donnes et les objets manipuls par la couche [DAO] est fait principalement de deux faons :

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'idal vis est que le dveloppeur de
la couche [DAO] puisse ignorer totalement qu'il travaille avec une base de donnes. 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 dlicate et
ncessite une certaine habitude.
La couche [4] des objets, image de la BD est appele "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 excuts par la couche JDBC. Pour les actions d'interrogation de la base (le SQL Select), Hibernate
fournit au dveloppeur, un langage HQL (Hibernate Query Language) pour interroger le contexte de persistance [4] et non la BD
elle-mme.
Hibernate est populaire mais complexe matriser. La courbe d'apprentissage souvent prsente comme facile est en fait assez raide.
Ds qu'on a une base de donnes avec des tables ayant des relations un--plusieurs ou plusieurs--plusieurs, la configuration du
pont relationnel / objets n'est pas la porte du premier dbutant venu. Des erreurs de configuration peuvent conduire des
applications peu performantes.
Devant le succs des produits ORM, Sun le crateur de Java, a dcid de standardiser une couche ORM via une spcification
appele JPA (Java Persistence API) apparue en mme temps que Java 5. La spcification JPA a t implmente par divers produits :
Hibernate, Toplink, OpenJpa, .... Avec JPA, l'architecture prcdente devient la suivante :

Couche d'accs aux


donnes [DAO]
3

4
Objets image
de la BD

Interface
[JPA]

Implmentation JPA
[Hibernate / ...]
5

Couche
[JDBC]
6

Base de
Donnes
7

La couche [DAO] dialogue maintenant avec la spcification JPA, un ensemble d'interfaces. Le dveloppeur 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 spcifique. Maintenant, il va crire une couche [DAO] qui va dialoguer avec une couche JPA. Quelque soit
le produit qui implmente celle-ci, l'interface de la couche JPA prsente la couche [DAO] reste la mme.
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.

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

Interface Implmentation JPA


[EclipseLink
[JPA]
/ Hibernate]
5

Couche
[JDBC]
6

Spring

Le grand intrt de Spring est qu'il permet de lier les couches par configuration et non dans le code. Ainsi si l'implmentation JPA /
Hibernate doit tre remplace par une implmentation Hibernate sans JPA, parce que par exemple l'application s'excute dans un
environnement JDK 1.4 qui ne supporte pas JPA, ce changement d'implmentation de la couche [DAO] n'a pas d'impact sur le
code de la couche [mtier]. Seul le fichier de configuration Spring qui lie les couches entre elles doit tre modifi.
Avec Java EE 5, une autre solution existe : implmenter les couches [metier] et [DAO] avec des EJB3 (Enterprise Java Bean version
3) :

9/290

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

Interface Implmentation JPA


[EclipseLink
[JPA]
/ Hibernate]
5

Couche
[JDBC]
6

conteneur Ejb3

Nous verrons que cette solution n'est pas trs diffrente 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 utiliss en-dehors d'un serveur d'applications. C'est le cas de JBoss EJB3 ou OpenEJB.
Dans un environnement EE5, les couches sont implmentes par des objets appels EJB (Enterprise Java Bean). Dans les
prcdentes versions d'EE, les EJB (EJB 2.x) taient rputs difficiles mettre en uvre et tester et considrs 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 donnes et EJB2.x "session" un objet utilis pour implmenter les couches [metier], [DAO] d'une architecture
multicouche. L'un des principaux reproches faits aux couches implmentes avec des EJB est qu'elles ne sont utilisables qu'au sein
de conteneurs EJB, un service dlivr par l'environnement EE. Cet environnement, plus complexe mettre en oeuvre qu'un
environnement SE (Standard Edition), peut dcourager le dveloppeur faire frquemment des tests. Nanmoins, il existe des
environnements de dveloppement Java qui facilitent l'utilisation d'un serveur d'applications en automatisant le dploiement 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 raction 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 donnes", 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'implmentation des couches d'une application par des objets Java classiques (POJO, Plain Old/Ordinary Java Object), permettant
la rutilisation de ceux-ci dans un autre contexte. Enfin, il intgre de nombreux outils tiers de faon assez transparente, notamment
des outils de persistance tels que Hibernate, EclipseLink, Ibatis, ...
Java EE5 a t conu pour corriger les lacunes de la spcification EJB2. Les EJB 2.x sont devenus les EJB3. Ceux-ci sont des
POJOs tagus 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 bnficier 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 ignores.
Ci-dessus, nous avons reprsent Spring et un conteneur EJB3 comme infrastructure (framework) possible de notre architecture
multicouche. C'est cette infrastructure qui dlivrera les services dont nous avons besoin : un pool de connexions et un gestionnaire
de transactions.

avec Spring, les couches seront implmentes avec des POJOs. Ceux-ci auront accs aux services de Spring (pool
de connexions, gestionnaire de transaction) par injection de dpendances dans ces POJOs : lors de la
construction de ceux-ci, Spring leur injecte des rfrences sur les services dont il vont avoir besoin ;

avec le conteneur EJB3, les couches seront implmentes avec des EJB. Une architecture en couches
implmentes avec des EJB3 est peu diffrente de celles implmentes avec des POJO instancis par Spring.
Nous trouverons beaucoup de ressemblances ;

pour terminer, nous prsenterons un exemple d'application web multicouche [1] :

Couche
[web]
1

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

Interface Implmentation JPA


[EclipseLink
[JPA]
/ Hibernate]
5

Couche
[JDBC]
6

Spring ou Ejb3

10/290

2 Les outils du document


Les exemples du document ont t tests avec les outils suivants :
Netbeans 8.1. L'installation de Netbeans est dcrite dans [ref3] au paragraphe 1.3.1 ;
Wampserver version 2.5. L'installation de WampServer est dcrite dans [ref3] au paragraphe 1.3.3 ;
Maven est intgr Netbeans. Nous dcrivons maintenant cet outil.

2.1
2.1.1

Maven
Introduction

Maven est disponible l'URL [[Link] ]. Selon ses crateurs :


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 intgr dans Netbeans et nous allons l'utiliser pour une seule de ses caractristiques : la gestion des bibliothques d'un
projet. Celles-ci sont formes de l'ensemble des archives jars qui doivent tre dans le Classpath du projet. Elles peuvent tre trs
nombreuses. Par exemple, nos projets futurs vont utiliser l'ORM (Object Relational Mapper) Hibernate. Cet ORM est compos de
dizaines d'archives jar. L'intrt de Maven est qu'il nous affranchit de les connatre 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
tlcharge alors galement toutes les bibliothques ncessaires Hibernate. On appelle cela les dpendances d'Hibernate. Une
bibliothque ncessaire Hibernate peut elle mme dpendre d'autres archives. Celles-ci seront galement tlcharges. Toutes ces
bibliothques sont places dans un dossier appel le dpt local de Maven.
Un projet Maven est facilement partageable. Si on le transfre d'un poste un autre et que les dpendances du projet ne sont pas
prsentes dans le dpt local du nouveau poste, elles seront tlcharges.
Maven peut tre utilis seul ou intgr un EDI (Environnement de Dveloppement Intgr) tel Netbeans ou Eclipse.
Crons un projet Maven dans Netbeans :

en [1], crer un nouveau projet,


en [2-3], choisir la catgorie [Maven] et le type de projet [Java Application],

11/290

en [4], donner un nom au projet ;


en [5], dsigner le dossier parent du dossier du nouveau projet ;
en [6], l'identifiant du groupe auquel appartient le projet Maven ;
en [7], la version du projet ;
en [8], un package vide qui sera cr automatiquement par Netbeans ;
en [9], le projet gnr ;

Crons maintenant un test unitaire [10-19] :

12/290

Examinons les lments du projet et explicitons le rle de chacun :

[Source packages] : les classes Java du projet ;


[Test packages] : les classes de test du projet ;
[Dependencies] : les archives .jar ncessaires au projet et gres par Maven ;
[Test Dependencies] : les archives .jar ncessaires aux tests du projet et gres par Maven ;
[Java Dependencies] : les archives .jar ncessaires au projet et non gres par Maven ;
[Project Files] : fichiers de configuration de Maven et Netbeans,

Le fichier [[Link]] configure le projet Maven. Son contenu est pour l'instant le suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
<modelVersion>4.0.0</modelVersion>
<groupId>[Link]</groupId>
<artifactId>mv-exemple</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>[Link]</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<[Link]>UTF-8</[Link]>
<[Link]>1.8</[Link]>
<[Link]>1.8</[Link]>
</properties>
</project>

les lignes 4-7 dfinissent 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 cration du projet :

13/290

Un objet Maven est dfini par quatre proprits :

ligne 4 : [groupId] : une information qui ressemble un nom de package. Ainsi les bibliothques 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 gnrer un [jar] (ligne 7) dans le groupe [[Link]] (ligne 4), nomm [mv-exemple] (ligne 5) et de version [1.0SNAPSHOT] (ligne 6). Ces quatre informations dfinissent de faon unique un artifact Maven.
Les lignes 8-21 listent les dpendances du projet Maven, c'est dire la liste des bibliothques ncessaires au projet. Chaque
bibliothque est dfinie 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 bibliothque. La valeur par dfaut est compile qui indique que la bibliothque est ncessaire la compilation et
l'excution. La valeur test signifie que la bibliothque n'est ncessaire que pour excuter les tests du projet. C'est le cas ici avec les
bibliothques JUnit (ligne 13) et Hamcrest (ligne 19). Si ces dpendances ne sont pas prsentes dans le dpt local du poste (notion
dfinie plus loin), elles seront tlcharges.

2.1.2

Excution du projet

Crons maintenant une classe excutable [1-7] :

14/290

Modifions la classe [Main] gnre de la faon suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.

package [Link];
public class Main {
public static void main(String[] args) {
[Link]("Hello world!");
}
}

Nous excutons le projet :

En [1], le projet Maven est construit puis excut [2]. Les logs dans la console Netbeans sont les suivants :
1.

2.
3.
4.
5.
6.
7.
8.

cd U:\Temp\16-08-23\mv-exemple; "JAVA_HOME=C:\\Program Files\\Java\\jdk1.8.0_77" "M2_HOME=C:\\Program Files\\apache-maven3.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\\[Link]\" -[Link]=UTF-8 process-classes [Link]:exec-maven-plugin:1.2.1:exec\""
Scanning for projects...
-----------------------------------------------------------------------Building mv-exemple 1.0-SNAPSHOT
-------------------------------------------------------------------------- maven-resources-plugin:2.6:resources (default-resources) @ mv-exemple ---

15/290

9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.

Using 'UTF-8' encoding to copy filtered resources.


skip non existing resourceDirectory U:\Temp\16-08-23\mv-exemple\src\main\resources
--- maven-compiler-plugin:3.1:compile (default-compile) @ mv-exemple --Changes detected - recompiling the module!
Compiling 1 source file to U:\Temp\16-08-23\mv-exemple\target\classes
--- exec-maven-plugin:1.2.1:exec (default-cli) @ mv-exemple --Hello world!
-----------------------------------------------------------------------BUILD SUCCESS
-----------------------------------------------------------------------Total time: 2.744 s
Finished at: 2016-08-23T[Link]+02:00
Final Memory: 13M/194M
------------------------------------------------------------------------

Le rsultat de l'excution est ligne 17.

2.1.3

2.1.4

Le systme de fichiers d'un projet Maven

[1] : le systme de fichiers du projet est dans l'onglet [Files],


[2] : les sources Java sont dans le dossier [src / main / java],
[3] : les sources Java des tests sont dans le dossier [src / test / java],
[4] : le dossier [target] est cr par la construction (build) du projet,
[5] : ici, la construction du projet a cr une archive [[Link]].

Le dpt Maven local

Nous avons dit que Maven tlchargeait les dpendances ncessaires au projet et les stockait localement. On peut explorer ce dpt
local [1-3] :

16/290

2.1.5

en [3], on voit l'artifact Maven [[Link], mv-exemple, [Link]] cr par notre projet lors du [Clean and Build] ;

Chercher un artifact avec Maven

Apprenons maintenant chercher un artifact avec Maven. Partons de la liste des dpendances actuelles du projet :

en [2], on supprime les deux dpendances de la branche [Test Dependencies] ;


en [3], une erreur est signale cause de ces dpendances manquantes ;

Le fichier [[Link]] est alors modifi :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
<modelVersion>4.0.0</modelVersion>
<groupId>[Link]</groupId>
<artifactId>mv-exemple</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<[Link]>UTF-8</[Link]>
<[Link]>1.8</[Link]>
<[Link]>1.8</[Link]>
</properties>
</project>

Les dpendances supprimes n'apparaissent plus dans [[Link]]. Maintenant, recherchons-les dans les dpts Maven.

17/290

en [1-2], on ajoute une dpendance au projet ;


en [3], on doit prciser des informations sur l'artifact cherch (groupId, artifactId, version, packaging (Type) et scope).
Nous commenons par prciser 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 procdant de la mme faon, on choisit la version la plus rcente ;
en [6], le type de packaging est jar,
en [7], nous choisissons la porte test pour indiquer que la dpendance n'est ncessaire qu'aux tests.

En [8], les dpendances ajoutes apparaissent dans le projet. Le fichier [[Link]] reflte ces changements :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
<modelVersion>4.0.0</modelVersion>
<groupId>[Link]</groupId>
<artifactId>mv-exemple</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<type>jar</type>

18/290

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 dpendance [hamcrest-core-1.3] que nous voyons en [8]. Cela parce que c'est
une dpendance de JUnit 4.12 et non du projet lui-mme. Cela est signal par une icne diffrente dans la branche [Dependencies].
Elle a t tlcharge automatiquement.
Supposons maintenant qu'on ne connaisse pas le [groupId] de l'artifact que l'on dsire. 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] :

En [2], on peut taper des mots cls. Tapons hibernate et lanons la recherche [3-4] :

19/290

en [5-6], nous obtenons le code Maven coller dans le fichier [[Link]]. Nous le faisons :

1.
2.

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
<modelVersion>4.0.0</modelVersion>
<groupId>[Link]</groupId>
<artifactId>mv-exemple</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<type>jar</type>
</dependency>
<!-- [Link] -->
<dependency>
<groupId>[Link]</groupId>
<artifactId>hibernate-core</artifactId>
<version>[Link]</version>
</dependency>
</dependencies>
<properties>
<[Link]>UTF-8</[Link]>
<[Link]>1.8</[Link]>
<[Link]>1.8</[Link]>
</properties>
</project>

3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.

Nous sauvegardons le fichier [[Link]]. Le projet Maven volue comme suit :

20/290

en [7], la nouvelle dpendance apparat. Une icne spciale indique qu'elle n'est pas encore tlcharge ;
pour tlcharger la nouvelle dpendance, on fait un [Clean and Build] [8] du projet ;

Maven entreprend alors le tlchargement des nouvelles dpendances. Le projet volue comme suit :

en [9], la dpendance [hibernate-core-5.2.2-Final]. Dans le dpt o il a t trouv, cet [artifactId] est lui aussi dcrit par un
fichier [[Link]]. Ce fichier a t lu et Maven a dcouvert que l'[artifactId] avait des dpendances. Il les tlcharge
galement. Il fera cela pour chaque [artifactId] tlcharg. Au final, on trouve en [10] des dpendances qu'on n'avait pas
demandes directement. Elles sont signales par une icne diffrente de celle de l'[artifactId] principal ;

Dans ce document, nous utilisons Maven principalement pour cette caractristique. Cela nous vite de connatre toutes les
dpendances d'une bibliothque que l'on veut utiliser. On laisse Maven les grer. Par ailleurs, en partageant un fichier [[Link]]
entre dveloppeurs, on est assur que chaque dveloppeur utilise bien les mmes bibliothques.
Dans les exemples qui suivront, nous donnerons toujours le fichier [[Link]] utilis. Le lecteur n'aura qu' l'utiliser pour se trouver
dans les mmes 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.

21/290

3 JPA en rsum
Nous nous proposons d'introduire JPA (Java Persistence API) avec quelques exemples. JPA est dvelopp dans le cours :

Persistance Java 5 par la pratique : [[Link] - donne les outils pour construire la couche
d'accs aux donnes avec JPA

Nous prsentons tout d'abord les fondements de JPA. On attendra le paragraphe 3.4, page 33 pour crer une application
exemple. D'ici l, il n'y a que de la lecture faire.

3.1

La place de JPA dans une architecture en couches

Le lecteur est invit relire le dbut de ce document (paragraphe 1, page 8) qui explique le rle de la couche JPA dans une
architecture en couches. La couche JPA s'insre dans les couches d'accs aux donnes :

Couche d'accs aux


donnes [DAO]

Objets image
de la BD

Interface
[JPA]

Implmentation JPA
[Hibernate / ...]

Couche
[JDBC]

Base de
Donnes

La couche [DAO] dialogue avec la spcification JPA. Quelque soit le produit qui implmente celle-ci, l'interface de la couche JPA
prsente la couche [DAO] reste la mme. Nous prsentons dans la suite quelques exemples tirs de [ref1] qui nous permettront
de construire notre propre couche JPA.

3.2
3.2.1
[Link]

JPA exemples
Exemple 1 - Reprsentation objet d'une table unique
La table [personne]

Considrons une base de donnes ayant une unique table [personne] dont le rle est de mmoriser quelques informations sur des
individus :

ID

cl primaire de la table

VERSION

version de la ligne dans la table. A chaque fois que la personne est modifie, son n de version est
incrment.

NOM

nom de la personne

PRENOM

son prnom

DATENAISSANCE

sa date de naissance

22/290

MARIE

entier 0 (non mari) ou 1 (mari)

NBENFANTS

nombre d'enfants de la personne

[Link]

L'entit [Personne]

Nous nous plaons dans l'environnement d'excution suivant :

Programme de test
console [main]
3

4
Objets image
de la BD

Interface
[JPA]
5

Implmentation
6 [Eclipselink
/ Hibernate]

Couche
[JDBC]

Base de
Donnes
7

La couche JPA [5] doit faire un pont entre le monde relationnel de la base de donnes [7] et le monde objet [4] manipul par les
programmes Java [3]. Ce pont est fait par configuration et il y a deux faons de le faire :
1. avec des fichiers XML. C'tait quasiment l'unique faon de faire jusqu' l'avnement du JDK 1.5 ;
2. avec des annotations Java depuis le JDK 1.5 ;
Dans ce document, nous utiliserons exclusivement la seconde mthode.
L'objet [Personne] image de la table [personne] prsente prcdemment pourrait tre le suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.

...
@SuppressWarnings("unused")
@Entity
@Table(name="Personne")

public class Personne implements Serializable{


@Id
@Column(name = "ID", nullable = false)
@GeneratedValue(strategy = [Link])
private Integer id;
@Column(name = "VERSION", nullable = false)
@Version
private int version;
@Column(name = "NOM", length = 30, nullable = false, unique = true)
private String nom;
@Column(name = "PRENOM", length = 30, nullable = false)
private String prenom;
@Column(name = "DATENAISSANCE", nullable = false)
@Temporal([Link])
private Date datenaissance;
@Column(name = "MARIE", nullable = false)
private boolean marie;
@Column(name = "NBENFANTS", nullable = false)
private int nbenfants;
// constructeurs
public Personne() {
}
public Personne(String nom, String prenom, Date datenaissance, boolean marie,
int nbenfants) {
setNom(nom);
setPrenom(prenom);
setDatenaissance(datenaissance);
setMarie(marie);
setNbenfants(nbenfants);
}
// toString
public String toString() {
...
}
// getters and setters
...

23/290

53. }

La configuration se fait l'aide d'annotations Java @Annotation. Les annotations Java sont soit exploites par le compilateur, soit
par des outils spcialiss, au moment de l'excution. En-dehors de l'annotation de la ligne 3 destine au compilateur, toutes les
annotations sont ici destines l'implmentation JPA utilise, Hibernate ou EclipseLink. Elles seront donc exploites l'excution.
En l'absence des outils capables de les interprter, ces annotations sont ignores. Ainsi la classe [Personne] ci-dessus pourrait tre
exploite dans un contexte hors JPA.
Il faut distinguer deux cas d'utilisation des annotations JPA dans une classe C associe une table T :
1.
2.

la table T existe dj : les annotations JPA doivent alors reproduire l'existant (nom et dfinition des colonnes, contraintes
d'intgrit, cls trangres, cls primaires, ...) ;
la table T n'existe pas et elle peut alors tre cre d'aprs les annotations trouves dans la classe C ;

Le cas 2 est le plus facile grer. 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 adapte au pont relationnel / objet de JPA. Pour simplifier, nous nous plaons dans le cas 2 o la table T associe la
classe C va tre cre d'aprs les annotations JPA de la classe C.
Commentons les annotations JPA de la classe [Personne] :

ligne 4 : l'annotation @Entity est la premire annotation indispensable. Elle se place avant la ligne qui dclare la classe et
indique que la classe en question doit tre gre par la couche de persistance JPA. En l'absence de cette annotation, toutes
les autres annotations JPA seraient ignores ;
ligne 5 : l'annotation @Table dsigne la table de la base de donnes dont la classe est une reprsentation. Son principal
argument est name qui dsigne 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 dsigner 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 mme
nom que le champ. Dans notre exemple, l'argument name n'tait donc pas obligatoire. L'argument nullable=false indique
que la colonne associe au champ ne peut avoir la valeur NULL et que donc le champ doit avoir ncessairement une
valeur ;
ligne 10 : l'annotation @GeneratedValue indique comment est gnre la cl primaire lorsqu'elle est gnre
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 gnr par le SGBD mais fix par
l'application. Dans ce cas, l'annotation @GeneratedValue serait absente. L'argument strategy indique comment est
gnre la cl primaire lorsqu'elle est gnre par le SGBD. Les SGBD n'ont pas tous la mme technique de gnration des
valeurs de cl primaire. Par exemple :
Firebird
SQL server

Oracle

utilise un gnrateur de valeurs appele avant chaque insertion.


le champ cl primaire est dfini comme ayant le type Identity. On a un rsultat similaire au
gnrateur de valeurs de Firebird, si ce n'est que la valeur de la cl n'est connue qu'aprs
l'insertion de la ligne.
utilise un objet appel SEQUENCE qui l encore joue le rle d'un gnrateur de valeurs.

La couche JPA doit gnrer des ordres SQL diffrents selon les SGBD pour crer le gnrateur de valeurs. On lui indique
par configuration le type de SGBD qu'elle a grer. Du coup, elle peut savoir quelle est la stratgie habituelle de
gnration de valeurs de cl primaire de ce SGBD. L'argument strategy = [Link] indique la couche
JPA qu'elle doit utiliser cette stratgie habituelle. Cependant, si on souhaite changer d'implmentation JPA, il ne faut pas
utiliser cette stratgie mais en imposer une la couche JPA :

la statgie [[Link]] peut tre utilise avec les SGBD MySQL, SQL Server, IBM DB2 et
les versions rcentes d'Oracle ;

la statgie [[Link]] peut tre utilise avec les SGBD Firebird, Postgresql et les versions
anciennes d'Oracle ;
ligne 14 : l'annotation @Version dsigne le champ qui sert grer les accs concurrents une mme ligne de la table.
Pour comprendre ce problme d'accs concurrents une mme ligne de la table [personne], supposons qu'une
application web permette la mise jour d'une personne et examinons le cas suivant :

24/290

Au temps T1, un utilisateur U1 entre en modification dune personne P. A ce moment, le nombre denfants est 0. Il
passe ce nombre 1 mais avant quil ne valide sa modification, un utilisateur U2 entre en modification de la mme
personne P. Puisque U1 na pas encore valid sa modification, U2 voit sur son cran le nombre denfants 0. U2
passe le nom de la personne P en majuscules. Puis U1 et U2 valident leurs modifications dans cet ordre. Cest la
modification de U2 qui va gagner : dans la base, le nom va passer en majuscules et le nombre denfants va rester
zro alors mme que U1 croit lavoir chang en 1.
La notion de version de personne nous aide rsoudre ce problme. On reprend le mme cas dusage :
Au temps T1, un utilisateur U1 entre en modification dune personne P. A ce moment, le nombre denfants est 0 et
la version V1. Il passe le nombre denfants 1 mais avant quil ne valide sa modification, un utilisateur U2 entre en
modification de la mme personne P. Puisque U1 na pas encore valid sa modification, U2 voit le nombre denfants
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 vrifie que celui qui modifie une personne P dtient la mme
version que la personne P actuellement en base. Ce sera le cas de lutilisateur U1. Sa modification est donc accepte
et la version de la personne modifie 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 sapercevoir que U2 dtient une version V1 de la personne P, alors
quactuellement la version de celle-ci en base est V2. On va alors pouvoir dire lutilisateur U2 que quelquun est
pass avant lui et quil doit repartir de la nouvelle version de la personne P. Il le fera, rcuprera une personne P de
version V2 qui a maintenant un enfant, passera le nom en majuscules, validera. Sa modification sera accepte si la
personne P enregistre a toujours la version V2. Au final, les modifications faites par U1 et U2 seront prises en
compte alors que dans le cas dusage sans version, lune des modifications tait perdue.
La couche [DAO] de l'application cliente peut grer elle-mme 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 incrmente de 1 dans la table. L'annotation
@Version permet de transfrer 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 prsents cause de la persistance.
On n'en aurait pas besoin si la classe [Personne] n'avait pas besoin d'tre persiste. On voit donc qu'un objet n'a pas
la mme reprsentation 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] associe
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 donnes
par l'ajout d'une contrainte d'unicit sur la colonne NOM de la table [personne] ;

length=30 fixe 30 le nombre de caractres 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] dsigne une date seule sans heure associe. Les autres types possibles sont [Link]
pour coder une heure et [Link] pour coder une date avec heure ;

Commentons maintenant le reste du code de la classe [Personne] :

ligne 6 : la classe implmente l'interface Serializable. La srialisation d'un objet consiste le transformer en une suite de bits.
La dsrialisation est l'opration inverse. La srialisation / dsrialisation est notamment utilise dans les applications client /
serveur o des objets sont changs via le rseau. Les applications clientes ou serveur sont ignorantes de cette opration
qui est faite de faon transparente par les JVM. Pour qu'elle soit possible, il faut cependant que les classes des objets
changs soit " tagues " 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 paramtres. En effet,
ces deux champs sont grs par la couche JPA et non par l'application ;
lignes 51 et au-del : les mthodes get et set de chacun des champs de la classe. Il est noter que les annotations JPA
peuvent tre places sur les mthodes get des champs au lieu d'tre places sur les champs eux-mmes. La place des
annotations indique le mode que doit utiliser JPA pour accder aux champs :

si les annotations sont mises au niveau champ, JPA accdera directement aux champs pour les lire ou les crire ;

si les annotations sont mises au niveau get, JPA accdera aux champs via les mthodes 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. Place au niveau champ, elle
indique un accs direct aux champs et place au niveau get, un accs aux champs via les get et set. Les autres annotations
doivent alors tre places de la mme faon que l'annotation @Id.

25/290

3.2.2

Configuration de la couche JPA

Les tests de la couche JPA peuvent tre faits avec l'architecture suivante :

Programme de test
console [main]
3

Objets image
de la BD
4

Interface
[JPA]
5

Implmentation
6 [Hibernate]

Couche
[JDBC]

Base de
Donnes
7

en [7] : la base de donnes qui sera gnre partir des annotations de l'entit [Personne] ainsi que de configurations
complmentaires faites dans un fichier appel [[Link]]
en [5, 6] : une couche JPA implmente par Hibernate
en [4] : l'entit [Personne]
en [3] : un programme de test de type console

La configuration de la couche JPA est assure par le fichier [META-INF/[Link]] :

A l'excution, 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.
<!-- cration automatique du schma -->
20.
<property name="[Link]" value="create" />
21.
<!-- Dialecte -->
22.
<property name="[Link]" value="[Link].MySQL5InnoDBDialect" />
23.
<!-- proprits 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'accs aux donnes de notre application :

26/290

Programme de test
console [main]
3

Couche
[JPA/Hibernate]
4

Pool de
connexions
[c3p0] 5

Couche
[JDBC]
6

Base de
Donnes
7

le fichier [[Link]] va configurer les couches [4, 5, 6] ;


[4] : implmentation Hibernate de JPA ;
[5] : Hibernate accde la base de donnes via un pool de connexions. Un pool de connexions est une rserve de
connexions ouvertes avec le SGBD. Un SGBD est accd par de multiples utilisateurs alors mme que pour des raisons de
performances, il ne peut dpasser un nombre limite N de connexions ouvertes simultanment. 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
faon rpte, chaque fois qu'il a besoin de travailler avec la base. Le cot d'ouverture / fermeture d'une connexion n'est
pas ngligeable et c'est l qu'intervient le pool de connexions. Celui-ci va au dmarrage de l'application ouvrir N1
connexions avec le SGBD. C'est lui que l'application demandera une connexion ouverte lorsqu'elle en aura besoin. Celleci sera rendue au pool ds que l'application n'en aura plus besoin, de prfrence le plus vite possible. La connexion n'est
pas ferme et reste disponible pour l'utilisateur suivant. Un pool de connexions est donc un systme 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 :

ligne 2 : la balise racine du fichier XML est <persistence> ;


ligne 3 : <persistence-unit> sert dfinir une unit de persistance. Il peut y avoir plusieurs units de persistance. Chacune
d'elles a un nom (attribut name) et un type de transactions (attribut transaction-type). L'application aura accs l'unit
de persistance via le nom de celle-ci, ici JPA. Le type de transaction RESOURCE_LOCAL indique que l'application gre
elle-mme les transactions avec le SGBD. Ce sera le cas ici. Lorsque l'application s'excute dans un conteneur EJB3, elle
peut utiliser le service de transactions de celui-ci. Dans ce cas, on mettra transaction-type=JTA (Java Transaction API).
JTA est la valeur par dfaut lorsque l'attribut transaction-type est absent ;
ligne 5 : la balise <provider> sert dfinir une classe implmentant l'interface [[Link]],
interface qui permet l'application d'initialiser la couche de persistance. Parce qu'on utilise une implmentation JPA /
Hibernate, la classe utilise ici est une classe d'Hibernate ;
ligne 6 : la balise <properties> introduit des proprits propres au provider particulier choisi. Ainsi selon qu'on a choisi
Hibernate, EclipseLink, Kodo, ... on aura des proprits diffrentes. Celles qui suivent sont propres Hibernate ;
ligne 8 : demande Hibernate d'explorer le classpath du projet pour y trouver les classes ayant l'annotation @Entity afin de
les grer. Les classes @Entity peuvent galement tre dclares par des balises <class>nom_de_la_classe</class>,
directement sous la balise <persistence-unit>. C'est ce que nous ferons avec le provider JPA / EclipseLink ;
les lignes 10-12, ici mises en commentaires configurent les logs console d'Hibernate :

ligne 10 : pour afficher ou non les ordres SQL mis par Hibernate sur le SGBD. Ceci est utile lors de la phase
d'apprentissage. A cause du pont relationnel / objet, l'application travaille sur des objets persistants sur lesquels
elle applique des oprations de type [persist, merge, remove]. Il est trs intressant de savoir quels sont les ordres
SQL rellement mis sur ces oprations. En les tudiant, peu peu on en vient deviner les ordres SQL
qu'Hibernate va gnrer lorsqu'on fait telle opration sur les objets persistants et le pont relationnel / objet
commence prendre consistance dans l'esprit ;

ligne 11 : les ordres SQL affichs sur la console peuvent tre formats joliment pour rendre leur lecture plus
aise ;

ligne 12 : les ordres SQL affichs seront de plus comments ;


les lignes 15-19 dfinissent la couche JDBC (couche [6] dans l'architecture) :

ligne 15 : la classe du pilote JDBC du SGBD, ici MySQL5 ;

ligne 16 : l'url de la base de donnes utilise ;

lignes 17, 18 : l'utilisateur de la connexion et son mot de passe ;


ligne 22 : Hibernate a besoin de connatre le SGBD qu'il a en face de lui. En effet, les SGBD ont tous des extensions SQL
propritaires, une faon propre de grer la gnration automatique des valeurs d'une cl primaire, ... qui font qu'Hibernate
a besoin de connatre le SGBD avec qui il travaille afin de lui envoyer les ordres SQL que celui-ci comprendra.
[MySQL5InnoDBDialect] dsigne le SGBD MySQL5 avec des tables de type InnoDB qui supportent les transactions.
les lignes 24-28 configurent le pool de connexions c3p0 (couche [5] dans l'architecture) :

lignes 24, 25 : le nombre minimal (dfaut 3) et maximal de connexions (dfaut 15) dans le pool. Le nombre initial
de connexions par dfaut est 3 ;

ligne 26 : dure maximale en milli-secondes d'attente d'une demande de connexion de la part du client. Pass ce
dlai, c3p0 lui renverra une exception ;

27/290

ligne 27 : pour accder la BD, Hibernate utilise des ordres SQL prpars (PreparedStatement) que c3p0 peut
mettre en cache. Cela signifie que si l'application demande une seconde fois un ordre SQL prpar dj en cache,
celui-ci n'aura pas besoin d'tre prpar (la prparation d'un ordre SQL a un cot) et celui qui est en cache sera
utilis. Ici, on indique le nombre maximal d'ordres SQL prpars que le cache peut contenir, toutes connexions
confondues (un ordre SQL prpar appartient une connexion) ;

ligne 28 : frquence de vrification 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 prsente des " bugs ", ...) ;
ligne 20 : on demande ici, qu' l'initialisation de l'unit de persistance, la base de donnes image des objets @Entity soit
gnre.

Hibernate a dsormais tous les outils pour mettre les ordres SQL de gnration des tables de la base de donnes :

la configuration des objets @Entity lui permet de connatre les tables gnrer ;

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 gnrer les tables ;
Le fichier [[Link]] utilis ici recre une base neuve chaque nouvelle excution de l'application. Les tables sont recres
(create table) aprs avoir t dtruites (drop table) si elles existaient. On notera que ce n'est videmment pas faire avec une base en
production.

3.2.3

Exemple 2 : relation un--plusieurs

[Link]

Le schma de la base de donnes

1.
alter table jpa06_article
2.
drop
3.
foreign key FKFFBDD9D8ECCE8750;
4.
5.
drop table if exists jpa06_article;
6.
7.
drop table if exists jpa06_categorie;
8.
9.
create table jpa06_article (
10.
id bigint not null auto_increment,
11.
version integer not null,
12.
nom varchar(30),
13.
categorie_id bigint not null,
14.
primary key (id)
15.
) ENGINE=InnoDB;
16.
17.
create table jpa06_categorie (
18.
id bigint not null auto_increment,
19.
version integer not null,
20.
nom varchar(30),
21.
primary key (id)
22.
) ENGINE=InnoDB;
23.
24.
alter table jpa06_article
25.
add index FKFFBDD9D8ECCE8750 (categorie_id),
26.
add constraint FKFFBDD9D8ECCE8750
27.
foreign key (categorie_id)
28. references jpa06_categorie (id);

en [1], la base de donnes et en [2], sa DDL (Definition Data Language) MySQL5 ;

Un article A(id, version, nom) appartient exactement une catgorie C(id, version, nom). Une catgorie 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 matrialise par la cl trangre que possde la table [article] sur la table [categorie] (lignes 24-28 de la
DDL).

[Link]

Les objets @Entity reprsentant la base de donnes

Un article est reprsent par l'@Entity [Article] suivante :


1.
2.
3.
4.

package entites;
...
@Entity

28/290

5. @Table(name="jpa05_hb_article")
6. public class Article implements Serializable {
7.
8.
// champs
9.
@Id
10. @GeneratedValue(strategy = [Link])
11. private Long id;
12.
13. @SuppressWarnings("unused")
14. @Version
15. private int version;
16.
17. @Column(length = 30)
18. private String nom;
19.
20. // relation principale Article (many) -> Category (one)
21. // implmente par une cl trangre (categorie_id) dans Article
22. // 1 Article a ncessairement 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. }

lignes 9-11 : cl primaire de l'@Entity ;


lignes 13-15 : son n de version ;
lignes 17-18 : nom de l'article ;
lignes 20-25 : relation plusieurs--un qui relie l'@Entity Article l'@Entity Categorie :

ligne 23 : l'annotation ManyToOne. Le Many se rapporte l'@Entity Article dans lequel on se trouve et le One
l'@Entity Categorie (ligne 25). Une catgorie (One) peut avoir plusieurs articles (Many) ;

ligne 24 : l'annotation ManyToOne dfinit la colonne cl trangre dans la table [article]. Elle s'appellera (name)
categorie_id et chaque ligne devra avoir une valeur dans cette colonne (nullable=false) ;

ligne 25 : la catgorie laquelle appartient l'article. Lorsqu'un article sera mis dans le contexte de persistance, on
demande ce que sa catgorie n'y soit pas mise immdiatement (fetch=[Link], ligne 23) ;

Une catgorie est reprsente par l'@Entity [Categorie] suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.

package entites;
...
@Entity
@Table(name="jpa05_hb_categorie")
public class Categorie implements Serializable {
// champs
@Id
@GeneratedValue(strategy = [Link])
private Long id;
@SuppressWarnings("unused")
@Version
private int version;
@Column(length = 30)
private String nom;
// relation inverse Categorie (one) -> Article (many) de la relation Article (many) -> Categorie (one)
// cascade insertion Categorie -> insertion Articles
// cascade maj Categorie -> maj Articles
// cascade suppression Categorie -> suppression Articles
@OneToMany(mappedBy = "categorie", cascade = { [Link] })
private Set<Article> articles = new HashSet<Article>();
// constructeurs
public Categorie() {
}

29/290

29.
30.
// getters et setters
31. ...
32.
// toString
33.
public String toString() {
34.
return [Link]("Categorie[%d,%d,%s]", id, version, nom);
35.
}
36.
37.
// association bidirectionnelle Categorie <--> Article
38.
public void addArticle(Article article) {
39.
// l'article est ajout dans la collection des articles de la catgorie
40.
[Link](article);
41.
// l'article change de catgorie
42.
[Link](this);
43.
}
44. }

lignes 8-11 : la cl primaire de l'@Entity ;


lignes 12-14 : sa version ;
lignes 16-17 : le nom de la catgorie ;
lignes 19-24 : l'ensemble (set) des articles de la catgorie ;

ligne 23 : l'annotation @OneToMany dsigne une relation un--plusieurs. Le One dsigne l'@Entity
[Categorie] dans laquelle on se trouve, le Many le type [Article] de la ligne 24 : une (One) catgorie a plusieurs
(Many) articles ;

ligne 23 : l'annotation est l'inverse (mappedBy) de l'annotation ManyToOne place sur le champ categorie de
l'@Entity Article : mappedBy=categorie. La relation ManyToOne place sur le champ categorie de l'@Entity
Article est la relation principale. Elle est indispensable. Elle matrialise la relation de cl trangre qui lie
l'@Entity Article l'@Entity Categorie. La relation OneToMany place 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
catgorie. Sans cette commodit, ces articles seraient obtenus par une requte JPQL ;

ligne 23 : [Link] demande que les oprations (persist, merge, remove) faites sur une @Entity
Categorie soient cascades sur ses articles ;

ligne 24 : les articles d'une catgorie seront placs dans un objet de type Set<Article>. Le type Set n'accepte pas
les doublons. Ainsi on ne peut pas mettre deux fois le mme article dans l'objet Set<Article>. Que veut dire "le
mme article" ? Pour dire que l'article a est le mme que l'article b, Java utilise l'expression [Link](b). Dans la
classe Object, mre de toutes les classes, [Link](b) est vraie si a==b, c.a.d. si les objets a et b ont le mme
emplacement mmoire. On pourrait vouloir dire que les articles a et b sont les mmes s'ils ont le mme nom.
Dans ce cas, le dveloppeur doit redfinir deux mthodes dans la classe [Article] :

equals : qui doit rendre vrai si les deux articles ont le mme nom ;

hashCode : doit rendre une valeur entire identique pour deux objets [Article] que la mthode equals
considre 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 utilise dans diffrents 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 mthode [addArticle] nous permet d'ajouter un article une catgorie. La mthode prend soin de mettre jour
les deux extrmits de la relation OneToMany qui lie [Categorie] [Article] ;

Maintenant que nous avons dfini le rle des entits JPA, nous allons apprendre les manipuler via l'API JPA.

3.3

L'API de la couche JPA

Explicitons l'environnement d'excution d'un client JPA :

Interface [JPA] =
EntityManager

Client JPA
1

Base de
Donnes
4

Objets image de la BD =
Contexte de persistance

30/290

Nous savons que le couche JPA [2] cre un pont objet [3] / relationnel [4]. On appelle " contexte de persistance " l'ensemble des
objets grs par la couche JPA dans le cadre de ce pont objet / relationnel. Pour accder aux donnes du contexte de persistance,
un client JPA [1] doit passer par la couche JPA [2] :
1. il peut crer 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 rfrence 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 prsente au client une interface appele [EntityManager] qui, comme son nom l'indique permet de grer les objets
@Entity du contexte de persistance. Nous prsentons ci-dessous, les principales mthodes de cette interface :
void persist(Object entity)
void remove(Object entity)
<T> T merge(T entity)

<T> T find(Class<T> entityClass, Object


primaryKey)

Query createQuery(String queryText)

Query createNativeQuery(String queryText)


Query createNamedQuery(String name)

met entity dans le contexte de persistance


enlve entity du contexte de persistance
fusionne un objet entity du client non gr par le contexte de persistance avec
l'objet entity dj prsent dans le contexte de persistance et ayant la mme cl
primaire. Le rsultat rendu est l'objet entity du contexte de persistance.
met dans le contexte de persistance, un objet cherch dans la base de donnes
via sa cl primaire. Le type T de l'objet permet la couche JPA de savoir quelle
table requter. L'objet persistant ainsi cr est rendu au client.
cre un objet Query partir d'une requte JPQL (Java Persistence Query
Language). Une requte JPQL est analogue une requte SQL si ce n'est qu'on
requte des objets plutt que des tables.
mthode analogue la prcdente, si ce n'est que queryText est un ordre SQL et
non JPQL.
mthode 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 paramtre de la mthode.

Un objet EntityManager a un cycle de vie qui n'est pas forcment celui de l'application. Il a un dbut et une fin. Ainsi un client
JPA peut travailler successivement avec diffrents objets EntityManager. Le contexte de persistance associ un EntityManager a le
mme 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 ncessaire, synchronis avec la base de donnes puis il n'existe plus. Il faut crer un nouvel EntityManager pour
avoir de nouveau un contexte de persistance.
Le client JPA peut crer 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 lie une unit de persistance prcise. On se rappelle que le fichier de configuration [METAINF/[Link]] permet de dfinir des units 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-daojpa-mysql-01PU")] cre une fabrique d'objets de type EntityManagerFactory capable de fournir des objets EntityManager
destins grer des contextes de persistance lis l'unit de persistance nomme 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 faon suivante :
EntityManager em = [Link]();

Les mthodes suivantes de l'interface [EntityManager] permettent de grer 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 donnes :

si un objet du contexte n'est pas prsent dans la base, il y est mis par une opration SQL INSERT ;

si un objet du contexte est prsent dans la base et qu'il a t modifi depuis qu'il a t lu, une opration
SQL UPDATE est faite pour persister la modification ;

si un objet du contexte a t marqu comme " supprim " l'issue d'une opration remove sur lui, une

31/290

void clear()
void flush()

opration SQL DELETE est faite pour le supprimer de la base ;


le contexte de persistance est vid de tous ses objets mais pas ferm.
le contexte de persistance est synchronis avec la base de donnes de la faon dcrite pour close().

Le client JPA peut forcer la synchronisation du contexte de persistance avec la base de donnes avec la mthode
[EntityManager].flush prcdente. La synchronisation peut tre explicite ou implicite. Dans le premier cas, c'est au client de faire des
oprations flush lorsqu'il veut faire des synchronisations, sinon celles-ci se font certains moments que nous allons prciser. Le
mode de synchronisation est gr par les mthodes suivantes de l'interface [EntityManager] :
void setFlushMode(FlushModeType
flushMode)

FlushModeType getFlushMode()

Il y a deux valeurs possibles pour flushmode :


[Link] (dfaut): la synchronisation a lieu avant chaque requte SELECT
faite sur la base.
[Link] : la synchronisation n'a lieu qu' la fin des transactions sur la
base.
rend le mode actuel de synchronisation

Rsumons. En mode [Link] qui est le mode par dfaut, le contexte de persistance sera synchronis avec la base
de donnes aux moments suivants :
1. avant chaque opration SELECT sur la base
2. la fin d'une transaction sur la base
3. la suite d'une opration flush ou close sur le contexte de persistance
En mode [Link], c'est la mme chose sauf pour l'opration 1 qui n'a pas lieu. Le mode normal d'interaction
avec la couche JPA est un mode transactionnel. Le client fait diverses oprations sur le contexte de persistance, l'intrieur d'une
transaction. Dans ce cas, les moments de synchronisation du contexte de persistance avec la base de donnes sont les cas 1 et 2 cidessus 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 donnes. L'interface Query est la suivante :
3
1
2

32/290

1 - la mthode getResultList execute un SELECT qui ramne 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 lments de la liste L sous la forme
suivante :

1.
2.
3.
4.
5.

Iterator iterator = [Link]();


while ([Link]()) {
// exploiter l'objet [Link]() qui reprsente l'lment courant de la liste
...
}

La liste L peut tre galement exploite avec un for :


1.
2.
3.

for (Object o : L) {
// exploiter objet o
}

3.4

2 - la mthode getSingleResult excute un ordre JPQL / SQL SELECT qui ramne un unique objet ;
3 - la mthode executeUpdate excute un ordre SQL update ou delete et rend le nombre de lignes affectes
l'opration ;
4 - la mthode setParameter(String, Object) permet de donner une valeur un paramtre nomm d'un ordre JPQL
paramtr ;
5 - la mthode setParameter(int, Object) mais le paramtre n'est pas dsign par son nom mais par sa position dans
l'ordre JPQL ;

Les requtes JPQL

Note : le projet Netbeans et le script SQL de la base de donnes de ce paragraphe sont disponibles dans le support du document.
JPQL (Java Persistence Query Language) est le langage de requtes de la couche JPA. Le langage JPQL est apparent au langage
SQL des bases de donnes. 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
[DAO]

Couche
[JPA /
Hibernate]

Couche
[JDBC]

SGBD

BD

La base de donnes qu'on appellera [dbrdvmedecins2] est une base de donnes MySQL5 avec quatre tables :

Elle rassemble des informations permettant de grer les rendez-vous d'un groupe de mdecins.

3.4.1

La table [MEDECINS]

Elle contient des informations sur les mdecins.

33/290

3.4.2

ID : n identifiant le mdecin - cl primaire de la table ;


VERSION : n identifiant la version de la ligne dans la table. Ce nombre est incrment de 1 chaque fois qu'une
modification est apporte la ligne ;
NOM : le nom du mdecin ;
PRENOM : son prnom ;
TITRE : son titre (Melle, Mme, Mr) ;

La table [CLIENTS]

Les clients des diffrents mdecins sont enregistrs dans la table [CLIENTS] :

3.4.3

ID : n identifiant le client - cl primaire de la table ;


VERSION : n identifiant la version de la ligne dans la table. Ce nombre est incrment de 1 chaque fois qu'une
modification est apporte la ligne ;
NOM : le nom du client ;
PRENOM : son prnom ;
TITRE : son titre (Melle, Mme, Mr) ;

La table [CRENEAUX]

Elle liste les crneaux horaires o les RV sont possibles :

34/290

ID : n identifiant le crneau horaire - cl primaire de la table (ligne 8) ;


VERSION : n identifiant la version de la ligne dans la table. Ce nombre est incrment de 1 chaque fois qu'une
modification est apporte la ligne ;
ID_MEDECIN : n identifiant le mdecin auquel appartient ce crneau cl trangre sur la colonne MEDECINS(ID) ;
HDEBUT : heure dbut crneau ;
MDEBUT : minutes dbut crneau ;
HFIN : heure fin crneau ;
MFIN : minutes fin crneau ;

La seconde ligne de la table [CRENEAUX] (cf [1] ci-dessus) indique, par exemple, que le crneau n 2 commence 8 h 20 et se
termine 8 h 40 et appartient au mdecin n 1 (Mme Marie PELISSIER).

3.4.4

La table [RV]

Elle liste les RV pris pour chaque mdecin :

ID : n identifiant le RV de faon unique cl primaire ;


JOUR : jour du RV ;
ID_CRENEAU : crneau horaire du RV - cl trangre sur le champ [ID] de la table [CRENEAUX] fixe la fois le
crneau horaire et le mdecin concern ;
ID_CLIENT : n du client pour qui est faite la rservation cl trangre 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 mme moment pour le mme mdecin. 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 crneau n 20 et le client n 4 le 23/08/2006. La table
[CRENEAUX] nous apprend que le crneau n 20 correspond au crneau horaire 16 h 20 - 16 h 40 et appartient au mdecin n 1
(Mme Marie PELISSIER). La table [CLIENTS] nous apprend que le client n 4 est Melle Brigitte BISTROU.

3.4.5

Gnration de la base

Pour crer les tables et les remplir on pourra utiliser le script [[Link]] (cf support du cours). Avec [WampServer], on
pourra procder comme suit :

35/290

en [1], on clique sur l'icne de [WampServer] et on choisit l'option [PhpMyAdmin] [2],


en [3], dans la fentre qui s'est ouverte, on slectionne le lien [Bases de donnes],

en [2], on cre une base de donnes dont on a donn le nom [4] et l'encodage [5],
en [7], la base a t cre. On clique sur son lien,

en [8], on importe un fichier SQL,


qu'on dsigne dans le systme de fichiers avec le bouton [9],

en [11], on slectionne le script SQL et en [12] on l'excute,


en [13], les quatre tables de la base ont t cres. On suit l'un des liens,

36/290

en [14], le contenu de la table.

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 requtes JPQL qu'il va crire.

3.4.6

La couche [JPA]

Revenons l'architecture de l'exemple :

couche
[DAO]

Couche
[JPA /
Hibernate]

Couche
[JDBC]

SGBD

BD

Nous construisons maintenant le projet Maven de la couche [JPA].

3.4.7

Le projet Netbeans

C'est le suivant :

en [1], on construit un projet Maven de type [Java Application] [2],


en [3], on donne un nom au projet,

37/290

3.4.8

en [4], le projet gnr.

Gnration de la couche [JPA]

Revenons l'architecture que nous devons construire :

couche
[DAO]

Couche
[JPA /
Hibernate]

Couche
[JDBC]

SGBD

BD

Avec Netbeans, il est possible de gnrer automatiquement la couche [JPA] . Il est intressant de connatre ces mthodes de
gnration automatique car le code gnr donne de prcieuses indications sur la faon d'crire des entits JPA.

[Link]

Cration d'une connexion Netbeans la base de donnes

lancer le SGBD MySQL 5 afin que la BD soit disponible,


crer une connexion Netbeans sur la base [dbrdvmedecins2],

dans l'onglet [Services] [1], dans la branche [Databases] [2], slectionner le pilote JDBC MySQL [3],
puis slectionner l'option [4] "Connect Using" permettant de crer une connexion avec une base MySQL,
en [5], donner les informations qui sont demandes. 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,

38/290

[Link]

en [10], la connexion est cre. On y voit les quatre tables de la base de donnes connecte.

Cration d'une unit de persistance

Revenons l'architecture en cours de construction :

couche
[DAO]

Couche
[JPA /
Hibernate]

Couche
[JDBC]

SGBD

BD

Nous sommes en train de construire la couche [JPA]. La configuration de celle-ci est faite dans un fichier [[Link]] dans
lequel on dfinit des units de persistance. Chacune d'elles a besoin des informations suivantes :

les caractristiques JDBC d'accs la base (URL, utilisateur, mot de passe),


les classes qui seront les images des tables de la base de donnes,
l'implmentation JPA utilise. En effet, JPA est une spcification implmente par divers produits. Ici, nous utiliserons
Hibernate.

Netbeans peut gnrer ce fichier de persistance via l'utilisation d'un assistant.

cliquer droit sur le projet et choisir la cration d'une unit de persistance [1-4],
en [5-6], crer une unit de persistance,

39/290

en [7], donner un nom l'unit de persistance que l'on cre,


en [8], choisir l'implmentation JPA Hibernate (JPA 2.1),
en [9], indiquer que les tables de la BD sont dj cres et que donc on ne les cre pas. On valide l'assistant,
en [10], le nouveau projet,
en [11], le fichier [[Link]] a t gnr dans le dossier [META-INF],
en [12], de nouvelles dpendances ont t ajoutes au projet Maven.

Le fichier [META-INF/[Link]] gnr ressemble ceci :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link] xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-rdvmedecins-jpql-hibernate-PU" transaction-type="RESOURCE_LOCAL">
<provider>[Link]</provider>
<properties>
<property name="[Link]" value="jdbc:mysql://localhost:3306/dbrdvmedecins2?
zeroDateTimeBehavior=convertToNull"/>
<property name="[Link]" value="root"/>
<property name="[Link]" value="[Link]"/>
<property name="[Link]" value=""/>
<property name="[Link].provider_class" value="[Link]"/>
</properties>
</persistence-unit>
</persistence>

Il reprend les informations donnes dans l'assistant :

ligne 3 : le nom de l'unit de persistance,

ligne 3 : le type de transactions avec la base de donnes. Ici, RESOURCE_LOCAL indique que l'application va grer ellemme ses transactions,

lignes 6-9 : les proprits JDBC de la source de donnes.


Dans l'onglet [Design], on peut avoir une vue globale du fichier [[Link]] :

40/290

Pour avoir des logs d'Hibernate lors de l'excution du projet, nous compltons le fichier [[Link]] de la faon suivante :
1.
2.

7.
8.
9.
10.
11.
12.
13.
14.
15.

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link] xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-rdvmedecins-jpql-hibernate-PU" transaction-type="RESOURCE_LOCAL">
<provider>[Link]</provider>
<properties>
<property name="[Link]" value="jdbc:mysql://localhost:3306/dbrdvmedecins2?
zeroDateTimeBehavior=convertToNull"/>
<property name="[Link]" value="root"/>
<property name="[Link]" value="[Link]"/>
<property name="[Link]" value=""/>
<property name="[Link].provider_class" value="[Link]"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>

ligne 11 : on demande voir les ordres SQL mis par Hibernate,


ligne 12 : cette proprit permet d'avoir un affichage format de ceux-ci.

3.
4.
5.
6.

Des dpendances ont t ajoutes au projet. Le fichier [[Link]] est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
<modelVersion>4.0.0</modelVersion>
<groupId>[Link]</groupId>
<artifactId>mv-rdvmedecins-jpql-hibernate</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<repositories>
<repository>
<id>unknown-jars-temp-repo</id>
<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>
<url>file:${[Link]}/lib</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>[Link]</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>[Link]</version>
</dependency>

41/290

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>

lignes 16-20 : la dpendance sur Hibernate ;


lignes 21-26 : cette dpendance peut tre supprime ;
lignes 8-14 : cette liste de dpts Maven peut tre supprime ;

On ajoutera la dpendance du pilote JDBC de MySQL (lignes 10-14 ci-dessous) :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.

[Link]

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
<modelVersion>4.0.0</modelVersion>
<groupId>[Link]</groupId>
<artifactId>mv-rdvmedecins-jpql-hibernate</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!-- Hibernate -->
<dependency>
<groupId>[Link]</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>[Link]</version>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency>
</dependencies>
<properties>
<[Link]>UTF-8</[Link]>
<[Link]>1.8</[Link]>
<[Link]>1.8</[Link]>
</properties>
</project>

Gnration des entits JPA

Les entits JPA peuvent tre gnres par un assistant de Netbeans :

42/290

en [1-5], on cre des entits JPA partir d'une base de donnes,

en [6], on slectionne la connexion [dbrdvmedecins2] cre prcdemment,


en [7], on slectionne toutes les tables de la base de donnes associe,
en [8], ne cochez pas la case ;

en [8], on donne un nom aux classes Java associes 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,

43/290

[Link]

en [11], les classes Java cres par l'assistant.

Les entits JPA gnres

L'entit [Medecin] est l'image de la table [MEDECINS]. La classe Java est truffe d'annotations qui rendent le code peu lisible au
premier abord. Si on ne garde que ce qui est essentiel la comprhension du rle de l'entit, on obtient le code suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.

package [Link];
import [Link];
...
@Entity
@Table(name = "medecins")
public class Medecin implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = [Link])
@Column(name = "ID")
private Long id;
@Column(name = "TITRE")
private String titre;
@Column(name = "NOM")
private String nom;
@Column(name = "VERSION")
private int version;
@Column(name = "PRENOM")
private String prenom;
@OneToMany(cascade = [Link], mappedBy = "idMedecin")
private List<Creneau> creneauList;
public Medecin() {
}
public Medecin(Long id) {
[Link] = id;
}
public Medecin(Long id, String titre, String nom, int version, String prenom) {
[Link] = id;
[Link] = titre;
[Link] = nom;
[Link] = version;
[Link] = prenom;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? [Link]() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Medecin)) {
return false;

44/290

58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73. }

}
Medecin other = (Medecin) object;
if (([Link] == null && [Link] != null) || ([Link] != null && ![Link]([Link]))) {
return false;
}
return true;

@Override
public String toString() {
return "[Link][ id=" + id + " ]";
}
// getters et setters
...

ligne 6, l'annotation @Entity fait de la classe [Medecin], une entit JPA, c.a.d. une classe lie une table de BD via l'API
JPA,
ligne 7, le nom de la table de BD associe l'entit JPA. Chaque champ de la table fait l'objet d'un champ dans la classe
Java,
ligne 8, la classe implmente l'interface Serializable. Ceci est ncessaire dans les applications client / serveur, o les entits
sont srialises 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 reconnat pas le fait que la colonne est en fait un colonne de version qui doit tre incrmente chaque
modification de la ligne laquelle elle appartient. Pour lui donner ce rle, 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 prcisent 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 gnrer la cl primaire des lignes qu'elle insrera dans la table [Medecins]. Il y a plusieurs
stratgies possibles. Ici la stratgie [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 trangre sur la table [medecins]. Un crneau appartient un mdecin.
Inversement, un mdecin a plusieurs crneaux qui lui sont associs. On a donc une relation un (mdecin) plusieurs
(crneaux), une relation qualifie par l'annotation @OneToMany par JPA (ligne 28). Le champ de la ligne 26 contiendra
tous les crneaux du mdecin. Ceci sans programmation. Pour comprendre totalement la ligne 28, il nous faudra prsenter
la classe [Creneau] ;
lignes 53-64 : deux entits [Medecin] seront dites gales si elles ont la mme cl primaire [id] ;
lignes 46-51 : le hashCode de l'entit. Ici la mthode assure que deux entits [Medecin] de mme [id] auront le mme
hashCode ;

La classe [Creneau] est la suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.

package [Link];
import [Link];
...
@Entity
@Table(name = "creneaux")
public class Creneau implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = [Link])
@Column(name = "ID")
private Long id;
@Column(name = "MDEBUT")
private int mdebut;
@Column(name = "HFIN")
private int hfin;
@Column(name = "HDEBUT")
private int hdebut;
@Column(name = "MFIN")
private int mfin;
@Column(name = "VERSION")

45/290

29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71. }

private int version;


@OneToMany(cascade = [Link], mappedBy = "idCreneau")
private List<Rv> rvList;
@JoinColumn(name = "ID_MEDECIN", referencedColumnName = "ID")
@ManyToOne(optional = false)
private Medecin idMedecin;
public Creneau() {
}
public Creneau(Long id) {
[Link] = id;
}
public Creneau(Long id, int mdebut, int hfin, int hdebut, int mfin, int version) {
[Link] = id;
[Link] = mdebut;
[Link] = hfin;
[Link] = hdebut;
[Link] = mfin;
[Link] = version;
}
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
return "[Link][ id=" + id + " ]";
}
// getters et setters
...

Nous ne commentons que les nouvelles annotations :


nous avons dit que la table [CRENEAUX] avait une cl trangre vers la table [MEDECINS] : un crneau est associ un
mdecin. Plusieurs crneaux peuvent tre associs au mme mdecin. On a une relation de la table [CRENEAUX] vers la
table [MEDECINS] qui est qualifie de plusieurs (crneaux) un (mdecin). C'est l'annotation @ManyToOne de la
ligne 35 qui sert qualifier la cl trangre,
la ligne 34 avec l'annotation @JoinColumn prcise la relation de cl trangre : la colonne [ID_MEDECIN] de la table
[CRENEAUX] est cl trangre sur la colonne [ID] de la table [MEDECINS],
ligne 36 : une rfrence sur le mdecin propritaire du crneau. On l'obtient l encore sans programmation. On
remarquera le nom du champ : [idMedecin]. Cela fait penser l'identifiant du mdecin alors qu'il s'agit du mdecin luimme ;

Le lien de cl trangre entre l'entit [Creneau] et l'entit [Medecin] est donc matrialis par deux annotations :
dans l'entit [Creneau] :

1.
2.
3.

@JoinColumn(name = "ID_MEDECIN", referencedColumnName = "ID")


@ManyToOne(optional = false)
private Medecin idMedecin;

dans l'entit [Medecin] :

1.
2.

@OneToMany(cascade = [Link], mappedBy = "idMedecin")


private List<Creneau> creneauList;

Les deux annotations refltent la mme relation : celle de la cl trangre 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 ambigut la relation
de cl trangre. La relation @OneToMany est facultative. Si elle est prsente, elle se contente de rfrencer la relation
@ManyToOne laquelle elle est associe. 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 spcifie la cl trangre. Toujours dans
cette mme ligne 1 de l'entit [Medecin], l'attribut cascade=[Link] fixe le comportement de l'entit [Medecin] vis
vis de l'entit [Creneau] :

46/290

si on insre une nouvelle entit [Medecin] dans la base, alors les entits [Creneau] du champ de la ligne 2 doivent tre
insres elles-aussi,
si on modifie une entit [Medecin] dans la base, alors les entits [Creneau] du champ de la ligne 2 doivent tre modifies
elles-aussi,
si on supprime une entit [Medecin] dans la base, alors les entits [Creneau] du champ de la ligne 2 doivent tre
supprimes elles-aussi.

Nous donnons le code des deux autres entits sans commentaires particuliers puisqu'elles n'introduisent pas de nouvelles notations.
L'entit [Client]
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.

package [Link];

les lignes 24-25 refltent la relation de cl trangre entre la table [rv] et la table [clients].

...
@Entity
@Table(name = "clients")
public class Client implements Serializable {
@Id
@GeneratedValue(strategy = [Link])
@Column(name = "ID")
private Long id;
@Column(name = "TITRE")
private String titre;
@Column(name = "NOM")
private String nom;
@Column(name = "VERSION")
private int version;
@Column(name = "PRENOM")
private String prenom;
@OneToMany(cascade = [Link], mappedBy = "idClient")
private List<Rv> rvList;
// constructeurs
...
// getters et setters
...
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
...
}
}

L'entit [Rv] :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.

package [Link];
...
@Entity
@Table(name = "rv")
public class Rv implements Serializable {
@Id
@GeneratedValue(strategy = [Link])
@Column(name = "ID")
private Long id;
@Column(name = "JOUR")
@Temporal([Link])
private Date jour;
@JoinColumn(name = "ID_CRENEAU", referencedColumnName = "ID")
@ManyToOne(optional = false)
private Creneau idCreneau;

47/290

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 trangre qu'a la table [RV] vers la table [CRENEAUX],
lignes 20-22 : qualifient la relation de cl trangre qu'a la table [RV] vers la table [CLIENTS].

La gnration automatique des entits 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 diffrents champs version des entits,
il faut crire des mthodes toString plus explicites que celles gnres,
les entits [Medecin] et [Client] sont analogues. On va les faire driver d'une classe [Personne],
on va supprimer les relations @OneToMany inverses des relations @ManyToOne. Elles ne sont pas indispensables et
elles amnent parfois des complications de programmation. On supprime et l'annotation et le champ annot.

Avec ces spcifications, les diffrentes classes deviennent les suivantes :


La classe Personne est utilise pour reprsenter les mdecins et les clients :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.

package [Link];
import [Link];
import [Link].*;
@MappedSuperclass
public class Personne implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = [Link])
@Column(name = "ID")
private Long id;
@Basic(optional = false)
@Column(name = "TITRE")
private String titre;
@Basic(optional = false)
@Column(name = "NOM")
private String nom;
@Basic(optional = false)
@Column(name = "VERSION")
@Version
private int version;
@Basic(optional = false)
@Column(name = "PRENOM")
private String prenom;
// constructeurs
public Personne() {
}

48/290

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-mme une entit (@Entity). Elle va tre la classe parent d'entits.
L'annotation @MappedSuperClass dsigne cette situation.

L'entit [Client] encapsule les lignes de la table [clients]. Elle drive de la classe [Personne] prcdente :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.

package [Link];

ligne 6 : la classe [Client] est une entit Jpa,


ligne 7 : elle est associe la table [CLIENTS],
ligne 8 : elle drive de la classe [Personne].

import [Link];
import [Link].*;
@Entity
@Table(name = "clients")
public class Client extends Personne implements Serializable {
private static final long serialVersionUID = 1L;
// constructeurs
public Client() {
super();
}
public Client(Long id) {
super(id);
}
public Client(Long id, String titre, String nom, int version, String prenom) {
super(id, titre, nom, version, prenom);
}
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
return [Link]("Client[%s,%s,%s,%s]", getId(), getTitre(), getPrenom(), getNom());
}
}

L'entit [Medecin] qui encapsule les lignes de la table [MEDECINS] suit le mme modle :
1.
2.
3.
4.
5.
6.
7.
8.

package [Link];
import [Link];
import [Link].*;
@Entity
@Table(name = "medecins")
public class Medecin extends Personne implements Serializable {

49/290

9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39. }

private static final long serialVersionUID = 1L;


// constructeurs
public Medecin() {
super();
}
public Medecin(Long id) {
super(id);
}
public Medecin(Long id, String titre, String nom, int version, String prenom) {
super(id, titre, nom, version, prenom);
}
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
return [Link]("Mdecin[%s,%s,%s,%s]", getId(), getTitre(), getPrenom(), getNom());
}

L'entit [Creneau] encapsule les lignes de la table [CRENEAUX] :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.

package [Link];
import [Link];
import [Link];
import [Link].*;
@Entity
@Table(name = "creneaux")
public class Creneau implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = [Link])
@Basic(optional = false)
@Column(name = "ID")
private Long id;
@Basic(optional = false)
@Column(name = "MDEBUT")
private int mdebut;
@Basic(optional = false)
@Column(name = "HFIN")
private int hfin;
@Basic(optional = false)
@NotNull
@Column(name = "HDEBUT")
private int hdebut;
@Basic(optional = false)
@Column(name = "MFIN")
private int mfin;
@Basic(optional = false)
@Column(name = "VERSION")
@Version
private int version;
@JoinColumn(name = "ID_MEDECIN", referencedColumnName = "ID")
@ManyToOne(optional = false)
private Medecin medecin;
// constructeurs
...
// getters et setters
...
@Override
public int hashCode() {
...

50/290

53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65. }

}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
...
}
@Override
public String toString() {
return [Link]("Creneau [%s, %s, %s:%s, %s:%s,%s]", id, version, hdebut, mdebut, hfin, mfin, medecin);
}

les lignes 40-42 modlisent la relation "plusieurs un" qui existe entre la table [creneaux] et la table [medecins] de la base
de donnes : un mdecin a plusieurs crneaux, un crneau appartient un seul mdecin.

L'entit [Rv] encapsule les lignes de la table [RV] :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.

package [Link];

les lignes 27-29 modlisent la relation "plusieurs un" qui existe entre la table [RV] et la table [CLIENTS] (un client peut
apparatre dans plusieurs Rv) de la base de donnes et les lignes 23-25 la relation "plusieurs un" qui existe entre la table
[RV] et la table [CRENEAUX] (un crneau peut apparatre dans plusieurs Rv).

3.4.9

import [Link];
import [Link];
import [Link].*;
@Entity
@Table(name = "rv")
public class Rv implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = [Link])
@Basic(optional = false)
@Column(name = "ID")
private Long id;
@Basic(optional = false)
@Column(name = "JOUR")
@Temporal([Link])
private Date jour;
@JoinColumn(name = "ID_CRENEAU", referencedColumnName = "ID")
@ManyToOne(optional = false)
private Creneau creneau;
@JoinColumn(name = "ID_CLIENT", referencedColumnName = "ID")
@ManyToOne(optional = false)
private Client client;
// constructeurs
...
// getters et setters
...
@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
return [Link]("Rv[%s, %s, %s]", id, creneau, client);
}
}

Le code d'accs aux donnes

Nous allons ajouter maintenant au projet, le code d'accs aux donnes via la couche JPA :

51/290

couche
[console]

Couche
[JPA /
Hibernate]

Couche
[JDBC]

SGBD

BD

La classe [MainJpql] est la suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.

package [Link];

ligne 12 : cration de l'EntityManagerFactory associ l'unit de persistance que nous avons cre prcdemment. le
paramtre de la mthode createEntityManagerFactory est le nom de cette unit de persistance :
1.
2.
3.

import
import
import
import

[Link];
[Link];
[Link];
[Link];

public class MainJpql {


public static void main(String[] args) {
// EntityManagerFactory
EntityManagerFactory emf = [Link]("mv-rdvmedecins-jpql-hibernatePU");
// entityManager
EntityManager em = [Link]();
// scanner clavier
Scanner clavier = new Scanner([Link]);
// boucle de saisie des requtes JPQL
[Link]("Requete JPQL sur la base dbrdvmedecins2 (* pour arrter) :");
String requete = [Link]();
while (![Link]().equals("*")) {
try {
// affichage rsultat requte
for (Object o : [Link](requete).getResultList()) {
[Link](o);
}
} catch (Exception e) {
[Link]("L'exception suivante s'est produite : " + e);
}
// on vide le contexte de persistance
[Link]();
// nouvelle requte
[Link]("---------------------------------------------");
[Link]("Requete JPQL sur la base dbrdvmedecins2 (* pour arrter) :");
requete = [Link]();
}
// fermeture des ressources
[Link]();
[Link]();
}
}

<persistence-unit name="mv-rdvmedecins-jpql-hibernate-PU" transaction-type="RESOURCE_LOCAL">


...
</persistence-unit>

ligne 14 : cration de l'EntityManager qui gre la couche de persistance,


ligne 19 : saisie d'une requte JPQL select,
lignes 23-28 : affichage du rsultat de la requte,
ligne 20 : la saisie s'arrte lorsque l'utilisateur tape *.

52/290

Travail faire : donner les requtes JPQL permettant d'obtenir les informations suivantes :

liste des mdecins dans l'orde dcroissant de leurs noms


liste des mdecins dont titre='Mr'
liste des crneaux horaires de Mme Pelissier
liste des Rv pris dans l'ordre croissant des jours
liste des clients (nom) ayant pris RV avec Mme PELISSIER le 24/08/2006
nombre de clients de Mme PELISSIER le 24/08/2006
les clients n'ayant pas pris de Rdv
les mdecins n'ayant pas de Rdv

On s'inspirera de l'exemple du paragraphe 2.7 de [ref1]. Voici un exemple d'excution :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.

Requete JPQL sur la base dbrdvmedecins2 (* pour arrter) :


select c from Client c
Hibernate:
select
client0_.ID as ID2_,
client0_.NOM as NOM2_,
client0_.PRENOM as PRENOM2_,
client0_.TITRE as TITRE2_,
client0_.version as version2_
from
clients client0_
Client[1,Mr,Jules,MARTIN]
Client[2,Mme,Christine,GERMAN]
Client[3,Mr,Jules,JACQUARD]
Client[4,Melle,Brigitte,BISTROU]

ligne 2 : la requte JPQL,


lignes 3-11 : la requte SQL correspondante,
lignes 12-15 : le rsultat de la requte JPQL.

3.5

Liens entre contexte de persistance et SGBD

Note : jusqu' la fin du chapitre 3, il n'y a pas de projets construire. Il faut simplement lire le cours.
Nous allons tudier une application qui montre les liens entre le contexte de persistance construit par une couche JPA et la base de
donnes laquelle est adosse cette couche JPA. Les lments utiliss par ce paragraphe sont prsents dans le dossier [chap-03] du
support du TD :

3.5.1

L'architecture de l'application

L'application a l'architecture suivante :

Couche
[console]

Objets image
de la BD

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

BD

53/290

3.5.2

La base de donnes

La base de donnes est une base MySQL et s'appelle [dbpersonnes2]. Son script de gnration se trouve dans le dossier [database] :

Travail : crez la base de donnes [dbpersonnes2] avec WampServer et PhpMyAdmin. La base de donnes gnre est sans tables.
Nous allons gnrer celles-ci partir des entits JPA.

3.5.3

Le projet Maven [mv-personnes]

Nous allons utiliser le projet Maven [mv-personnes] suivant :

3.5.4

le package [jpa] implmente la couche JPA ;


le package [main] implmente des classes de test de la couche JPA ;

Configuration Maven

Le fichier [[Link]] qui configure le projet Maven [mv-personnes] est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
<modelVersion>4.0.0</modelVersion>
<groupId>[Link]</groupId>
<artifactId>mv-personnes</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!-- hibernate -->
<dependency>
<groupId>[Link]</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>[Link]</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency>
</dependencies>
<properties>
<[Link]>UTF-8</[Link]>

54/290

24.
<[Link]>1.8</[Link]>
25.
<[Link]>1.8</[Link]>
26.
</properties>
27. </project>

3.5.5

lignes 10-14 : implmentation JPA / Hibernate ;


lignes 16-20 : pilote JDBC du SGBD MySQL ;

Configuration de la couche [jpa]

La couche [jpa] est configure par le fichier [META-INF/[Link]] suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.

3.5.6

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link] xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-personnesPU" transaction-type="RESOURCE_LOCAL">
<provider>[Link]</provider>
<class>[Link]</class>
<properties>
<property name="[Link]" value="jdbc:mysql://localhost:3306/dbpersonnes2?
zeroDateTimeBehavior=convertToNull"/>
<property name="[Link]" value="root"/>
<property name="[Link]" value="[Link]"/>
<property name="[Link]" value=""/>
<property name="[Link].provider_class" value="[Link]"/>
<property name="[Link]" value="drop-and-create"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="use_sql_comments" value="true"/>
</properties>
</persistence-unit>
</persistence>

ligne 12 : le mode [drop-and-create] d'Hibernate va crer les tables de la base de donnes [dbpersonnes2] (ligne 7)
chaque instanciation de la couche JPA. Normalement, une unique fois au dmarrage de l'application. Si les tables existent
dj, elle seront tout d'abord supprimes (drop) avant d'tre recres (create) ;
lignes 13-15 : demandent ce que les ordres SQL mis par Hibernate soient logus sur la console ;

L'entit JPA [Personne]

La base de donnes contiendra une table [PERSONNES] contenant des informations sur des personnes. L'entit JPA [Personne]
correspondante est la suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.

package jpa;
import
import
import
import
import
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

@Entity
@Table(name = "personnes")
public class Personne implements Serializable {
// cl primaire
@Id
@Column(name = "ID", nullable = false)
@GeneratedValue(strategy = [Link])
private Integer id;
// n de version
@Column(name = "VERSION", nullable = false)
@Version
private int version;
// nom
@Column(name = "NOM", length = 30, nullable = false, unique = true)
private String nom;
// prnom
@Column(name = "PRENOM", length = 30, nullable = false)

55/290

37.
private String prenom;
38.
39.
// date de naissance
40.
@Column(name = "DATENAISSANCE", nullable = false)
41.
@Temporal([Link])
42.
private Date datenaissance;
43.
44.
// tat marital
45.
@Column(name = "MARIE", nullable = false)
46.
private boolean marie;
47.
48.
// nombre d'enfants
49.
@Column(name = "NBENFANTS", nullable = false)
50.
private int nbenfants;
51.
52.
// constructeurs
53.
public Personne() {
54.
}
55.
56.
public Personne(String nom, String prenom, Date datenaissance, boolean marie, int nbenfants) {
57.
[Link] = nom;
58.
[Link] = prenom;
59.
[Link] = datenaissance;
60.
[Link] = marie;
61.
[Link] = nbenfants;
62.
}
63.
64.
// toString
65.
@Override
66.
public String toString() {
67.
return [Link]("[%d,%d,%s,%s,%s,%s,%d]", id, version, nom, prenom,
68.
new SimpleDateFormat("dd/MM/yyyy").format(datenaissance), marie, nbenfants);
69.
}
70.
71.
// getters and setters
72. ...
73. }

Les champs de cette entit ont t enrichis d'informations qui vont permettre de crer la table [personnes] lie l'entit (ligne 17) :

ligne 33 : le nom fait au plus 30 caractres et doit tre unique dans la table ;

ligne 36 : le prnom fait au plus 30 caractres ;

lignes 32-50 : tous les champs ont l'attribut [nullable=false], ce qui va entraner que les colonnes de la table
[PERSONNES] qui leur sont lies auront, elles, l'attribut SQL [NOT NULL] ;

3.5.7

Cration de la table [PERSONNES]

La table [[Link]] est cre par l'excution de la classe [Create] suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.

package main;

ligne 11 : on cre l'EntityManagerFactory correspondant l'unit de persistance [mv-personnesPU] dfinie dans le fichier
[[Link]] que nous avons prsent ;
ligne 13 : la cration d'un [EntityManager] partir de l'[EntityManagerFactory] prcdent va crer le contexte de
persistance et la couche JPA. A cause de la proprit [drop-and-create] prsente dans le fichier [[Link]], la table
[PERSONNES] associe l'entit JPA [Personne] va tre cre. Les attributs des diffrentes colonnes de cette table vont
tre tirs des attributs donns aux champs de l'entit JPA [Personne] ;
lignes 15-16 : on libre les ressources ;

import [Link];
import [Link];
import [Link];
public class Create {
public static void main(String[] args) {
// EntityManagerFactory
EntityManagerFactory emf = [Link]("mv-personnesPU");
// cration contexte de persistance
EntityManager em = [Link]();
// fermeture contexte de persistance
[Link]();
[Link]();
}
}

Lorsqu'on excute cette classe, on a les logs suivants dans la console :


1.

Hibernate:

56/290

2.
drop table if exists personnes
3. Hibernate:
4.
create table personnes (
5.
ID integer not null auto_increment,
6.
DATENAISSANCE date not null,
7.
MARIE bit not null,
8.
NBENFANTS integer not null,
9.
NOM varchar(30) not null,
10.
PRENOM varchar(30) not null,
11.
VERSION integer not null,
12.
primary key (ID)
13.
)
14. Hibernate:
15.
alter table personnes
16. add constraint UK_sc8py9yq9d9lpvyi6xv4vfqod unique (NOM)

ligne 2 : la table [personnes] est supprime si elle existe ;


lignes 4-13 : cration de la table [personnes] ;
lignes 6-11 : on remarquera que toutes les colonnes ont l'attribut [not null], consquence de l'attribut [nullable=false] des
champs de l'entit JPA [Personne] ;
ligne 9 : l'attribut [varchar(30)] de la colonne [NOM] provient de l'attribut [length=30] du champ [nom] de l'entit
[Personne] ;
ligne 10 : idem pour le prnom ;
lignes 15-16 : l'attribut [UNIQUE] attribu la colonne [NOM] provient de l'attribut [unique=true] du champ
[[Link]] ;

On peut vrifier avec PhpMyAdmin que la table [personnes] a bien t cre :

3.5.8

Tests des liens entre contexte de persistance et base de donnes

La classe [Main] a pour but de montrer les liens entre contexte de persistance et base de donnes. Son code est le suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.

package main;
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

public class Main {


// constantes
// Contexte de persistance
private final static String TABLE_NAME = "PERSONNES";
private static EntityManagerFactory emf;
private static Personne p1;
public static void main(String[] args) throws Exception {
// EntityManagerFactory
emf = [Link]("mv-personnesPU");
// nettoyage base
log("clean");
clean();
// dump

57/290

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.
// dbut 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.
// dbut transaction
64.
EntityTransaction tx = [Link]();
65.
[Link]();
66.
// supprimer les lments de la table PERSONNES
67.
[Link]("delete from " + TABLE_NAME).executeUpdate();
68.
// fin transaction
69.
[Link]();
70.
// fin contexte
71.
[Link]();
72.
}
73.
74.
// logs
75.
private static void log(String message) {
76.
[Link]("main : ----------- " + message);
77.
}
78.
79.
// gestion d'objets persists
80.
public static void test1() throws ParseException {
81.
...
82.
}
83.
84.
// gestion d'objets persists
85.
public static void test2() throws ParseException {
86. ...
87.
}
88. }

lignes 60-71 : la mthode [clean] supprime le contenu de la table [personnes] ;


ligne 67 : on utilise un ordre SQL natif et non un ordre JPQL ;
lignes 44-56 : la mthode [dump] affiche le contenu de la table [personnes]. Il faut se rappeler ici que toute opration
[SELECT] provoque une syncronisation du contexte de persistance avec la base de donnes. Donc avant que l'opration
SQL SELECT ne soit rellement excute, Hibernate met des ordres SQL INSERT, UPDATE, DELETE pour que la
base de donnes soit le fidle reflet du contexte de persistance ;
on notera :
qu'au dbut de chacune de ces mthodes, un contexte de persistance neuf (et donc vide) est cr ;
qu' la fin de chacune d'elles le contexte de persistance est ferm, ce qui provoque de nouveau une synchronisation du
contexte de persistance avec la base de donnes ;
lignes 23-36 : l'application excute successivement les mthodes [clean], [dump], [test1] et [test2] ;
on notera que comme l'application [Create], l'application [Main] provoque le [drop-and-create] de la table [personnes].
Cela est indpendant du code de ces applications et provient de la prsence de la proprit [drop-and-create] prsente dans
le fichier [[Link]] ;

Les logs de l'excution des lignes 23-28 sont les suivantes :

58/290

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.

Hibernate:
drop table if exists personnes
Hibernate:
create table personnes (
ID integer not null auto_increment,
DATENAISSANCE date not null,
MARIE bit not null,
NBENFANTS integer not null,
NOM varchar(30) not null,
PRENOM varchar(30) not null,
VERSION integer not null,
primary key (ID)
)
Hibernate:
alter table personnes
add constraint UK_sc8py9yq9d9lpvyi6xv4vfqod unique (NOM)
main : ----------- clean
Hibernate:
delete
from
PERSONNES
main : ----------- dump
Hibernate:
select
personne0_.ID as ID1_0_,
personne0_.DATENAISSANCE as DATENAIS2_0_,
personne0_.MARIE as MARIE3_0_,
personne0_.NBENFANTS as NBENFANT4_0_,
personne0_.NOM as NOM5_0_,
personne0_.PRENOM as PRENOM6_0_,
personne0_.VERSION as VERSION7_0_
from
personnes personne0_
order by
personne0_.NOM asc

lignes 1-16 : excution de la proprit [drop-and-create] du fichier [[Link]] ;


lignes 18-21 : excution de l'ordre SQL natif DELETE de la mthode [clean] ;
lignes 23-34 : l'excution de l'ordre JPQL de la mthode [dump] entrane la gnration d'un ordre SQL SELECT ;

[Link]

La mthode [test1]

La mthode [test1] est la suivante :


1.
// gestion d'objets persists
2.
public static void test1() throws ParseException {
3.
// contexte de persistance
4.
EntityManager em = [Link]();
5.
// cration 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.
// dbut transaction
9.
EntityTransaction tx = [Link]();
10.
[Link]("dbut transaction");
11.
[Link]();
12.
// persistance des personnes
13.
// les logs montrent que l'opration SQL INSERT est immdiatement gnre aprs l'opration persist
14.
// probablement pour avoir la cl primaire
15.
[Link]([Link]("Personne p1 %s non persiste", p1));
16.
[Link]("[Link](p1)");
17.
[Link](p1);
18.
[Link]([Link]("Personne p1 %s persiste", p1));
19.
// personne p2
20.
// INSERT est gnr ds l'opration persist
21.
[Link]([Link]("Personne p2 %s non persiste", p2));
22.
[Link]("[Link](p2)");
23.
[Link](p2);
24.
[Link]([Link]("Personne p2 %s persiste", p2));
25.
[Link](true);
26.
[Link]([Link]("Personne p2 %s modifie", p2));
27.
// l'opration DELETE lie l'opration remove n'est faite qu' la fin de la transaction
28.
[Link]("[Link](p2)");
29.
[Link](p2);
30.
[Link]([Link]("Personne p2 %s supprime", 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. }

59/290

L'excution de cette mthode produit les logs suivants :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.

main : ----------- test1


dbut transaction
Personne p1 [null,0,Martin,Paul,31/01/2000,true,2] non persiste
[Link](p1)
Hibernate:
insert
into
personnes
(DATENAISSANCE, MARIE, NBENFANTS, NOM, PRENOM, VERSION)
values
(?, ?, ?, ?, ?, ?)
Personne p1 [1,0,Martin,Paul,31/01/2000,true,2] persiste
Personne p2 [null,0,Durant,Sylvie,05/07/2001,false,0] non persiste
[Link](p2)
Hibernate:
insert
into
personnes
(DATENAISSANCE, MARIE, NBENFANTS, NOM, PRENOM, VERSION)
values
(?, ?, ?, ?, ?, ?)
Personne p2 [2,0,Durant,Sylvie,05/07/2001,false,0] persiste
Personne p2 [2,0,Durant,Sylvie,05/07/2001,true,0] modifie
[Link](p2)
Personne p2 [2,0,Durant,Sylvie,05/07/2001,true,0] supprime
fin transaction
Hibernate:
update
personnes
set
DATENAISSANCE=?,
MARIE=?,
NBENFANTS=?,
NOM=?,
PRENOM=?,
VERSION=?
where
ID=?
and VERSION=?
Hibernate:
delete
from
personnes
where
ID=?
and VERSION=?
Hibernate:
select
personne0_.ID as ID1_0_,
personne0_.DATENAISSANCE as DATENAIS2_0_,
personne0_.MARIE as MARIE3_0_,
personne0_.NBENFANTS as NBENFANT4_0_,
personne0_.NOM as NOM5_0_,
personne0_.PRENOM as PRENOM6_0_,
personne0_.VERSION as VERSION7_0_
from
personnes personne0_
order by
personne0_.NOM asc
[1,1,P1,Paul,31/01/2000,true,2]

ligne 3 des rsultats : la personne p1 pas encore persiste (id=null) ;


ligne 4 des rsultats : la personne p1 est ajoute au contexte de persistance ;
lignes 5-11 des rsultats : cet ajout au contexte de persistance provoque une opration SQL INSERT sur la base de
donnes ;
ligne 12 des rsultats : la personne p1 qui appartient dsormais au contexte de persistance a rcupr une cl primaire
(id=1) ;
lignes 12-22 des rsultats : la mme opration est faite sur la personne p2 ;
ligne 23 des rsultats : la personne p2 est modifie dans son contexte de persistance (marie=true) ;
ligne 24 des rsultats : la personne p2 est enleve du contexte de persistance ;
ligne 26 des rsultats : on termine la transaction (ligne 35 du code). Ceci a pour effet de forcer une synchronisation entre
contexte de persistance et base de donnes ;
lignes 27-39 des rsultats : une opration SQL UPDATE est faite sur la base de donnes. Ce que ne montrent pas les logs
est que cette opration est faite sur la personne p1 (ligne 32 du code) ;

60/290

lignes 40-46 des rsultats : une opration SQL INSERT est faite sur la base de donnes pour supprimer la personne p2. I y
a ici optimisation : on avait fait une modification de p2 suivie de sa suppression. Au moment de la synchronisation, seule
l'opration SQL DELETE est faite sur la personne p2 ;
ligne 37 du code des rsultats : le contexte de persistance est ferm. Cela a pour effet de le vider. Lorsque commencera la
mthode [test2], on partira d'un contexte de persistance vide. Les personnes [p1, p2] existent toujours mais ne sont pas
dans le contexte de persistance ;
lignes 47-59 des rsultats : une opration SQL SELECT est faite sur la base de donnes, provoque par la mthode [dump]
de la ligne 39 du code ;
ligne 60 des rsultats : on voit la personne p1 avec son nom modifi (P1 au lieu de p1). La personne p2 n'est plus l ;

[Link]

La mthode [test2]

La mthode [test2] est la suivante :


1.
// gestion d'objets persists
2.
public static void test2() throws ParseException {
3.
// contexte de persistance
4.
EntityManager em = [Link]();
5.
// dbut transaction
6.
EntityTransaction tx = [Link]();
7.
[Link]("dbut transaction");
8.
[Link]();
9.
// on modifie la personne p1 actuellement dtache
10.
[Link]([Link]("Personne p1 %s actuelle non persiste", p1));
11.
[Link](false);
12.
[Link]([Link]("Personne p1 %s nouvelle non persiste", p1));
13.
// on rattache la personne P1
14.
[Link]("[Link](p1)");
15.
Personne p1b = [Link](p1);
16.
[Link]([Link]("Personne p1b %s attache", p1b));
17.
// fin transaction
18.
[Link]("fin transaction");
19.
[Link]();
20.
// fin contexte
21.
[Link]();
22.
// on affiche la table
23.
dump();
24. }

Son excution produit les logs suivants :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.

main : ----------- test2


dbut transaction
Personne p1 [1,1,P1,Paul,31/01/2000,true,2] actuelle non persiste
Personne p1 [1,1,P1,Paul,31/01/2000,false,2] nouvelle non persiste
[Link](p1)
Hibernate:
select
personne0_.ID as ID1_0_0_,
personne0_.DATENAISSANCE as DATENAIS2_0_0_,
personne0_.MARIE as MARIE3_0_0_,
personne0_.NBENFANTS as NBENFANT4_0_0_,
personne0_.NOM as NOM5_0_0_,
personne0_.PRENOM as PRENOM6_0_0_,
personne0_.VERSION as VERSION7_0_0_
from
personnes personne0_
where
personne0_.ID=?
Personne p1b [1,1,P1,Paul,31/01/2000,false,2] attache
fin transaction
Hibernate:
update
personnes
set
DATENAISSANCE=?,
MARIE=?,
NBENFANTS=?,
NOM=?,
PRENOM=?,
VERSION=?
where
ID=?
and VERSION=?
Hibernate:
select
personne0_.ID as ID1_0_,
personne0_.DATENAISSANCE as DATENAIS2_0_,
personne0_.MARIE as MARIE3_0_,
personne0_.NBENFANTS as NBENFANT4_0_,
personne0_.NOM as NOM5_0_,

61/290

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]

ligne 4 du code : un nouveau contexte de persistance est cr ;


ligne 2 des rsultats : dbut d'une transaction (ligne 6 du code) ;
ligne 3 des rsultats : la personne p1 actuelle. On rappelle qu' l'issue de la mthode [test1], elle n'tait plus dans le
contexte de persistance ;
ligne 4 des rsultats : la personne p1 est modifie (marie=false) ;
ligne 5 des rsultats : la personne p1 est place dans le nouveau contexte de persistance par une opration [merge]. C'est la
seule faon de l'y mettre. L'opration [persist] n'est plus possible puisque la personne p1 a une cl primaire ;
lignes 6-18 : l'opration [merge] sur le contexte de persistance provoque un ordre SQL SELECT sur la base de donnes :
la personne p1 est cherche dans la base de donnes (via sa cl primaire). Les champs de la personne p1 ramene de la
base de donnes dans le contexte de persistance sont ensuite initialiss par les valeurs des champs de la personne p1 qui a
subi l'opration [merge]. Le rsultat est une personne [p1b] (ligne 19 des rsultats) qui elle, appartient dsormais au
contexte de persistance ;
ligne 20 des rsultats : la transaction commence ligne 2 des rsultats est ferme. Cela provoque la synchronisation du
contexte de persistance avec la base de donnes ;
lignes 21-33 des rsultats : la personne [p1b] prsente dans le contexte de persistance est mmorise dans la base par une
opration SQL UPDATE. En effet, cette personne existe dj dans la base. Il faut simplement la mettre jour ;
lignes 34-46 des rsultats : une opration SQL SELECT est faite, cause par l'instruction [dump] de la ligne 23 du code ;
ligne 47 des rsultats : on voit la personne p1 avec son nouveau statut marital (false au lieu de true initialement) ;

62/290

4 Version 1 : Architecture Spring / JPA


Note : le script SQL de la base de donnes de ce paragraphe est disponible dans le support du cours (voir site du document).
On se propose dcrire une application console ainsi qu'une application graphique permettant dtablir le bulletin de salaire des
assistantes maternelles employes par la "Maison de la petite enfance" d'une commune. Cette application aura l'architecture
suivante :

Couche
[ui]

Couche
[metier]

Objets image
de la BD

Couche
[DAO]

Interface
[JPA]

Implmentation
[Hibernate /
EclipseLink]

Couche
[JDBC]

BD

Spring

Note : lire jusqu'au paragraphe 4.4 inclus, puis passer la pratique.

4.1

La base de donnes

Les donnes statiques utiles pour construire la fiche de paie seront places dans une base de donnes que nous dsignerons par la
suite dbpam. Cette base de donnes pourrait avoir les tables suivantes :
Table EMPLOYES : rassemble des informations sur les diffrentes assistantes maternelles
Structure :
ID
VERSION
SS
NOM
PRENOM
ADRESSE
VILLE
CODEPOSTAL
INDEMNITE_ID

cl primaire
n de version augmente chaque modification de la ligne
numro de scurit sociale de l'employ - unique
nom de l'employ
son prnom
son adresse
sa ville
son code postal
cl trangre sur le champ [ID] de la table [INDEMNITES]

Son contenu pourrait tre le suivant :

Table COTISATIONS : rassemble des pourcentages ncessaires au calcul des cotisations sociales
Structure :
ID
VERSION
CSGRDS
CSGD
SECU
RETRAITE

cl primaire
n de version augmente chaque modification de la ligne
pourcentage : contribution sociale gnralise + contribution au remboursement de la dette
sociale
pourcentage : contribution sociale gnralise dductible
pourcentage : scurit sociale, veuvage, vieillesse
pourcentage : retraite complmentaire + assurance chmage

Son contenu pourrait tre le suivant :

63/290

Les taux des cotisations sociales sont indpendants du salari. La table prcdente n'a qu'une ligne.
Table INDEMNITES : rassemble les lments permettant le calcul du salaire payer.

ID
VERSION
INDICE
BASEHEURE
ENTRETIENJOUR
REPASJOUR
INDEMNITESCP

cl primaire
n de version augmente chaque modification de la ligne
indice de traitement - unique
prix net en euro dune heure de garde
indemnit dentretien en euro par jour de garde
indemnit de repas en euro par jour de garde
indemnit de congs pays. C'est un pourcentage appliquer au salaire
de base.

Son contenu pourrait tre le suivant :

On notera que les indemnits peuvent varier d'une assistante maternelle une autre. Elles sont en effet associes une assistante
maternelle prcise 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).

4.2

Mode de calcul du salaire d'une assistante maternelle

Nous prsentons maintenant le mode de calcul du salaire mensuel d'une assistante maternelle. Il ne prtend pas tre celui utilis
dans la ralit. Nous prenons pour exemple, le salaire de Mme Marie Jouveinal qui a travaill 150 h sur 20 jours pendant le mois
payer.
Les lments suivants sont pris en compte
:

[TOTALHEURES]: total des heures


travailles dans le mois

[TOTALHEURES]=150
[TOTALJOURS]= 20

[TOTALJOURS]: total des jours travaills


dans le mois

Le salaire de base de l'assistante


maternelle est donn par la formule
suivante :

[SALAIREBASE]=([TOTALHEURES]*[BASEHEURE])*
(1+[INDEMNITESCP]/100)

[SALAIREBASE]=(150*[2.1])*(1+0.15)= 362,25

Un certain nombre de cotisations sociales


doivent tre prleves sur ce salaire de
base :

Contribution sociale gnralise et


contribution au remboursement de la dette
sociale : [SALAIREBASE]*[CSGRDS/100]

CSGRDS : 12,64

Contribution sociale gnralise


dductible : [SALAIREBASE]*[CSGD/100]

Scurit sociale : 34,02

Scurit sociale, veuvage, vieillesse :


[SALAIREBASE]*[SECU/100]

CSGD : 22,28

Retraite : 28,55

Retraite Complmentaire + AGPF + Assurance


Chmage : [SALAIREBASE]*[RETRAITE/100]

Total des cotisations sociales :

[COTISATIONSSOCIALES]=[SALAIREBASE]*(CSGRD
S+CSGD+SECU+RETRAITE)/100

[COTISATIONSSOCIALES]=97,48

Par ailleurs, l'assistante maternelle a droit,


chaque jour travaill, une indemnit
d'entretien ainsi qu' une indemnit de

[INDEMNITS]=[TOTALJOURS]*(ENTRETIENJOUR+R
EPASJOUR)

[INDEMNITES]=104

64/290

Les lments suivants sont pris en compte


:

[TOTALHEURES]: total des heures


travailles dans le mois

[TOTALHEURES]=150
[TOTALJOURS]= 20

[TOTALJOURS]: total des jours travaills


dans le mois

repas. A ce titre elle reoit les indemnits


suivantes :
Au final, le salaire net payer l'assistante
maternelle est le suivant :

4.3

[SALAIREBASE]-[COTISATIONSSOCIALES]+
[INDEMNITS]

[salaire NET]=368,77

Fonctionnement de l'application console

Voici un exemple d'excution de l'application console dans une fentre Dos :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.

dos>java -jar [Link] 254104940426058 150 20


Valeurs saisies :
N de scurit sociale de l'employ : 254104940426058
Nombre d'heures travailles : 150
Nombre de jours travaills : 20
Informations Employ :
Nom : Jouveinal
Prnom : Marie
Adresse : 5 rue des Oiseaux
Ville : St Corentin
Code Postal : 49203
Indice : 2
Informations Cotisations :
CSGRDS : 3.49 %
CSGD : 6.15 %
Retraite : 7.88 %
Scurit sociale : 9.39 %
Informations Indemnits :
Salaire horaire : 2.1 euro
Entretien/jour : 2.1 euro
Repas/jour : 3.1 euro
Congs Pays : 15.0 %
Informations Salaire :
Salaire de base : 362.25 euro
Cotisations sociales : 97.48 euro
Indemnits d'entretien : 42.0 euro
Indemnits de repas : 62.0 euro
Salaire net : 368.77 euro

On crira un programme qui recevra les informations suivantes :


1.
2.
3.

n de scurit sociale de l'assistante maternelle ( 254104940426058 dans l'exemple - ligne 1) ;


nombre total d'heures travailles (150 dans l'exemple - ligne 1) ;
nombre total de jours travaills (20 dans l'exemple - ligne 1) ;

On voit que :

lignes 9-14 : affichent les informations concernant l'employ dont on a donn le n de scurit sociale ;
lignes 17-20 : affichent les taux des diffrentes cotisations ;
lignes 23-26 : affichent les indemnits associes l'indice de traitement de l'employ (ici l'indice 2) ;
lignes 29-33 : affichent les lments constitutifs du salaire payer ;

L'application signale les erreurs ventuelles :


Appel sans paramtres :
dos>java -jar [Link]
Syntaxe : pg num_securite_sociale nb_heures_travailles nb_jours_travaills

Appel avec des donnes errones :


dos>java -jar [Link]
Le nombre d'heures travailles [150x] est erron

254104940426058 150x 20x

65/290

Le nombre de jours travaills [20x] est erron

Appel avec un n de scurit sociale erron :


dos>java -jar [Link] xx 150 20
L'erreur suivante s'est produite : L'employ de n[xx] est introuvable

4.4

Fonctionnement de l'application graphique

L'application graphique permet le calcul des salaires des assistantes maternelles au travers d'un formulaire Swing :

6
1
2

3
4

les informations passes en paramtres 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 diffrents lments du salaire jusqu'au salaire net payer [5] ;

La liste droulante [1, 6] ne prsente pas les ns SS des employs mais les noms et prnoms de ceux-ci. On fait ici l'hypothse qu'il
n'y a pas deux employs de mmes nom et prnom.

4.5

Cration de la base de donnes

Nous lanons WampServer et utilisons l'outil PhpMyAdmin [1] :

66/290

en [2-4], on importe un script SQL ;

en [5-6], on dsigne le script SQL et on l'excute ;

67/290

en [6], la base de donnes [dbpam_hibernate] a t cre. Son contenu est le suivant :

table EMPLOYES

table INDEMNITES

table COTISATIONS

4.6
4.6.1

Implmentation JPA
Couche JPA / Hibernate

Nous allons configurer la couche JPA dans l'environnement suivant :

Programme
console

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Base de
donnes

Nous crons le projet Maven [mv-pam-jpa-hibernate] [1] en suivant la procdure dcrite au paragraphe 2.1.1, page 11 :

68/290

Dans l'architecture de notre application il nous faut les lments suivants :

[Link]

la base de donnes,
le pilote JDBC du SGBD MySQL,
la couche JPA / Hibernate (entits et configuration),
le programme console de test.

Configuration de la couche JPA

La liaison entre la couche JDBC et la base de donnes se fait dans le fichier [META-INF / [Link]] qui configure la couche
JPA. Ce fichier peut tre construit avec Netbeans :

dans l'onglet [services] [1], on se connecte la base de donnes avec le pilote JDBC de MySQL [2],
en [3], le nom de la base de donnes 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 russi.

69/290

la connexion apparat en [9],


en [10-12], on ajoute un nouvel lment au projet,

en [13] on choisit la catgorie [Persistence] et en [14] l'lment [Persistence Unit],


en [15], on donne un nom cette unit de persistance,
en [17], on dsigne la connexion que nous venons de crer vers la base MySQL,
en [16], on choisit une implmentation Hibernate,
en [18], on indique qu' l'instanciation de la couche JPA, celle-ci doit construire (create) les tables correspondant aux
entits JPA du projet.

La fin de l'assistant gnre le fichier [[Link]] :

70/290

le fichier apparat dans une nouvelle branche du projet, dans un dossier [META-INF] [19],
en [20], des dpendances sont apparues. Ce sont celles du fournisseur JPA / Hibernate.

Le contenu du fichier [[Link]] gnr est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link] xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
<provider>[Link]</provider>
<properties>
<property name="[Link]" value="jdbc:mysql://localhost:3306/dbpam_hibernate?
zeroDateTimeBehavior=convertToNull"/>
<property name="[Link]" value="root"/>
<property name="[Link]" value="[Link]"/>
<property name="[Link]" value=""/>
<property name="[Link].provider_class" value="[Link]"/>
<property name="[Link]" value="create"/>
</properties>
</persistence-unit>
</persistence>

ligne 3 : le nom de l'unit de persistance et le type de transactions. RESOURCE_LOCAL indique que le projet gre luimme les transactions. C'est ici le programme console qui devra le faire,
ligne 4 : l'implmentation JPA utilise est Hibernate,
lignes 6-9 : les caractristiques JDBC de la connexion la base de donnes,
ligne 11 : demande la cration des tables correspondant aux entits 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,

Nous ajouterons trois autres proprits la configuration d'Hibernate :


1.
2.
3.

<property name="hibernate.show_sql" value="true"/>


<property name="hibernate.format_sql" value="true"/>
<property name="use_sql_comments" value="true"/>

Elles demandent Hibernate d'afficher les ordres SQL qu'il envoie la base de donnes. Le fichier complet est donc le suivant :
1.
2.
3.
4.
5.
6.
7.
8.

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link] xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
<provider>[Link]</provider>
<properties>
<property name="[Link]" value="jdbc:mysql://localhost:3306/dbpam_hibernate?
zeroDateTimeBehavior=convertToNull"/>
<property name="[Link]" value="root"/>
<property name="[Link]" value="[Link]"/>

71/290

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"/>
13.
<property name="hibernate.format_sql" value="true"/>
14.
<property name="use_sql_comments" value="true"/>
15.
</properties>
16.
</persistence-unit>
17. </persistence>

[Link]

Les dpendances

Revenons l'architecture du projet :

Programme
console

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Base de
donnes

Nous avons configur la couche JPA via le fichier [[Link]]. L'implmentation choisie a t Hibernate. Cela a amen des
dpendances dans le projet [20] :

Ces dpendances sont dues l'inclusion d'Hibernate dans le projet. Elles sont inscrites dans le fichier [[Link]] qui configure le
projet Maven :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
<modelVersion>4.0.0</modelVersion>
<groupId>[Link]</groupId>
<artifactId>mv-pam-jpa-hibernate</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<repositories>
<repository>
<id>unknown-jars-temp-repo</id>
<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>
<url>file:${[Link]}/lib</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>[Link]</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>[Link]</version>
</dependency>

72/290

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>

Il nous faut ajouter une autre dpendance, celle du pilote JDBC de MySQL qui implmente la couche JDBC de l'architecture. Par
ailleurs, certaines des dpendances sont inutiles. Nous faisons voluer le fichier [[Link]] de la faon 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>

Les lignes 17-21 ajoutent la dpendance du pilote JDBC de MySQL.


Note : pour connatre les ns de version disponibles pour les diffrentes archives du fichier [[Link]], on peut procder de la
faon suivante : slectionner 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'excution du projet avec un
SGBD MySQL 5.x.

[Link]

Les entits JPA

73/290

Travail faire : Ecrire les entits [Cotisation, Indemnite, Employe].


Notes :

on pourra suivre la procdure dcrite au paragraphe [Link], page 42,


les entits feront partie d'un paquetage nomm [jpa],
chaque entit aura un n de version,
si deux entits sont lies par une relation, seule la relation principale @ ManyToOne sera construite. La relation inverse
@OneToMany ne le sera pas,
on prendra la stratgie de gnration IDENTITY pour les cls primaires.

[Link]

Le code de la classe principale

Nous ajoutons en [1], la classe [[Link]] suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.

package main;

ligne 10 : on cre l'EntityManagerFactory de l'unit de persistance nomme [mv-pam-jpa-hibernatePU]. Ce nom vient du


fichier [[Link]] :

1.
2.
3.

import [Link];
import [Link];
import [Link];
public class Main {

public static void main(String[] args) {


// crer l'Entity Manager suffit construire la couche JPA
EntityManagerFactory emf = [Link]("mv-pam-jpa-hibernatePU");
EntityManager em=[Link]();
// libration ressources
[Link]();
[Link]();
}

<persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">


...
</persistence-unit>

74/290

[Link]

ligne 12 : on cre l'EntityManager. Cette cration cre la couche JPA. Le fichier [[Link]] va tre exploit et alors les
tables de la base de donnes vont tre cres,
lignes 14-15 : on libre les ressources.

Tests

Revenons l'architecture de notre projet :

Programme
console

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Base de
donnes

Toutes les couches ont t implmentes.


On supprime les tables de la base de donnes [dbpam_hibernate][1-4] :

On excute le projet [1-3].

Les rsultats console sont les suivants :

75/290

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.

-----------------------------------------------------------------------Building mv-pam-jpa-hibernate 1.0-SNAPSHOT


-------------------------------------------------------------------------- exec-maven-plugin:1.2.1:exec (default-cli) @ mv-pam-jpa-hibernate --aot 25, 2016 [Link] AM [Link] logDeprecation
WARN: HHH015016: Encountered a deprecated [Link]
[[Link]]; use [[Link]] instead.
aot 25, 2016 [Link] AM [Link] logDeprecation
WARN: HHH015016: Encountered a deprecated [Link]
[[Link]]; use [[Link]] instead.
aot 25, 2016 [Link] AM [Link] logDeprecation
WARN: HHH015016: Encountered a deprecated [Link]
[[Link]]; use [[Link]] instead.
aot 25, 2016 [Link] AM [Link] logPersistenceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [
name: mv-pam-jpa-hibernatePU
...]
aot 25, 2016 [Link] AM [Link] logVersion
INFO: HHH000412: Hibernate Core {[Link]}
aot 25, 2016 [Link] AM [Link] <clinit>
INFO: HHH000206: [Link] not found
aot 25, 2016 [Link] AM [Link] buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
aot 25, 2016 [Link] AM [Link] <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {[Link]}
aot 25, 2016 [Link] AM [Link]
WARN: HHH000402: Using Hibernate built-in connection pool (not for production use!)
aot 25, 2016 [Link] AM [Link]
INFO: HHH000401: using driver [[Link]] at URL [jdbc:mysql://localhost:3306/dbpam_hibernate?
zeroDateTimeBehavior=convertToNull]
aot 25, 2016 [Link] AM [Link]
INFO: HHH000046: Connection properties: {user=root}
aot 25, 2016 [Link] AM [Link]
INFO: HHH000006: Autocommit mode: false
aot 25, 2016 [Link] AM [Link]
INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
aot 25, 2016 [Link] AM [Link] <init>
INFO: HHH000400: Using dialect: [Link].MySQL5Dialect
aot 25, 2016 [Link] AM [Link] <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
aot 25, 2016 [Link] AM [Link] <init>
INFO: HHH000400: Using dialect: [Link].MySQL5Dialect
Hibernate:
create table cotisations (
ID bigint not null auto_increment,
CSGD double precision not null,
CSGRDS double precision not null,
RETRAITE double precision not null,
SECU double precision not null,
VERSION integer not null,
primary key (ID)
)
Hibernate:
create table employes (
ID bigint not null auto_increment,
ADRESSE varchar(255) not null,
CP varchar(255) not null,
NOM varchar(255) not null,
PRENOM varchar(255) not null,
SS varchar(255) not null,
VERSION integer not null,
VILLE varchar(255) not null,
INDEMNITE_ID bigint not null,
primary key (ID)
)
Hibernate:
create table indemnites (
ID bigint not null auto_increment,
BASE_HEURE double precision not null,
ENTRETIEN_JOUR double precision not null,
INDEMNITES_CP double precision not null,
INDICE integer not null,
REPAS_JOUR double precision not null,
VERSION integer not null,
primary key (ID)
)
Hibernate:
alter table employes
add constraint FK_7xa7rdyjqxrbvew7n6f83ey5h
foreign key (INDEMNITE_ID)
references indemnites (ID)
aot 25, 2016 [Link] AM [Link]
INFO: HHH000030: Cleaning up connection pool [jdbc:mysql://localhost:3306/dbpam_hibernate?
zeroDateTimeBehavior=convertToNull]

configure
buildCreator
buildCreator
buildCreator
configure

stop

76/290

On trouve dans la console uniquement des logs d'Hibernate puisque le programme excut ne fait rien en-dehors d'instancier la
couche JPA. On notera les points suivants :

ligne 27 : Hibernate reconnat la base de donnes et son pilote JDBC,


ligne 39 : Hibernate va utiliser le dialecte MySQL5Dialect pour dialoguer avec le SGBD ;
lignes 40-49 : cration de la table [COTISATIONS],
lignes 50-62 : cration de la table [EMPLOYES],
lignes 63-73 : cration de la table [INDEMNITES],
lignes 74-78 : cration de la cl trangre de la table [EMPLOYES] sur la table [INDEMNITES].

Dans Netbeans, on peut voir les tables dans la connexion qui a t cre prcdemment :

Si on excute le projet une seconde fois, nous avons des erreurs :


Caused by: [Link]: Table 'cotisations' already exists

Hibernate a tent une seconde fois de recrer les tables de la base de donnes [dbpam_hibernate]. Il y a eu alors une erreur parce
que ces tables existent dj. Cette erreur peut tre vite de la faon suivante :

Nous slectionnons le fichier [[Link]] du projet et en [1-2] nous indiquons que la stratgie de gnration des tables est
[Drop and Create]. Ainsi si les tables existent, elles seront supprimes avant d'tre recres. Le fichier [[Link]] volue de la
faon suivante :
1.
2.
3.
4.

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link] xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
<provider>[Link]</provider>

77/290

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>

Si nous excutons de nouveau le projet, il n'y a cette fois-ci pas d'erreurs. Hibernate ajoute les logs suivants :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.

Hibernate:
alter table employes
drop
foreign key FK_7xa7rdyjqxrbvew7n6f83ey5h
Hibernate:
drop table if exists cotisations
Hibernate:
drop table if exists employes
Hibernate:
drop table if exists indemnites

lignes 1-4 : Hibernate supprime la cl trangre 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 cres dpendent la fois de l'implmentation de la couche JPA utilise, du SGBD utilis et des annotations JPA utilises
dans les entits. Ainsi une implmentation JPA / EclipseLink avec le mme SGBD peut gnrer des tables diffrentes de celles
gnres par une implmentation JPA / Hibernate. C'est ce que nous verrons prochainement.
Revenons sur la structure des tables gnres. Dans les logs, il est crit :
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.

Hibernate:
create table cotisations (
ID bigint not null auto_increment,
CSGD double precision not null,
CSGRDS double precision not null,
RETRAITE double precision not null,
SECU double precision not null,
VERSION integer not null,
primary key (ID)
)
Hibernate:
create table employes (
ID bigint not null auto_increment,
ADRESSE varchar(255) not null,
CP varchar(255) not null,
NOM varchar(255) not null,
PRENOM varchar(255) not null,
SS varchar(255) not null,
VERSION integer not null,
VILLE varchar(255) not null,
INDEMNITE_ID bigint not null,
primary key (ID)
)
Hibernate:
create table indemnites (
ID bigint not null auto_increment,
BASE_HEURE double precision not null,
ENTRETIEN_JOUR double precision not null,
INDEMNITES_CP double precision not null,
INDICE integer not null,
REPAS_JOUR double precision not null,
VERSION integer not null,
primary key (ID)
)
Hibernate:
alter table employes
add constraint FK_7xa7rdyjqxrbvew7n6f83ey5h
foreign key (INDEMNITE_ID)
references indemnites (ID)

lignes 94-100 : en l'absence d'informations, Hibernate a gnr des colonnes de 255 caractres. C'est trop ;
on voudrait par ailleurs, que le n SS de l'employ, ligne 98, soit unique dans la table [employes] ;

78/290

Ces contraintes sur la gnration des tables peuvent tre obtenues partir d'informations supplmentaires sur certains champs des
entits JPA. L'entit JPA [Employe] pourrait maintenant tre la suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.

@Entity
@Table(name = "employes")
@NamedQueries({
@NamedQuery(name = "[Link]", query = "SELECT e FROM Employe e")})
public class Employe implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = [Link])
@Basic(optional = false)
@Column(name = "ID")
private Long id;
@Basic(optional = false)
@Column(name = "VERSION")
@Version
private int version;
@Basic(optional = false)
@Column(name = "PRENOM", length = 30)
private String prenom;
@Basic(optional = false)
@Column(name = "NOM", length = 30)
private String nom;
@Basic(optional = false)
@Column(name = "SS", length = 15, unique = true)
private String ss;
@Basic(optional = false)
@Column(name = "ADRESSE", length = 50)
private String adresse;
@Basic(optional = false)
@Column(name = "CP", length = 5)
private String cp;
@Basic(optional = false)
@Column(name = "VILLE", length = 20)
private String ville;
@JoinColumn(name = "INDEMNITE_ID", referencedColumnName = "ID")
@ManyToOne(optional = false, fetch = [Link])
private Indemnite indemniteId;

lignes 20, 24, 28, 32, 36, 40 : avec l'attribut [length], on fixe la taille des colonnes associes ;
ligne 28 : l'attribut [unique=true] va forcer la gnration d'une contrainte d'unicit sur la colonne [SS] de la table
[employes] ;

Les logs de cration sont maintenant les suivants :


1. Hibernate:
2.
create table employes (
3.
ID bigint not null auto_increment,
4.
ADRESSE varchar(50) not null,
5.
CP varchar(5) not null,
6.
NOM varchar(30) not null,
7.
PRENOM varchar(30) not null,
8.
SS varchar(15) not null,
9.
VERSION integer not null,
10.
VILLE varchar(20) not null,
11.
INDEMNITE_ID bigint not null,
12.
primary key (ID)
13.
)
14. Hibernate:
15.
alter table employes
16. add constraint UK_16wn4skb85wj4gssjesdsgjil unique (SS)

lignes 4-8, 10 : les colonnes ont la taille fixe dans l'entit [Employe] ;
lignes 15-16 : la contrainte d'unicit de la colonne [SS] ;

Dans la mme veine, nous souhaitons que dans la table [INDEMNITES], la colonne [INDICE] ait l'attribut UNIQUE : deux lignes
ne peuvent avoir le mme indice. Nous faisons voluer le champ [[Link]] de la faon suivante :
1.
2.

@Column(name = "INDICE", unique = true, nullable = false)


private int indice;

79/290

4.6.2

Couche JPA / EclipseLink

Nous allons construire un nouveau projet Maven dans l'environnement suivant :

Programme
console

Interface
[JPA]

Implmentation
[EclipseLink]

Couche
[JDBC]

Base de
donnes

On suivra la dmarche suivante :

crer une base de donnes MySQL [dbpam_eclipselink] sans tables pour le moment [1-6],

crer un nouveau projet Maven [mv-pam-jpa-eclipselink] en suivant la dmarche du paragraphe 2.1.1, page 11 :

en suivant la dmarche du paragraphe 3.4.8, page 38, crer le fichier [[Link]] du projet. Prendre l'implmentation
JPA 2.1 EclipseLink,

80/290

ajouter dans les dpendances gnres, celle du pilote JDBC de MySQL ;


mettre jour les dpendances prsentes dans le [[Link]] ;

On obtient le [[Link]] final suivant :


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</artifactId>
7.
<version>1.0-SNAPSHOT</version>
8.
<packaging>jar</packaging>
9.
<name>mv-pam-jpa-eclipselink</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.
<!-- Eclipselink -->
18.
<dependency>
19.
<groupId>[Link]</groupId>
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 entits JPA et le programme console du prcdent projet. Adaptez le programme console pour qu'il utilise la
bonne unit de persistance :

Le fichier [[Link]] final sera le suivant :


1.
2.

10.
11.
12.
13.
14.
15.
16.
17.

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link] xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-pam-jpa-eclipselinkPU" transaction-type="RESOURCE_LOCAL">
<provider>[Link]</provider>
<class>[Link]</class>
<class>[Link]</class>
<class>[Link]</class>
<properties>
<property name="[Link]" value="jdbc:mysql://localhost:3306/dbpam_eclipselink?
zeroDateTimeBehavior=convertToNull"/>
<property name="[Link]" value="root"/>
<property name="[Link]" value="[Link]"/>
<property name="[Link]" value=""/>
<property name="[Link]" value="drop-and-create"/>
<property name="[Link]" value="FINE"/>
</properties>
</persistence-unit>
</persistence>

les proprits 9-13 ont t gnres par l'assistant Netbeans,

3.
4.
5.
6.
7.
8.
9.

81/290

ligne 14 : cette proprit nous permet de fixer le niveau de logs d'EclipseLink. Le niveau FINE nous permet de connatre
les ordres SQL qu'EclipseLink va mettre sur la base de donnes,
ligne 13 : l'instanciation de la couche JPA / EclipseLink, les tables des entits JPA seront dtruites puis cres.

Les rsultats console obtenus sont les suivants :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.

-----------------------------------------------------------------------Building mv-pam-jpa-eclipselink 1.0-SNAPSHOT


-----------------------------------------------------------------------...
[EL Fine]: connection: 2016-08-25 [Link].631--Thread(Thread[main,5,main])--Detected database platform:
[Link]
[EL Config]: connection: 2016-08-25 [Link].647--ServerSession(1725097945)--Connection(768669591)-Thread(Thread[main,5,main])--connecting(DatabaseLogin(
platform=>MySQLPlatform
user name=> "root"
datasource URL=> "jdbc:mysql://localhost:3306/dbpam_eclipselink?zeroDateTimeBehavior=convertToNull"
))
[EL Config]: connection: 2016-08-25 [Link].663--ServerSession(1725097945)--Connection(848409667)-Thread(Thread[main,5,main])--Connected: jdbc:mysql://localhost:3306/dbpam_eclipselink?zeroDateTimeBehavior=convertToNull
User: root@localhost
Database: MySQL Version: 5.6.25-log
Driver: MySQL Connector Java Version: mysql-connector-java-5.1.39 ( Revision: 3289a357af6d09ecc1a10fd3c26e95183e5790ad
)
...
[EL Fine]: sql: 2016-08-25 [Link].819--ServerSession(1725097945)--Connection(848409667)--Thread(Thread[main,5,main])-ALTER TABLE employes DROP FOREIGN KEY FK_employes_INDEMNITE_ID
[EL Fine]: sql: 2016-08-25 [Link].85--ServerSession(1725097945)--Thread(Thread[main,5,main])--SELECT 1
[EL Warning]: 2016-08-25 [Link].85--ServerSession(1725097945)--Thread(Thread[main,5,main])--Exception [EclipseLink-4002]
(Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): [Link]
Internal Exception: [Link]: Table 'dbpam_eclipselink.employes' doesn't
exist
Error Code: 1146
Call: ALTER TABLE employes DROP FOREIGN KEY FK_employes_INDEMNITE_ID
Query: DataModifyQuery(sql="ALTER TABLE employes DROP FOREIGN KEY FK_employes_INDEMNITE_ID")
[EL Fine]: sql: 2016-08-25 [Link].85--ServerSession(1725097945)--Connection(848409667)--Thread(Thread[main,5,main])--DROP
TABLE cotisations
[EL Fine]: sql: 2016-08-25 [Link].866--ServerSession(1725097945)--Thread(Thread[main,5,main])--SELECT 1
[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]
Internal Exception: [Link]: Unknown table
'dbpam_eclipselink.cotisations'
Error Code: 1051
Call: DROP TABLE cotisations
Query: DataModifyQuery(sql="DROP TABLE cotisations")
[EL Fine]: sql: 2016-08-25 [Link].866--ServerSession(1725097945)--Connection(848409667)--Thread(Thread[main,5,main])-DROP TABLE employes
[EL Fine]: sql: 2016-08-25 [Link].866--ServerSession(1725097945)--Thread(Thread[main,5,main])--SELECT 1
[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]
Internal Exception: [Link]: Unknown table 'dbpam_eclipselink.employes'
Error Code: 1051
Call: DROP TABLE employes
Query: DataModifyQuery(sql="DROP TABLE employes")
[EL Fine]: sql: 2016-08-25 [Link].866--ServerSession(1725097945)--Connection(848409667)--Thread(Thread[main,5,main])-DROP TABLE indemnites
[EL Fine]: sql: 2016-08-25 [Link].866--ServerSession(1725097945)--Thread(Thread[main,5,main])--SELECT 1
[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]
Internal Exception: [Link]: Unknown table 'dbpam_eclipselink.indemnites'
Error Code: 1051
Call: DROP TABLE indemnites
Query: DataModifyQuery(sql="DROP TABLE indemnites")
...
[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))
[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))
[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))
[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)
[EL Config]: connection: 2016-08-25 [Link].272--ServerSession(1725097945)--Connection(848409667)-Thread(Thread[main,5,main])--disconnect
[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
[EL Config]: connection: 2016-08-25 [Link].272--ServerSession(1725097945)--Connection(768669591)-Thread(Thread[main,5,main])--disconnect
-----------------------------------------------------------------------BUILD SUCCESS

82/290

55.
56.
57.
58.
59.

-----------------------------------------------------------------------Total time: 3.157 s


Finished at: 2016-08-25T[Link]+02:00
Final Memory: 7M/245M
------------------------------------------------------------------------

lignes 6-15 : EclipseLink se connecte la base de donnes [dbpam_eclipselink],


lignes 17-23 : suppression de la cl trangre de la table [EMPLOYES],
lignes 24-30 : suppression de la table [COTISATIONS],
lignes 31-37 : suppression de la table [EMPLOYES],
lignes 38-44 : suppression de la table [COTISATIONS],
ligne 46 : cration de la table [COTISATIONS],
ligne 47 : cration de la table [EMPLOYES] ;
ligne 48 : cration de la table [INDEMNITES] ;
ligne 49 : cration de la cl trangre de la table [EMPLOYES] vers la table [INDEMNITES],
lignes 50-52 : dconnexion de la base.

L'existence des tables gnres peut tre vrifie dans Netbeans [1-2] :

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

@Column(name = "PRENOM", length = 30, nullable = false)


private String prenom;

Les champs de l'entit JPA [Employe] deviennent alors les suivants :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.

@Id
@GeneratedValue(strategy = [Link])
@Column(name = "ID", nullable = false)
private Long id;
@Column(name = "VERSION", nullable = false)
@Version
private int version;
@Column(name = "PRENOM", length = 30, nullable = false)
private String prenom;
@Column(name = "NOM", length = 30, nullable = false)
private String nom;
@Column(name = "SS", length = 13, nullable = false, unique = true)
private String ss;
@Column(name = "ADRESSE", length = 50, nullable = false)
private String adresse;
@Column(name = "CP", length = 5, nullable = false)
private String cp;
@Column(name = "VILLE", length = 20, nullable = false)

83/290

26.
private String ville;
27.
28.
@JoinColumn(name = "INDEMNITE_ID", referencedColumnName = "ID", nullable = false)
29.
@ManyToOne(optional = false, fetch = [Link])
30. private Indemnite indemniteId;

pour tous les champs, nous avons :


enlev l'annotation @Basic(optional=false) ;
ajout l'attribut [nullable=false] l'annotation [@Column] ;

Avec ce changement, les logs de cration 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'implmentation JPA / Hibernate. Pour ces raisons, nous
garderons cette dfinition pour la suite. Nous faisons ce mme type de modifications dans les deux autres entits : [Cotisation] et
[Indemnite].
Maintenant, modifions le mode de gnration des cls primaires des entits JPA [Cotisation, Employe, Indemnite] :
1.
2.
3.
4.
5.

@Id
//@GeneratedValue(strategy = [Link])
@GeneratedValue(strategy = [Link])
@Basic(optional = false)
@Column(name = "ID")

ligne 2, on indique avec le mode [AUTO] qu'on laisse l'implmentation JPA dcider du mode de gnration des cls
primaires.

On fait cette modification dans les trois entits JPA. Puis procdez de la faon suivante :

supprimez les tables existantes de la base [dbpam_eclipselink],


rexcutez le projet.

Cette fois-ci, les tables gnres sont les suivantes :

On voit en [3] qu'une table [SEQUENCE] est apparue. Les logs de cration des tables sont les suivants :
1.
2.

3.
4.

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

84/290

5.
6.
7.

[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))
[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
[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 : cration de la table [COTISATIONS]. La cl primaire [ID] n'a pas l'attribut SQL [auto_increment] qu'elle avait
dans la version prcdente du projet. C'est le cas galement pour les deux autres tables (lignes 2 et 3),
ligne 4 : cration de la cl trangre de la table [EMPLOYES] vers la table [INDEMNITES],
ligne 5 : cration d'une table [SEQUENCE] avec deux colonnes [SEQ_NAME, SEQ_COUNT]. La colonne
[SEQ_NAME] est cl primaire,
ligne 7 : cration d'une ligne dans la table [SEQUENCE] avec les valeurs (SEQ_GEN, 0).

EclipseLink utilise la table [SEQUENCE] pour gnrer les cls primaires des tables [COTISATIONS, EMPLOYES,
INDEMNITES].
Donc, partir des mmes entits JPA, les implmentations JPA peuvent ne pas gnrer les mmes tables selon le mode de
gnration des cls primaires :

avec MySQL et Hibernate, les modes [[Link]] et [[Link]] gnrent les mmes trois
tables en utilisant le mode [autoincrement] pour la gnration des cls primaires,
avec MySQL, EclipseLink et le mode [[Link]], la base gnre est identique celle gnre par
Hibernate,
avec MySQL, EclipseLink et le mode [[Link]], la base gnre est diffrente des prcdentes : elle
contient quatre tables et la gnration des cls primaires n'utilise pas le mode [autoincrement] de MySQL mais une table
supplmentaire appele [SEQUENCE].

Dans la suite du document, quelque soit l'implmentation JPA utilise, Hibernate ou EclipseLink :

on utilisera la stratgie [[Link]] pour gnrer les cls primaires des entits JPA ;

on utilisera la base de donnes [dbpam_hibernate] ;

4.6.3

Travail faire

En suivant la mme dmarche que prcdemment,


1.
2.
3.
4.

4.6.4

crer et tester un projet [mv-pam-jpa-hibernate-oracle] utilisant une implmentation JPA Hibernate et un SGBD Oracle,
crer et tester un projet [mv-pam-jpa-hibernate-mssql] utilisant une implmentation JPA Hibernate et un SGBD SQL
Server,
crer et tester un projet [mv-pam-jpa-eclipselink-oracle] utilisant une implmentation JPA EclipseLink et un SGBD
Oracle,
crer et tester un projet [mv-pam-jpa-eclipselink-mssql] utilisant une implmentation JPA EclipseLink et un SGBD SQL
Server,

Lazy ou Eager ?

Revenons une dfinition possible de l'entit [Employe] :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.

...
@Entity
@Table(name = "employes")
public class Employe implements Serializable {
private static final long serialVersionUID = 1L;
// cl primaire
@Id
@GeneratedValue(strategy = [Link])
@Column(name = "ID")
private Long id;
// prnom
@Column(name = "PRENOM", length = 30, nullable = false)
private String prenom;
// nom
@Column(name = "NOM", length = 30, nullable = false)
private String nom;
// n de scu
@Column(name = "SS", length = 15, nullable = false)

85/290

24.
private String ss;
25.
26.
// adresse
27.
@Column(name = "ADRESSE", length = 50, nullable = false)
28.
private String adresse;
29.
30.
// code postal
31.
@Column(name = "CP", length = 5, nullable = false)
32.
private String cp;
33.
34. // ville
35.
@Column(name = "VILLE", length = 20, nullable = false)
36.
private String ville;
37.
38.
// n de version
39.
@Column(name = "VERSION", nullable = false)
40.
@Version
41.
private int version;
42.
43.
// indemnits
44.
@JoinColumn(name = "INDEMNITE_ID", referencedColumnName = "ID")
45.
@ManyToOne(optional = false, fetch = [Link])
46.
private Indemnite indemniteId;
47. ...

Les lignes 44-46 dfinissent la cl trangre de la table [EMPLOYES] vers la table [INDEMNITES]. L'attribut fetch de la ligne 46
dfinit la stratgie 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 ramene. Elle le sera lorsque
le champ [Employe].indemniteId sera rfrenc pour la premire fois.

[Link] : lorsqu'un employ est cherch, l'indemnit qui lui correspond est ramene. C'est le mode par
dfaut lorsqu'aucun mode n'est prcis.
Pour comprendre l'intrt de l'option [Link], on peut prendre l'exemple suivant. Une liste d'employs sans les indemnits
est prsente dans une page web avec un lien [Details]. Un clic sur ce lien prsente alors les indemnits de l'employ slectionn. On
voit que :

pour afficher la premire page on n'a pas besoin des employs avec leurs indemnits. Le mode [Link] convient
alors,

pour afficher la seconde page avec les dtails, une requte supplmentaire doit tre faite la base de donnes pour avoir
les indemnits de l'employ slectionn.
Le mode [Link] vite de ramener des donnes dont l'application n'a pas besoin tout de suite. Voyons un exemple.
Le projet [mv-pam-jpa-hibernate] est dupliqu :

86/290

en [1-3], on copie le projet,


en [4], on indique le nom du nouveau projet,
en [5], le nouveau projet porte le mme nom que l'ancien. Pour savoir lequel est lequel, passez la souris sur le nom des
projets :

Procdez avec le nouveau projet de la faon suivante :

87/290

en [1-2], on renomme le projet,


en [3-4], on renomme le projet et son artifactId,
en [5], le nouveau projet.

Nous modifions le programme [[Link]] de la faon suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.

package main;
import
import
import
import

[Link];
[Link];
[Link];
[Link];

public class Main {


// la requte JPQL ci-dessous ramne un employ
// la cl trangre [Employe].indemniteId est en [Link]
public static void main(String[] args) {
// crer l'Entity Manager suffit construire la couche JPA
EntityManagerFactory emf = [Link]("mv-pam-jpa-hibernatePU");
// premier essai
EntityManager em = [Link]();
Employe employe = (Employe) [Link]("select e from Employe e where [Link]=:nom").setParameter("nom",
"Jouveinal").getSingleResult();
[Link]();
// on affiche l'employ
try {
[Link]("Employ=" + employe);
} catch (Exception ex) {
[Link]("L'erreur suivante s'est produite : " + ex);
}
// deuxime essai
em = [Link]();
employe = (Employe) [Link]("select e from Employe e left join fetch [Link] where
[Link]=:nom").setParameter("nom", "Jouveinal").getSingleResult();
// librer les ressources
[Link]();
// on affiche l'employ
try {
[Link]("Employ=" + employe);
} catch (Exception ex) {
[Link]("L'erreur suivante s'est produite : " + ex);
}
// libration ressources
[Link]();
}

88/290

39. }

ligne 14 : on cre l'EntityManagerFactory de la couche JPA,


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 reu,
ligne 26 : on recre un nouveau contexte de persistance,
ligne 27 : on excute une requte JPQL pour ramener l'employ de nom Jouveinal, avec ses indemnits. Pour cela, on
contourne le mode [LAZY] en forant 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 ;

Revenons au code de la classe [Main] :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.

// premier essai
EntityManager em = [Link]();
Employe employe = (Employe) [Link]("select e from Employe e where [Link]=:nom").setParameter("nom",
"Jouveinal").getSingleResult();
[Link]();
// on affiche l'employ
try {
[Link]("Employ=" + employe);
} catch (Exception ex) {
[Link]("L'erreur suivante s'est produite : " + ex);
}
// deuxime essai
em = [Link]();
employe = (Employe) [Link]("select e from Employe e left join fetch [Link] where
[Link]=:nom").setParameter("nom", "Jouveinal").getSingleResult();
// librer les ressources
[Link]();
// on affiche l'employ
try {
[Link]("Employ=" + employe);
} catch (Exception ex) {
[Link]("L'erreur suivante s'est produite : " + ex);
}

lignes 7 et 18 : la mthode [Employe].toString() va tre appele. Celle-ci va rendre la chane jSON de l'objet [Employe]
suivant :

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.

package jpa;
import [Link];
...
@Entity
@Table(name = "employes")
public class Employe implements Serializable {
private static final long serialVersionUID = 1L;
// cl primaire
@Id
@GeneratedValue(strategy = [Link])
@Column(name = "ID")
private Long id;
// prnom
@Column(name = "PRENOM", length = 30, nullable = false)
private String prenom;
// nom
@Column(name = "NOM", length = 30, nullable = false)
private String nom;
// n de scu
@Column(name = "SS", length = 15, nullable = false)
private String ss;
// adresse
@Column(name = "ADRESSE", length = 50, nullable = false)
private String adresse;
// code postal
@Column(name = "CP", length = 5, nullable = false)
private String cp;
// ville

89/290

39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.

@Column(name = "VILLE", length = 20, nullable = false)


private String ville;
// n de version
@Column(name = "VERSION", nullable = false)
@Version
private int version;
// indemnits
@JoinColumn(name = "INDEMNITE_ID", referencedColumnName = "ID")
@ManyToOne(optional = false, fetch = [Link])
private Indemnite indemniteId;
// constructeurs
...

// toString
@Override
public String toString() {
return [Link]("Employ=[id=%s, version=%s, prnom=%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 chane jSON de l'objet [Employe], la
bibliothque jSON va automatiquement vouloir produire la chane jSON du champ [Indemnite indemniteId] de la ligne
49. Elle va alors faire une opration [[Link]()] pour obtenir l'objet [Indemnite] associ l'employ. Avec
Hibernate, cette opration n'est possible que si le contexte de persistance est encore ouvert ;

Revenons au code du [Main] :

lignes 6-10 : on devrait avoir une exception. En effet, la mthode toString va tre appele. 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 cre un nouvel EntityManager,
ligne 13 : on demande l'employ Jouveinal en demandant explicitement dans la requte JPQL l'indemnit qui va avec. Cette
demande explicite est ncessaire parce que le mode de recherche de cette indemnit est LAZY,
ligne 15 : on ferme l'EntityManager,
lignes 17-21 : on raffiche l'employ. Il ne devrait pas y avoir d'exception.

Pour excuter le projet, on a besoin d'une base de donnes remplie. On la crera en suivant la dmarche du paragraphe 4.5, page 66.
Par ailleurs, le fichier [[Link]] doit tre modifi :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.0" xmlns="[Link] xmlns:xsi="[Link] xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
<provider>[Link]</provider>
<class>[Link]</class>
<class>[Link]</class>
<class>[Link]</class>
<properties>
<property name="[Link]" value="jdbc:mysql://localhost:3306/dbpam_hibernate"/>
<property name="[Link]" value=""/>
<property name="[Link]" value="[Link]"/>
<property name="[Link]" value="root"/>
<property name="[Link].provider_class" value="[Link]"/>
</properties>
</persistence-unit>
</persistence>

on a enlev l'option qui crait les tables. La base de donnes ici existe dj et est remplie,
on a enlev les options qui faisaient qu'Hibernate loguait les ordres SQL qu'il mettait vers la base de donnes.

L'excution du projet donne les deux affichages suivants dans la console :


1.

L'erreur suivante s'est produite : [Link]: [Link]: could


not initialize proxy - no Session (through reference chain: [Link]["indemniteId"]->jpa.Indemnite_$$_jvstdc_1["id"])

90/290

2.

Employ={"id":494,"prenom":"Marie","ss":"254104940426058","adresse":"5 rue des oiseaux","cp":"49203","ville":"St


Corentin","nom":"Jouveinal","version":0,"indemniteId":
{"id":641,"entretienJour":2.1,"repasJour":3.1,"indice":2,"indemnitesCp":15.0,"baseHeure":2.1,"version":0}}

ligne 1 : l'exception qui s'est produite lorsqu'il a fallu chercher l'indemnit qui manquait, alors que la session tait ferme.
On voit que l'indemnit n'avait pas t ramene cause du mode LAZY,
ligne 2 : l'employ avec son indemnit obtenue par une requte qui a contourn le mode LAZY.

4.6.5

Travail faire

En suivant une dmarche analogue celle qui vient d'tre suivie, crez un projet [mv-pam-jpa-eclipselink-lazy] qui montre le
comportement d'EclipseLink face au mode LAZY.
EclipseLink ncessite une configuration spciale pour que le mode [LAZY] du champ [[Link]] soit respect. Si on suit
la mme dmarche que prcdemment avec les fichiers suivants :
[[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.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link] xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-pam-jpa-eclipselink-lazyPU" transaction-type="RESOURCE_LOCAL">
<provider>[Link]</provider>
<class>[Link]</class>
<class>[Link]</class>
<class>[Link]</class>
<properties>
<property name="[Link]" value="jdbc:mysql://localhost:3306/dbpam_hibernate?
zeroDateTimeBehavior=convertToNull"/>
<property name="[Link]" value="root"/>
<property name="[Link]" value="[Link]"/>
<property name="[Link]" value=""/>
<property name="[Link]" value="FINE"/>
<!-- proprits ncessaires pour que le [@ManyToOne] puisse tre cherch en mode LAZY -->
</properties>
</persistence-unit>

91/290

17. </persistence>

on obtient les logs suivants :


1.

[EL Warning]: metadata: 2016-09-01 [Link].163--ServerSession(226710952)--Thread(Thread[main,5,main])--Reverting the lazy


setting on the OneToOne or ManyToOne attribute [indemniteId] for the entity class [class [Link]] since weaving was not
enabled or did not occur.

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].
L'URL [[Link] explique ce qu'est le
weaving des entits JPA et donne une solution pour l'activer. Il faut ajouter deux plugins Maven au fichier [[Link]] qui devient alors
le suivant :
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.
...
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>

92/290

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.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.

--- staticweave-maven-plugin:1.0.0:weave (default) @ mv-pam-jpa-eclipselink-lazy --Start EclipseLink static weaving...


[EL Fine]: moxy: 2016-09-01 [Link].438--Thread(Thread[main,5,main])--SAXParserFactory instance:
[Link]@25e49cb2
[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
[EL Finest]: properties: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])-property=[Link]; default value=true
[EL Finest]: properties: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])-property=[Link]; default value=true
[EL Finest]: properties: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])-property=[Link]; default value=false
[EL Finest]: properties: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])-property=[Link]; default value=true
[EL Finest]: properties: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])-property=[Link]; default value=true
[EL Finest]: properties: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])-property=[Link]; default value=true
[EL Finest]: properties: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])-property=[Link]-share-emf; default value=true
[EL Finest]: properties: 2016-09-01 [Link].61--ServerSession(297031141)--Thread(Thread[main,5,main])-property=[Link]-share-cache; default value=false
[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/].
[EL Finer]: metadata: 2016-09-01 [Link].625--Thread(Thread[main,5,main])--Searching for mapping file: [METAINF/[Link]] at root URL: [file:/D:/data/istia-1617/netbeans/support-td/chap-04/mv-pam-jpa-eclipselinklazy/target/classes/].
[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].
[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].
[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].
[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].
[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.
[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.
[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.
[EL Finer]: weaver: 2016-09-01 [Link].782--ServerSession(297031141)--Thread(Thread[main,5,main])--Class [[Link]]
registered to be processed by weaver.
[EL Finer]: weaver: 2016-09-01 [Link].782--ServerSession(297031141)--Thread(Thread[main,5,main])--Class [[Link]]
registered to be processed by weaver.
[EL Finer]: weaver: 2016-09-01 [Link].782--ServerSession(297031141)--Thread(Thread[main,5,main])--Class [[Link]]
registered to be processed by weaver.
[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
[EL Finest]: weaver: 2016-09-01 [Link].797--Thread(Thread[main,5,main])--Begin weaver class transformer processing class
[jpa/Cotisation].
[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].
[EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Visiting the end of the class [jpa/Cotisation].
[EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Adding _persistence_get() method into
[jpa/Cotisation].
[EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Adding _persistence_set() method into
[jpa/Cotisation].
[EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Weaved persistence (PersistenceEntity)
[jpa/Cotisation].
[EL Finest]: weaver: 2016-09-01 [Link].813--Thread(Thread[main,5,main])--Weaved change tracking (ChangeTracker)
[jpa/Cotisation].

93/290

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.

ligne 2 : dbut du weaving des entits JPA ;


ligne 59 : fin du weaving ;

Ce weaving a enrichi les entits JPA de faon 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'excution du projet. Ce n'est pas le cas ici.
Le fichier [[Link]] doit lui aussi tre modifi :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link] xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-pam-jpa-eclipselink-lazyPU" transaction-type="RESOURCE_LOCAL">
<provider>[Link]</provider>
<class>[Link]</class>
<class>[Link]</class>
<class>[Link]</class>
<properties>
<property name="[Link]" value="jdbc:mysql://localhost:3306/dbpam_hibernate?
zeroDateTimeBehavior=convertToNull"/>
<property name="[Link]" value="root"/>
<property name="[Link]" value="[Link]"/>
<property name="[Link]" value=""/>
<property name="[Link]" value="FINE"/>
<!-- weaving -->
<property name="[Link]" value="static"/>
<property name="[Link]" value="true"/>
<property name="[Link]" value="true"/>
</properties>
</persistence-unit>
</persistence>

94/290

lignes 15-17 : on dclare qu'un weaving statique est fait la compilation. Sans ces lignes et sous certaines conditions
(configuration Spring / EclipseLink), un weaving dynamique est tent et amne des erreurs. Dans ce projet, on ne verra
aucune diffrence selon que ces lignes sont ou non prsentes ;

Enfin , on met la mthode [[Link]] en commentaires :


// toString
// @Override
// public String toString() {
//
return [Link]("Employ=[id=%s, version=%s, prnom=%s, nom=%s, adresse=%s, ville=%s, code postal=%s, indemnite=%s]",
//
id, version, prenom, nom, adresse, ville, cp, indemniteId);
// }

On met un point d'arrt la ligne 18 suivante :

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 excute l'application en mode dbogage [2-3]. A la ligne d'arrt, on obtient les rsultats suivants :

On voit en [4] que [indemniteId] vaut null indiquant par l que le mode [LAZY] a bien fonctionn.
Maintenant nous pouvons enlever les commentaires de la mthode [[Link]] :
1.
2.
3.
4.
5.

// toString
@Override
public String toString() {
return [Link]("Employ=[id=%s, version=%s, prnom=%s, nom=%s, adresse=%s, ville=%s, code postal=%s, indemnite=
%s]",id, version, prenom, nom, adresse, ville, cp, indemniteId);
}

Il tait important de les enlever pour la raison suivante : nous allons dcouvrir qu'EclipseLink :

respecte bien le mode [LAZY] du champ [Indemnite [Link]] comme nous venons de le voir ;

95/290

est malgr tout capable de rcuprer l'indemnit ds que le code fait rfrence 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 mthode [toString] fait rfrence au champ [indemniteId] (ligne 4). Donc le simple fait d'utiliser la
mthode [[Link]] va forcer la rcupration de l'indemnit de l'employ. Or le dbogueur, pour afficher un objet,
utilise la mthode [toString] de celui-ci. Donc avec la mthode [toString] dcommente ci-dessus, le dbogueur 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 ncessaire de modifier la mthode [[Link]] ;

Avec cette configuration, les rsultats de l'excution 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-eclipselinklazy/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, prnom=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, indemnits
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, prnom=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, indemnits
CP=15.0]
11. [EL Config]: connection: 2016-09-01 [Link].647--ServerSession(226710952)--Connection(737897289)-Thread(Thread[main,5,main])--disconnect
12. ...

4.7

ligne 7 : le 1er affichage. On pourrait croire, le voir, que le mode [LAZY] n'a pas fonctionn. Le dbogueur nous a
montr que ce n'tait pas le cas. On voit sur ces logs que :
lignes 3-4 : une requte a t faite pour obtenir l'employ ;
lignes 5-6 : une seconde requte a t faite pour satisfaire la mthode [[Link]] qui rfrence le champ
[[Link]]. L'indemnit a alors t cherche 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 ;

Le projet Maven [mv-pam-spring-hibernate]

Note : le prochain travail pratique est au paragraphe 4.8, page 110. D'ici l, il faut lire le cours.
L'architecture de l'application construire est la suivante :

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Spring

Pour implmenter cette architecture, nous utiliserons le projet suivant prsent dans le support du TD :

96/290

Le contenu des diffrents packages est le suivant :

4.7.1

[jpa] : entits JPA ;


[dao] : lments de la couche [DAO] ;
[metier] : lments de la couche [mtier] ;
[[Link]] : couche prsentation sous la forme d'une application console ;
[[Link]] : couche prsentation sous la forme d'une application swing ;

Configuration Maven

Le projet Maven [mv-pam-spring-hibernate] est configur par le fichier [[Link]] suivant :


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

97/290

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>

4.7.2

lignes 15-19 : dpendance sur Hibernate ;


lignes 21-25 : dpendance sur le pilote JDBC de MySQL ;
lignes 27-32 : la dpendance pour les tests JUnit,
lignes 34-43 : les dpendances pour le pool de connexions Apache DBCP,
lignes 45-59 : les dpendances pour le framework Spring,
lignes 61-65 : la dpendance pour l'interface Swing. Celle-ci est automatiquement ajoute par Netbeans lorsqu'on ajoute
une interface Swing au projet.

La couche [JPA]

La couche [JPA] utilisera les entits JPA construites au paraphaphe 4.6.1, page 68 avec le mode LAZY prsent au paragraphe 4.6.4,
page 85. Ces entits sont les suivantes :
L'entit [Employe]
1.
2.
3.

package jpa;
import [Link];

98/290

4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.

import
import
import
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

@Entity
@Table(name = "employes")
public class Employe implements Serializable {
private static final long serialVersionUID = 1L;
// cl primaire
@Id
@GeneratedValue(strategy = [Link])
@Column(name = "ID")
private Long id;
// prnom
@Column(name = "PRENOM", length = 30, nullable = false)
private String prenom;
// nom
@Column(name = "NOM", length = 30, nullable = false)
private String nom;
// n de scu
@Column(name = "SS", length = 15, nullable = false, unique = true)
private String ss;
// adresse
@Column(name = "ADRESSE", length = 50, nullable = false)
private String adresse;
// code postal
@Column(name = "CP", length = 5, nullable = false)
private String cp;
// ville
@Column(name = "VILLE", length = 20, nullable = false)
private String ville;
// n de version
@Column(name = "VERSION", nullable = false)
@Version
private int version;
// indemnits
@JoinColumn(name = "INDEMNITE_ID", referencedColumnName = "ID")
@ManyToOne(optional = false, fetch = [Link])
private Indemnite indemnite;
// cl trangre
@Column(name = "INDEMNITE_ID", updatable = false, insertable = false, nullable = false)
private Long indemniteId;
// constructeurs
public Employe() {
}
public Employe(Long id) {
[Link] = id;
}
public Employe(Long id, int version, String prenom, String nom, String ss, String adresse, String ville, String cp,
Indemnite indemnite) {
[Link] = id;
[Link] = prenom;
[Link] = ss;
[Link] = adresse;
[Link] = cp;
[Link] = ville;
[Link] = nom;
[Link] = version;
[Link] = indemnite;
}
// toString
@Override
public String toString() {
return [Link]("Employ=[id=%s, version=%s, prnom=%s, nom=%s, adresse=%s, ville=%s, code postal=%s, indemniteId=
%s]",

99/290

89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113. }

id, version, prenom, nom, adresse, ville, cp, indemniteId);


}
// getters et setters --------...
// galit d'instances -----------@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? [Link]() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Employe)) {
return false;
}
Employe other = (Employe) object;
return !(([Link] == null && [Link] != null) || ([Link] != null && ![Link]([Link])));
}

ligne 58, on notera que l'entit [Indemnite] de l'employ est cherche en mode LAZY ;
lignes 62-63 : nous avons rajout un champ [indemniteId] pour rcuprer la valeur de la colonne [INDEMNITE_ID] de la
table [EMPLOYES] qui est une cl trangre sur la colonne [[Link]]. Ligne 71, l'annotation [@Column] a des
attributs pas encore rencontrs : updatable et insertable. Si on ne les met pas, on a une erreur l'excution 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
initialise 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 lve 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 opration INSERT. Le mme raisonnement s'applique lorsqu'il y a modification d'une entit
[Employe] qui va se traduire par une opration 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 opration UPDATE.
L'ajout d'un champ pour la cl trangre [INDEMNITES_ID] est rendue ncessaire par le mode LAZY appliqu au
champ [Indemnite indemnite]. En effet, lorsqu'on rcupre des employs par une opration JPQL [select e from Employe
e], nous rcuprons des employs sans leurs indemnits. Avec le champ [long indemniteId] qui reprsente la cl primaire
de l'indemnit de l'employ, il devient possible de faire une requte pour obtenir l'indemnit complte d'un employ
lorsqu'on en a besoin ;
lignes 86-90 : la mthode [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 sr d'avoir ce dernier champ, ce qui
n'est pas vrai pour le premier cause de son mode LAZY ;

L'entit [Indemnite]
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.

package jpa;
import
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

@Entity
@Table(name = "indemnites")
public class Indemnite implements Serializable {
private static final long serialVersionUID = 1L;
// cl primaire
@Id
@GeneratedValue(strategy = [Link])
@Column(name = "ID", nullable = false)
private Long id;
// n de version
@Column(name = "VERSION", nullable = false)

100/290

27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.

@Version
private int version;
// indemnit journalire d'entretien
@Column(name = "ENTRETIEN_JOUR", nullable = false)
private double entretienJour;
// indemnit journalire de repas
@Column(name = "REPAS_JOUR", nullable = false)
private double repasJour;
// indice d'indemnits
@Column(name = "INDICE", nullable = false, unique = true)
private int indice;
// indemnits de congs pays
@Column(name = "INDEMNITES_CP", nullable = false)
private double indemnitesCp;
// tarif horaire
@Column(name = "BASE_HEURE", nullable = false)
private double baseHeure;
public Indemnite() {
}
public Indemnite(Long id) {
[Link] = id;
}
public Indemnite(Long id, int version, int indice, double baseHeure, double entretienJour, double repasJour, double
indemnitesCp) {
[Link] = id;
[Link] = entretienJour;
[Link] = repasJour;
[Link] = indice;
[Link] = indemnitesCp;
[Link] = baseHeure;
[Link] = version;
}

@Override
public String toString() {
return [Link]("Indemnite[id=%s, version=%s, indice=%s, base heure=%s, entretien jour=%s, repas jour=%s,
indemnits 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 && ![Link]([Link])));
92.
}
93.
94. }

L'entit [Cotisation]
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.

package jpa;
import
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

@Entity
@Table(name = "cotisations")

101/290

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 scurit 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 && ![Link]([Link])));
88.
}
89. }

4.7.3

Les interfaces des couches [mtier] et [DAO]

102/290

Revenons l'architecture de l'application :

Couche
[ui]

Couche
[metier]

Objets image
de la BD

Couche
[DAO]

Interface
[JPA]

Implmentation
[Hibernate /
EclipseLink]

Couche
[JDBC]

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 premire approche pour dfinir les interfaces des diffrentes couches est d'examiner les
diffrents cas d'usage (use cases) de l'application. Ici nous en avons deux, selon l'interface utilisateur choisie : console ou formulaire
graphique.
Examinons le mode d'utilisation de l'application console :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.

dos>java -jar [Link] 254104940426058 150 20


Valeurs saisies :
N de scurit sociale de l'employ : 254104940426058
Nombre d'heures travailles : 150
Nombre de jours travaills : 20
Informations Employ :
Nom : Jouveinal
...
Informations Cotisations :
CSGRDS : 3.49 %
...
Informations Indemnits :
...
Informations Salaire :
Salaire de base : 362.25 euro
Cotisations sociales : 97.48 euro
Indemnits d'entretien : 42.0 euro
Indemnits de repas : 62.0 euro
Salaire net : 368.77 euro

L'application reoit trois informations de l'utilisateur (cf ligne 1 ci-dessus) :

le n de scurit sociale de l'assistante maternelle ;

le nombre d'heures travailles dans le mois ;

le nombre de jours travaills dans le mois ;


A partir de ces information et d'autres enregistres dans des fichiers de configuration, l'application affiche les informations suivantes
:

103/290

lignes 4-6 : les valeurs saisies ;


lignes 8-10 : les informations lies l'employ dont on a donn le n de scurit sociale ;
lignes 12-14 : les taux des diffrentes cotisations sociales ;
lignes 16-17 : les diffrentes indemnits verses l'assistante maternelle ;
lignes 19-24 : les lments de la feuille de salaire de l'assistante maternelle ;

Certaines informations doivent tre fournies par la couche [metier] la couche [ui] :
1.
2.
3.
4.

les informations lies une assistante maternelle identifie par son n de scurit sociale. On trouve ces informations dans
la table [EMPLOYES]. Cela permet d'afficher les lignes 6-8 ;
les montants des divers taux de cotisations sociales prlever sur le salaire brut. On trouve ces informations dans la table
[COTISATIONS]. Cela permet d'afficher les lignes 10-12 ;
les montants des diverses indemnits lies la fonction d'assistante maternelle. On trouve ces informations dans la table
[INDEMNITES]. Cela permet d'afficher les lignes 14-15 ;
les lments constitutifs du salaire affichs lignes 18-22 ;

De ceci, on pourrait dcider d'une premire criture de l'interface [IMetier] prsente par la couche [metier] la couche [ui] :
1.
2.
3.
4.
5.
6.

package metier;

ligne 1 : les lments de la couche [metier] sont mis dans le paquetage [metier] ;
ligne 5 : la mthode [ calculerFeuilleSalaire ] prend pour paramtres 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 ;

public interface IMetier {


// obtenir la feuille de salaire
FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int nbJoursTravaills );
}

La classe [FeuilleSalaire] pourrait tre la suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.

package metier;

ligne 9 : l'employ concern par la feuille de salaire - information n 1 affiche par la couche [ui] ;
ligne 10 : les diffrents taux de cotisation - information n 2 affiche par la couche [ui] ;
ligne 11 : les diffrentes indemnits lies l'indice de l'employ - information n 3 affiche par la couche [ui] ;
ligne 12 : les lments constitutifs de son salaire - information n 4 affiche par la couche [ui] ;

import [Link];
import [Link];
import [Link];
public class FeuilleSalaire {
// champs privs
private Employe employe;
private Cotisation cotisation;
private ElementsSalaire elementsSalaire;
...
}

Un second cas d'usage de la couche [mtier] apparat avec l'interface graphique :

On voit ci-dessus, que la liste droulante [1, 2] prsente tous les employs. Cette liste doit tre demande la couche [mtier].
L'interface de celle-ci volue alors de la faon suivante :
1.
2.

package metier;

104/290

3.
4.
5.
6.
7.
8.
9.
10.
11.

import [Link];
import [Link];

ligne [10] : la mthode qui va permettre la couche [ui] de demander la liste de tous les employs la couche [mtier] ;

public interface IMetier {


// obtenir la feuille de salaire
FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int nbJoursTravaills );
// liste des employs
List<Employe> findAllEmployes();
}

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 donnes. Il en est de mme pour obtenir la
liste de tous les employs. On peut crer une interface [DAO] unique grant l'accs aux trois entits [Employe, Cotisation,
Indemnite]. Nous dcidons plutt ici de crer une interface [DAO] par entit.
L'interface [DAO] pour les accs aux entits [Cotisation] de la table [COTISATIONS] sera la suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.

package dao;

ligne 6, l'interface [ICotisationDao] gre les accs l'entit [Cotisation] et donc la table [COTISATIONS] de la base de
donnes. Notre application n'a besoin que de la mthode [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 gnral o toutes les oprations CRUD (Create,
Read, Update, Delete) sont effectues sur l'entit ;
ligne 8 : la mthode [create] cre une nouvelle entit [Cotisation] ;
ligne 10 : la mthode [edit] modifie une entit [Cotisation] existante ;
ligne 12 : la mthode [destroy] supprime une entit [Cotisation] existante ;
ligne 14 : la mthode [find] permet de retrouver une entit [Cotisation] existante via son identifiant id ;
ligne 16 : la mthode [findAll] rend dans une liste toutes les entits [Cotisation] existantes ;

import [Link];
import [Link];
public interface ICotisationDao {
// crer une nouvelle cotisation
Cotisation create(Cotisation cotisation);
// modifier une cotisation existante
Cotisation edit(Cotisation cotisation);
// supprimer une cotisation existante
void destroy(Cotisation cotisation);
// chercher une cotisation particulire
Cotisation find(Long id);
// obtenir tous les objets Cotisation
List<Cotisation> findAll();
}

Attardons-nous sur la signature de la mthode [create] :


1.
// crer une nouvelle cotisation
2. Cotisation create(Cotisation cotisation);

La mthode create a un paramtre cotisation de type Cotisation. Le paramtre cotisation doit tre persist, c.a.d. ici mis dans la table
[COTISATIONS]. Avant cette persistance, le paramtre cotisation a un identifiant id sans valeur. Aprs la persistance, le champ id a
une valeur qui est la cl primaire de l'enregistrement ajout la table [COTISATIONS]. Le paramtre cotisation est donc un
paramtre d'entre / sortie de la mthode create. Il ne semble pas ncessaire que mthode create rende le paramtre cotisation comme
rsultat. La mthode appelante dtenant une rfrence sur l'objet [Cotisation cotisation], si celui-ci est modifi, elle a accs l'objet
modifi puisqu'elle a une rfrence dessus. Elle peut donc connatre la valeur que la mthode create a donn au champ id de l'objet
[Cotisation cotisation]. La signature de la mthode pourrait donc tre plus simplement :
1.
2.

// crer une nouvelle cotisation


void create(Cotisation cotisation);

Lorsqu'on crit une interface, il est bon de se rappeler qu'elle peut tre utilise dans deux contextes diffrents : local et distant. Dans
le contexte local, la mthode appelante et la mthode appele sont excutes dans la mme JVM :

105/290

utilisateur

Couche interface
utilisateur [ui]

Couche mtier
[metier]

Couche d'accs aux


donnes [DAO]

Donnes

JVM
Si la couche [metier] fait appel la mthode create de la couche [DAO], elle a bien une rfrence sur le paramtre [Cotisation
cotisation] qu'elle passe la mthode.
Dans le contexte distant, la mthode appelante et la mthode appele sont excutes dans des JVM diffrentes :
1
utilisateur

Couche
interface
utilisateur [ui]
JVM 1

Couche
mtier
[metier]

3
Rseau
tcp /ip

Couche d'accs aux


donnes [DAO]

Donnes

JVM 2

Ci-dessus, la couche [metier] s'excute dans la JVM 1 et la couche [DAO] dans la JVM 2 sur deux machines diffrentes. Les deux
couches ne communiquent pas directement. Entre-elles s'intercale une couche qu'on appellera couche de communication [1]. Celleci est compose d'une couche d'mission [2] et d'une couche de rception [3]. Le dveloppeur n'a en gnral pas crire ces
couches de communication. Elles sont gnres automatiquement par des outils logiciels. La couche [metier] est crite comme si elle
s'excutait dans la mme JVM que la couche [DAO]. Il n'y a donc aucune modification de code.
Le mcanisme de communication entre la couche [metier] et la couche [DAO] est le suivant :

la couche [metier] fait appel la mthode create de la couche [DAO] en lui passant le paramtre [Cotisation cotisation1]
ce paramtre est en fait pass la couche d'mission [2]. Celle-ci va transmettre sur le rseau, la valeur du paramtre
cotisation1 et non sa rfrence. La forme exacte de cette valeur dpend du protocole de communication utilis ;
la couche de rception [3] va rcuprer cette valeur et reconstruire partir d'elle un objet [Cotisation cotisation2] image du
paramtre initial envoy par la couche [metier]. On a maintenant deux objets identiques (au sens de contenu) dans deux
JVM diffrentes : cotisation1 et cotisation2 ;
la couche de rception va passer l'objet cotisation2 la mthode create de la couche [DAO] qui va le persister en base de
donnes. Aprs cette opration, 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 rfrence. Si on veut
que la couche [metier] ait une rfrence sur l'objet cotisation2, il faut le lui envoyer. Aussi est-on amens changer la
signature de la mthode create de la couche [DAO] :

1.
2.

// crer une nouvelle cotisation


Cotisation create(Cotisation cotisation);

avec cette nouvelle signature, la mthode create va rendre comme rsultat l'objet persist cotisation2. Ce rsultat est rendu
la couche de rception [3] qui avait appel la couche [DAO]. Celle-ci va rendre la valeur (et non la rfrence) de cotisation2
la couche d'mission [2] ;
la couche d'mission [2] va rcuprer cette valeur et reconstruire partir d'elle un objet [Cotisation cotisation3] image du
rsultat rendu par la mthode create de la couche [DAO] ;
l'objet [Cotisation cotisation3] est rendu la mthode de la couche [metier] dont l'appel la mthode create de la couche
[DAO] avait initi tout ce mcanisme. La couche [metier] peut donc connatre 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 prcdente n'est pas la plus courante. On trouve plus frquemment les couches [metier] et [DAO] dans la mme JVM
:

106/290

1
utilisateur

2
Couche
interface
utilisateur [ui]
JVM 1

3
Rseau
tcp /ip

Couche
mtier
[metier]

Couche d'accs
aux donnes
[DAO]

Donnes

JVM 2

Dans cette architecture, ce sont les mthodes de la couche [metier] qui doivent rendre des rsultats et non celles de la couche
[DAO]. Nanmoins la signature suivante de la mthode create de la couche [DAO] :
1.
2.

// crer une nouvelle cotisation


Cotisation create(Cotisation cotisation);

nous permet de ne pas faire d'hypothses sur l'architecture rellement mise en place. Utiliser des signatures qui fonctionneront
quelque soit l'architecture retenue, locale ou distante, implique que dans le cas o une mthode appele modifie certains de ses
paramtres :

ceux-ci doivent faire galement partie du rsultat de la mthode appele ;

la mthode appelante doit utiliser le rsultat de la mthode appele et non les rfrences des paramtres modifis qu'elle a
transmis la mthode appele ;
On se laisse ainsi la possibilit de passer une couche d'une architecture locale une architecture distante sans modification de code.
Rexaminons, cette lumire, l'interface [ICotisationDao] :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.

package dao;

ligne 8 : le cas de la mthode create a t trait


ligne 10 : la mthode edit utilise son paramtre [Cotisation cotisation1] pour mettre jour l'enregistrement de la table
[COTISATIONS] ayant la mme cl primaire que l'objet cotisation. Elle rend comme rsultat l'objet cotisation2 image de
l'enregistrement modifi. Le paramtre cotisation1 n'est lui pas modifi. La mthode doit rendre cotisation2 comme rsultat
qu'on soit dans le cadre d'une architecture distante ou locale.
ligne 12 : la mthode destroy supprime l'enregistrement de la table [COTISATIONS] ayant la mme cl primaire que l'objet
cotisation pass en paramtre. Celui-ci n'est pas modifi. Il n'a donc pas tre rendu.
ligne 14 : le paramtre id de la mthode find n'est pas modifi par la mthode. Il n'a pas faire partie du rsultat.
ligne 16 : la mthode findAll n'a pas de paramtres. On n'a donc pas l'tudier.

import [Link];
import [Link];
public interface ICotisationDao {
// crer une nouvelle cotisation
Cotisation create(Cotisation cotisation);
// modifier une cotisation existante
Cotisation edit(Cotisation cotisation);
// supprimer une cotisation existante
void destroy(Cotisation cotisation);
// chercher une cotisation particulire
Cotisation find(Long id);
// obtenir tous les objets Cotisation
List<Cotisation> findAll();
}

Au final, seule la signature de la mthode create doit tre adapte pour tre utilisable dans le cadre d'une architecture distante. Les
raisonnements prcdents seront valables pour les autres interfaces [DAO]. Nous ne les rpterons pas et utiliserons directement
des signatures utilisables aussi bien dans le cadre d'une architecture distante que locale.
L'interface [DAO] pour les accs aux entits [Indemnite] de la table [INDEMNITES] sera la suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.

package dao;
import [Link];
import [Link];
public interface IIndemniteDao {
// crer une entit Indemnite
Indemnite create(Indemnite indemnite);
// modifier une entit Indemnite
Indemnite edit(Indemnite indemnite);
// supprimer une entit Indemnite
void destroy(Indemnite indemnite);
// rechercher une entit Indemnite via son identifiant

107/290

14.
Indemnite find(Long id);
15.
// obtenir toutes les entits Indemnite
16.
List<Indemnite> findAll();
17. }

ligne 6, l'interface [IIndemniteDao] gre les accs l'entit [Indemnite] et donc la table [INDEMNITES] de la base de
donnes. Notre application n'a besoin que de la mthode [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 gnral o toutes les oprations CRUD (Create,
Read, Update, Delete) sont effectues sur l'entit.
ligne 8 : la mthode [create] cre une nouvelle entit [Indemnite]
ligne 10 : la mthode [edit] modifie une entit [Indemnite] existante
ligne 12 : la mthode [destroy] supprime une entit [Indemnite] existante
ligne 14 : la mthode [find] permet de retrouver une entit [Indemnite] existante via son identifiant id
ligne 16 : la mthode [findAll] rend dans une liste toutes les entits [Indemnite] existantes

L'interface [DAO] pour les accs aux entits [Employe] de la table [EMPLOYES] sera la suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.

package dao;

ligne 6, l'interface [IEmployeDao] gre les accs l'entit [Employe] et donc la table [EMPLOYES] de la base de
donnes. Notre application n'a besoin que de la mthode [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 gnral o toutes les oprations CRUD (Create, Read,
Update, Delete) sont effectues sur l'entit ;
ligne 9 : la mthode [create] cre une nouvelle entit [Employe] ;
ligne 11 : la mthode [edit] modifie une entit [Employe] existante ;
ligne 13 : la mthode [destroy] supprime une entit [Employe] existante ;
ligne 15 : la mthode [find] permet de retrouver une entit [Employe] existante via son identifiant id ;
ligne 17 : la mthode [find(String SS)] permet de retrouver une entit [Employe] existante via son n SS. Nous avons vu
que cette mthode tait ncessaire l'application console ;
ligne 19 : la mthode [findAll] rend dans une liste toutes les entits [Employe] existantes. Nous avons vu que cette
mthode tait ncessaire l'application graphique.

4.7.4

import [Link];
import [Link];
public interface IEmployeDao {
// crer une nouvelle entit Employe
Employe create(Employe employe);
// modifier une entit Employe existante
Employe edit(Employe employe);
// supprimer une entit Employe
void destroy(Employe employe);
// chercher une entit Employe via son identifiant id
Employe find(Long id);
// chercher une entit Employe via son n SS
Employe find(String SS);
// obtenir toutes les entits Employe
List<Employe> findAll();
}

La classe [PamException]

La couche [DAO] va travailler avec l'API JDBC de Java. Cette API lance des exceptions contrles de type [SQLException] qui
prsentent deux inconvnients :

elles alourdissent le code qui doit obligatoirement grer ces exceptions avec des try / catch ;

108/290

elles doivent tre dclares dans la signature des mthodes de l'interface [IDao] par un "throws SQLException". Ceci a
pour consquence d'empcher l'implmentation de cette interface par des classes qui lanceraient une exception contrle
d'un type diffrent de [SQLException] ;

Pour remdier ce problme, la couche [DAO] ne "remontera" que des exceptions non contrles de type [PamException].

PamException
Couche
[ui]

Couche
[metier]

[JPA]Exception

Couche
[DAO]

SQLException

Implmentation
[JPA / Hibernate]

Couche
[JDBC]

Spring

la couche [JDBC] lance des exceptions de type [SQLException] ;


la couche [JPA] lance des exceptions propres l'implmentation JPA utilise ;
la couche [DAO] lance des exceptions de type [PamException] non contrles ;

Ceci a deux consquences :

la couche [metier] n'aura pas l'obligation de grer les exceptions de la couche [DAO] avec des try / catch. Elle pourra
simplement les laisser remonter jusqu' la couche [ui] ;

les mthodes de l'interface [IDao] n'ont pas mettre dans leur signature la nature de l'exception [PamException], ce qui
laisse la possiblit d'implmenter cette interface avec des classes qui lanceraient un autre type d'exception non contrle ;
La classe [PamException] sera place dans le paquetage [exception] du projet Netbeans :

Son code est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.

package exception;
import [Link];
@SuppressWarnings("serial")
public class PamException extends RuntimeException implements Serializable{
// code d'erreur
private int code;
public PamException(int code) {
super();
[Link] = code;
}
public PamException(String message, int code) {
super(message);
[Link] = code;
}
public PamException(Throwable cause, int code) {
super(cause);
[Link] = code;

109/290

24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41. }

}
public PamException(String message, Throwable cause, int code) {
super(message, cause);
[Link] = code;
}
// getter et setter
public int getCode() {
return code;
}
public void setCode(int code) {
[Link] = code;
}

ligne 6 : [PamException] drive de [RuntimeException]. C'est donc un type d'exceptions que le compilateur ne nous oblige
pas grer par un try / catch ou mettre dans la signature des mthodes. C'est pour cette raison, que [PamException]
n'est pas dans la signature des mthodes de l'interface [IDao]. Cela permet cette interface d'tre implmente par une
classe lanant un autre type d'exceptions, pourvu que celui-ci drive galement de [RuntimeException] ;
pour diffrencier 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 paramtre : celui du code
d'erreur qu'on veut donner l'exception ;

Le fonctionnement de l'application, du point de vue des exceptions, sera le suivant :

la couche [DAO] encapsulera toute exception rencontre, dans une exception de type [PamException], et relancera cette
dernire pour la couche [mtier] ;
la couche [mtier] laissera remonter les exceptions lances par la couche [DAO]. Elle encapsulera toute exception
survenant dans la couche [mtier], dans une exception de type [PamException] et relancera cette dernire pour la couche
[ui] ;
la couche [ui] intercepte toutes les exceptions qui remontent des couches [mtier] et [DAO]. Elle se contentera d'afficher
l'exception sur la console ou l'interface graphique ;

Examinons maintenant successivement l'implmentation des couches [DAO] et [metier].

4.8

La couche [DAO] de l'application [PAM]

Nous nous plaons dans le cadre de l'architecture suivante :

110/290

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

4.8.1

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Spring

Implmentation

Lectures conseilles : paragraphe 3.1.3 de [ref1], API JPA au paragraphe 3.3, page 30.
Travail faire : En utilisant l'intgration Spring / JPA, crire les classes [CotisationDao, IndemniteDao, EmployeDao]
d'implmentation des interfaces [ICotisationDao, IIndemniteDao, IEmployeDao]. Chaque mthode de classe interceptera une
ventuelle exception et l'encapsulera dans une exception de type [PamException] avec un code d'erreur propre l'exception
intercepte.
Note : les classes du paquetage [dao] auront la forme suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.

package dao;

ligne 6 : l'annotation Spring @Transactional. Elle indique Spring que chaque mthode de la classe soit se drouler 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'intrieur d'une mthode de la couche [DAO], on utilise l'EntityManager de la ligne 10. Grce lui, on
fait des oprations de persistance (persist, merge, remove, createQuery). On relira le paragraphe 3.3, page 30, qui prsente
l'API d'une couche JPA ;
parce que la mthode se droule dans une transaction, on est assur qu' la fin de la mthode, les modifications apportes
au contexte de persistance seront synchronises avec la base de donnes ;
on fera en sorte que le code de la mthode soit entour d'un try / catch arrtant toute ventuelle exception. On
encapsulera celle-ci dans un type [PamException] (ligne 22).

4.8.2

...
import [Link];
@Transactional
public class CotisationDao implements ICotisationDao {
@PersistenceContext
private EntityManager em;
// constructeur
public CotisationDao() {
}
// crer une cotisation
public Cotisation create(Cotisation cotisation) {
try{
[Link](cotisation);
return cotisation;
}catch (Throwable th){
throw new PamException(th,11);
}
}
....
}

Configuration Spring / JPA

Lectures conseilles : paragraphe 3.1.5 de [ref1]


L'intgration DAO / JPA est configure par le fichier Spring [[Link]] et le fichier JPA [[Link]] :

111/290

Travail faire : crire le contenu de ces deux fichiers. On supposera que la base de donnes utilise est la base MySQL5
[dbpam_hibernate] gnre par le script SQL [[Link]]. Le fichier Spring dfinira les trois beans suivants :
employeDao de type EmployeDao, indemniteDao de type IndemniteDao, cotisationDao de type CotisationDao. Par ailleurs, l'implmentation
JPA utilise sera Hibernate.
Note 1 : suivre le paragraphe 3.1.5 de [ref1]
Note 2 : la configuration d'Hibernate dans [[Link]] sera la suivante :
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>
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>

4.8.3

lignes 9-15 : des proprits propres l'implmentation JPA utilise ;


ligne 9 : lors des tests, la valeur [drop-and-create] permet de dmarrer les tests avec des tables neuves. Une fois les tests
russis, on mettra cette ligne en commentaires pour ne plus recrer les tables ainsi que les lignes 12-14 pour viter d'avoir
trop de logs ;

Tests

Lectures conseilles : paragraphes 3.1.6 et 3.1.7 de [ref1]


Maintenant que la couche [DAO] est crite et configure, nous pouvons la tester. L'architecture des tests sera la suivante :

Couche
[tests]

Couche
[DAO]

Objets image
de la BD

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Spring

112/290

[Link]

JUnitInitDB

Nous allons crer deux programmes de tests de la couche [DAO]. Ceux-ci seront placs dans le paquetage [dao] [2] de la branche
[Test Packages] [1] du projet Netbeans. Cette branche n'est pas incluse dans le projet gnr par l'option [Build project], ce qui nous
assure que les programmes de tests que nous y plaons ne seront pas inclus dans le .jar final du projet.

Les classes places dans la branche [Test Packages] ont connaissance des classes prsentes dans la branche [Source Packages] ainsi
que des bibliothques de classes du projet. Si les tests ont besoin de bibliothques autres que celles du projet, celles-ci doivent tre
dclares dans la branche [Test Libraries] [2].
Les classes de tests utilisent l'outil de tests unitaires JUnit :

[JUnitInitDB] ne fait aucun test. Elle remplit la base de donnes avec quelques enregistrements et affiche ensuite ceux-ci
sur la console ;

[JUnitDao] fait une srie de tests dont elle vrifie le rsultat ;


Le squelette de la classe [JUnitInitDB] est le suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.

package dao;
import
import
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

public class JUnitInitDB {


static private IEmployeDao employeDao = null;
static private ICotisationDao cotisationDao = null;
static private IIndemniteDao indemniteDao = null;
@BeforeClass
public static void init() throws Exception {
// configuration de l'application
ApplicationContext ctx = new ClassPathXmlApplicationContext("[Link]");
// couches dao
employeDao = (IEmployeDao) [Link]("employeDao");
cotisationDao = (ICotisationDao) [Link]("cotisationDao");
indemniteDao = (IIndemniteDao) [Link]("indemniteDao");
}
@Test
public void initDB() {
// on la remplit
...
// on l'affiche
...

113/290

35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47. }

}
@Before()
public void clean() {
// on vide la base
...
}
@AfterClass
public static void terminate() throws Exception {
}

ligne 20 : la mthode [init] est excute avant le dbut de la srie des tests (annotation @BeforeClass). Elle instancie la
couche [DAO]. Elle doit tre statique (ligne 20) ce qui entrane que les champs qu'elle utilise doivent tre galement
statiques (lignes 15-17) ;
ligne 38 : la mthode [clean] est excute avant chaque test (annotation @Before). Elle vide la base de donnes ;
ligne 30 : la mthode [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 mthode est donc un faux test. Elle a pour rle de
remplir la base de donnes avec quelques lignes puis d'afficher le contenu de la base sur la console. Ce sont les mthodes
create et findAll des couches [DAO] qui sont ici utilises.

Travail faire : complter le code de la classe [JUnitInitDB]. On s'aidera de l'exemple du paragraphe 3.1.6 de [ ref1]. Le code
gnrera le contenu prsent page 63.
Note : on utilisera les mthodes [create] des classes [DAO]. Parce que l'entit [Employe] embarque l'entit [Indemnite], il faut
d'abord crer ces dernires avant de crer les employs qui vont les rfrencer. Par ailleurs, pour persister une entit en base, il faut
qu'elle ait son id gal null. En effet, c'est cette caractristique que la mthode [persist] de l'interface [EntityManager] sait qu'elle
doit persister ou non une entit en base. Par ailleurs, la version de l'entit cre sera mise 1.

[Link]

Mise en oeuvre des tests

Nous sommes dsormais prts pour excuter [InitDB] :

avec [PhpMyAdmin], supprimez la base [dbpam_hibernate] ;

114/290

puis excutez le script SQL [dbpam_hibernate-[Link]] que vous trouverez dans le support du TD [5] : il cre la
base [dbpam_hibernate] avec des tables vides ;

Excutez ensuite le test [JUnitInitDB] en suivant la procdure suivante :

les classes [1], les fichiers de configuration [2] et les classes de test de la couche [DAO] [3] sont mis en place ;

le projet est construit [4] ;


la classe [JUnitInitDB] est excute [5] (le SGBD MySQL5 est lanc avec une base [dbpam_hibernate] existante) ;
la fentre [Test Results] [6] dit que les tests ont t russis. Ce message n'est pas significatif ici, car le programme
[JUnitInitDB] ne contient aucune instruction d'assertion [Link] qui pourrait provoquer l'chec du test.
Nanmoins, cela montre qu'il n'y a pas eu d'exception l'excution du test ;

La fentre [Output] contient les logs de l'excution, ceux de Spring et ceux du test lui-mme. Les affichages faits par la classe
[JUnitInitDB] sont les suivants :
1.
2.
3.
4.
5.
6.
7.
8.

Employs ---------------------Employ=[id=496, version=1, prnom=Marie, nom=Jouveinal, adresse=5 rue des oiseaux, ville=St Corentin, code postal=49203]
Employ=[id=497, version=1, prnom=Justine, nom=Laverti, adresse=La brlerie, ville=St Marcel, code postal=49014]
Indemnits ---------------------Indemnite[id=642, version=1, indice=1, base heure=1.93, entretien jour=2.0, repas jour=3.0, indemnits CP=12.0
Indemnite[id=643, version=1, indice=2, base heure=2.1, entretien jour=2.1, repas jour=3.1, indemnits CP=15.0
Cotisations ---------------------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 vrifier avec une connexion Netbeans la
base [dbpam_hibernate].

115/290

[Link]

en [1-4], dans l'onglet [services], on visualise les donnes de la table [employes] de la connexion [dbpam_hibernate] [2] ;
en [3] le rsultat ;

JUnitDao

Note : la classe [JUnitDao] peut tre trouve dans le support de cours.


Nous nous intressons maintenant une seconde classe de tests [JUnitDao] :

Le squelette de la classe est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.

package dao;
import
import
import
import
import
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

public class JUnitDao {


// couches dao
static private IEmployeDao employeDao;
static private IIndemniteDao indemniteDao;
static private ICotisationDao cotisationDao;
@BeforeClass

116/290

24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.

public static void init() {


// log
log("init");
// configuration de l'application
ApplicationContext ctx = new ClassPathXmlApplicationContext("[Link]");
// couches dao
employeDao = (IEmployeDao) [Link]("employeDao");
indemniteDao = (IIndemniteDao) [Link]("indemniteDao");
cotisationDao = (ICotisationDao) [Link]("cotisationDao");
}
@AfterClass
public static void terminate() {
}
@Before()
public void clean() {
// on vide la base
[Link]().stream().forEach((employe) -> {
[Link](employe);
});
[Link]().stream().forEach((cotisation) -> {
[Link](cotisation);
});
[Link]().stream().forEach((indemnite) -> {
[Link](indemnite);
});
}
// logs
private static void log(String message) {
[Link]("----------- " + message);
}
// tests
@Test
public void test01() {
log("test01");
// liste des cotisations
List<Cotisation> cotisations = [Link]();
int nbCotisations = [Link]();
// on ajoute une cotisation
Cotisation cotisation = [Link](new Cotisation(null, 1, 9.39, 7.88, 6.15, 3.49));
// on la demande
cotisation = [Link]([Link]());
// vrification
[Link](cotisation);
[Link](3.49, [Link](), 1e-6);
[Link](6.15, [Link](), 1e-6);
[Link](9.39, [Link](), 1e-6);
[Link](7.88, [Link](), 1e-6);
// on la modifie
[Link](-1);
[Link](-1);
[Link](-1);
[Link](-1);
Cotisation cotisation2 = [Link](cotisation);
// vrifications
[Link]([Link]() + 1, [Link]());
[Link](-1, [Link](), 1e-6);
[Link](-1, [Link](), 1e-6);
[Link](-1, [Link](), 1e-6);
[Link](-1, [Link](), 1e-6);
// on demande l'lment modifi
Cotisation cotisation3 = [Link]([Link]());
// vrifications
[Link]([Link](), [Link]());
[Link](-1, [Link](), 1e-6);
[Link](-1, [Link](), 1e-6);
[Link](-1, [Link](), 1e-6);
[Link](-1, [Link](), 1e-6);
// on supprime l'lment
[Link](cotisation3);
// vrifications
Cotisation cotisation4 = [Link]([Link]());
[Link](cotisation4);
cotisations = [Link]();
[Link](nbCotisations, [Link]());
}
@Test
public void test02() {
log("test02");
// on demande la liste des indemnits
// on ajoute une Indemnite indemnite

117/290

111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.

// on va chercher indemnite en base via sa cl primaire on rcupre indemnite1


// on vrifie que indemnite1 = indemnite
// on modifie l'indemnit obtenue et on persiste la modification en BD. On obtient indemnite2
// on vrifie la version de indemnite2
// on va chercher indemnite2 en base on obtient indemnite3
// on vrifie que indemnite3 = indemnite2
// on supprime en base l'image de indemnite3
// on va chercher indemnite3 en base
// on vrifie qu'on obtient une rfrence null
}
@Test
public void test03() {
log("test03");
// on rpte un test analogue aux prcdents pour Employe
}
@Test
public void test04() {
log("test04");
// on teste la mthode [IEmployeDao].find(String SS)
// d'abord avec un employ existant
// puis avec un employ inexistant
}
@Test
public void test05() {
log("test05");
// on cre deux indemnits avec le mme indice
// enfreint la contrainte d'unicit de l'indice
// on vrifie qu'une exception de type PamException se produit
// et qu'elle a le n d'erreur attendu
}
@Test
public void test06() {
log("test06");
// on cre deux employs avec le mme n SS
// enfreint la contrainte d'unicit sur le n SS
// on vrifie qu'une exception de type PamException se produit
// et qu'elle a le n d'erreur attendu
}
@Test
public void test07() {
log("test07");
// on cre deux employs avec le mme n SS, le 1er avec create, le 2me avec edit
// enfreint la contrainte d'unicit sur le n SS
// on vrifie qu'une exception de type PamException se produit
// et qu'elle a le n d'erreur attendu
}
@Test
public void test08() {
[Link]("test08");
log("test08");
// supprimer un employ qui n'existe pas ne provoque pas d'exception
// il est ajout puis dtruit on le vrifie
}
@Test
public void test09() {
log("test09");
// modifier un employ sans avoir la bonne version doit provoquer une exception
// on le vrifie
}
@Test
public void test10() {
log("test10");
// supprimer un employ sans avoir la bonne version doit provoquer une exception

118/290

198.
199.
200.
201.
202.
203.
204.
205.
206. }

// on le vrifie
}
@Test
public void test11() {
// sert tester la mthode clean
}

Dans la classe de tests prcdente, la base est vide avant chaque test.
Travail faire : crire les mthodes suivantes :
- test02 : on s'inspirera de test01 ;
- test03 : un employ a un champ de type Indemnite. Il faut donc crer une entit Indemnite et une entit Employe ;
- test04 ;
En procdant de la mme faon que pour la classe de tests [JUnitInitDB], on obtient les rsultats suivants :

en [1], on excute la classe de tests ;


en [2], les rsultats des tests dans la fentre [Test Results] ;

Provoquons une erreur pour voir comment cela est signal dans la page des rsultats :
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.
// vrification
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'excution de la classe de tests donne les
rsultats suivants :

119/290

4.9

la page des rsultats [1] montre maintenant qu'il y a eu des tests non russis ;
en [2], un rsum 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 ;

La couche [mtier] de l'application [PAM]

Maintenant que la couche [DAO] a t crite, nous passons l'tude de la couche mtier [2] :

Couche
[ui]

Couche
[mtier]

Couche
[DAO]

Objets image
de la BD

4.9.1

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Spring

L'interface Java [IMetier]

Celle-ci a t dcrite page 104. Nous la rappelons ci-dessous :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.

package metier;
import [Link];
import [Link];
public interface IMetier {
// obtenir la feuille de salaire
FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int nbJoursTravaills );
// liste des employs
List<Employe> findAllEmployes();
}

Le paquetage [metier] comprendra, outre l'interface [IMetier] et son implmentation [Metier], deux autres classes [FeuilleSalaire] et
[ElementsSalaire]. La classe [FeuilleSalaire] a t brivement prsente page 104. Nous revenons dessus maintenant.

120/290

4.9.2

La classe [FeuilleSalaire]

La mthode [calculerFeuilleSalaire] de l'interface [IMetier] rend un objet de type [FeuilleSalaire] qui reprsente les diffrents
lments d'une feuille de salaire. Sa dfinition est la suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.

package metier;

ligne 7 : la classe implmente l'interface Serializable parce que ses instances sont susceptibles d'tre changes sur le rseau ;
ligne 9 : l'employ concern par la feuille de salaire ;
ligne 10 : les diffrents taux de cotisation ;
ligne 11 : les diffrentes indemnits lies l'indice de l'employ ;
ligne 12 : les lments constitutifs de son salaire ;
lignes 14-22 : les deux constructeurs de la classe ;
lignes 25-27 : mthode [toString] identifiant un objet [FeuilleSalaire] particulier ;
lignes 30 et au-del : les accesseurs publics aux champs privs de la classe ;

import [Link];
import [Link];
import [Link];
public class FeuilleSalaire implements Serializable {
// champs privs
private Employe employe;
private Cotisation cotisation;
private ElementsSalaire elementsSalaire;
// constructeurs
public FeuilleSalaire() {
}
public FeuilleSalaire(Employe employe, Cotisation cotisation, ElementsSalaire elementsSalaire) {
[Link] = employe;
[Link] = cotisation;
[Link] = elementsSalaire;
}
// toString
@Override
public String toString() {
return [Link]("FeuilleSalaire[%s, %s, ,%s]", employe, cotisation, elementsSalaire);
}

// accesseurs
...

La classe [ElementsSalaire] rfrence ligne 11 de la classe [FeuilleSalaire] ci-dessus, rassemble les lments constituant une fiche de
paie. Sa dfinition est la suivante :
1. package metier;
2.
3. import [Link];
4.
5. public class ElementsSalaire implements Serializable {
6.
7.
// champs privs
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() {

121/290

30.

return [Link]("[salaire base=%s, cotisations sociales=%s, indemnits d'entretien=%s, indemnits 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 implmente l'interface Serializable parce qu'elle est un composant de la classe FeuilleSalaire qui doit tre
srialisable.
ligne 8 : le salaire de base
ligne 9 : les cotisations sociales payes sur ce salaire de base
ligne 10 : les indemnits journalires d'entretien de l'enfant
ligne 11 : les indemnits journalires 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 : mthode [toString] identifiant un objet [ElementsSalaire] particulier
lignes 34 et au-del : les accesseurs publics aux champs privs de la classe

4.9.3

La classe d'implmentation [Metier] de la couche [metier]

La classe d'implmentation [Metier] de la couche [metier] pourrait tre la suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.

package metier;

ligne 12 : l'annotation Spring @Transactional fait que chaque mthode de la classe se droulera au sein d'une transaction.
On avait dj mis cette annotation sur les classes de la couche [DAO]. Lorsqu'une mthode de la couche [mtier] appelle
une mthode de la couche [DAO], il n'y pas de nouvelle transaction cre. La mthode de la couche [DAO] utilise celle
cre par la couche [mtier]. Au final, la dure de vie du contexte de persistance JPA est celle de la transaction de la
couche [mtier] et non plus celle de la transaction de la couche [DAO] ;
lignes 16-17 : les rfrences sur les couches [DAO] des entits [Cotisation, Employe] ;
lignes 20-23 : la mthode [calculerFeuilleSalaire] ;
lignes 26-29 : la mthode [findAllEmployes] ;
ligne 31 et au-del : les accesseurs publics des champs privs de la classe.

import
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

@Transactional
public class Metier implements IMetier {
// rfrences sur la couche [dao]
private ICotisationDao cotisationDao = null;
private IEmployeDao employeDao = null;
// obtenir la feuille de salaire
@Override
public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int nbJoursTravaills) {
...
}
// liste des employs
@Override
public List<Employe> findAllEmployes() {
...
}
// getters et setters
...
}

Travail faire : crire le code de la mthode [findAllEmployes].


Travail faire : crire le code de la mthode [calculerFeuilleSalaire].

122/290

On notera les points suivants :

le mode de calcul du salaire a t expliqu au paragraphe 4.2, page 64 ;

si le paramtre [SS] ne correspond aucun employ (la couche [DAO] a renvoy un pointeur null), la mthode lancera une
exception de type [PamException] avec un code d'erreur appropri.

4.9.4

Tests de la couche [metier]

Nous crons deux programmes de test :

Les classes de tests sont cres 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 donnes sont recres chaque nouvelle excution 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>
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>

La classe [JUnitMetier_1] pourrait tre la suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.

package metier;
import
import
import
import
import
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

public class JUnitMetier_1 {


// couches dao
static private IMetier metier;
@BeforeClass
public static void init(){
// log
log("init");
// instanciation couche [metier]
ApplicationContext ctx = new ClassPathXmlApplicationContext("[Link]");
metier = (IMetier) [Link]("metier");
// couches dao
IEmployeDao employeDao=(IEmployeDao) [Link]("employeDao");
ICotisationDao cotisationDao=(ICotisationDao) [Link]("cotisationDao");
IIndemniteDao indemniteDao=(IIndemniteDao) [Link]("indemniteDao");
// on vide la base
[Link]().stream().forEach((employe) -> {

123/290

34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.

[Link](employe);
});
[Link]().stream().forEach((cotisation) -> {
[Link](cotisation);
});
[Link]().stream().forEach((indemnite) -> {
[Link](indemnite);
});
// on la remplit
// Indemnite(Long id, int version, int indice, double baseHeure, double entretienJour, double repasJour, double
indemnitesCp)
Indemnite indemnite1 = [Link](new Indemnite(null, 1, 1, 1.93, 2, 3, 12));
Indemnite indemnite2 = [Link](new Indemnite(null, 1, 2, 2.1, 2.1, 3.1, 15));
// Employe(Long id, int version, String prenom, String nom, String ss, String adresse, String ville, String cp,
Indemnite indemnite)
[Link](new Employe(null, 1, "Marie", "Jouveinal", "254104940426058", "5 rue des oiseaux", "St Corentin",
"49203", indemnite2));
[Link](new Employe(null, 1, "Justine", "Laverti", "260124402111742", "La brlerie", "St Marcel", "49014",
indemnite1));
// Cotisation(Long id, int version, double secu, double retraite, double csgd, double csgrds)
[Link](new Cotisation(null, 1, 9.39, 7.88, 6.15, 3.49));
}

49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74. }

@AfterClass
public static void terminate() {
}
// logs
static private void log(String message) {
[Link]("----------- " + message);
}
// test
@Test
public void test01(){
// calcul de feuilles de salaire
[Link]([Link]("260124402111742",30, 5));
[Link]([Link]("254104940426058",150, 20));
try {
[Link]([Link]("xx", 150, 20));
} catch (PamException ex) {
[Link]([Link]("PamException[Code=%d, message=%s]",[Link](), [Link]()));
}
}

Il n'y a pas d'assertion [Link] dans la classe. On cherche simplement calculer quelques salaires afin de les
vrifier ensuite la main. L'affichage cran obtenu par l'excution de la classe prcdente est le suivant :
1.

3.

FeuilleSalaire[Employ=[id=552, version=1, prnom=Justine, nom=Laverti, adresse=La brlerie, 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, indemnits d'entretien=10.0, indemnits de repas=15.0,salaire net=72.4]]
FeuilleSalaire[Employ=[id=551, version=1, prnom=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, indemnits d'entretien=42.0, indemnits de repas=62.0,salaire net=368.77]]
PamException[Code=101, message=L'employ de n[xx] est introuvable]

ligne 1 : la feuille de salaire de Justine Laverti ;


ligne 2 : la feuille de salaire de Marie Jouveinal ;
ligne 3 : l'exception due au fait que l'employ de n SS 'xx' n'existe pas ;

2.

Travail faire : la ligne 27 de [JUnitMetier_1] utilise le bean Spring nomm metier. Donner la dfinition 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 dfinition du bean de la couche [mtier].
La classe [JUnitMetier_2] pourrait tre la suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.

package metier;
import
import
import
import
import
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

124/290

15. import [Link];


16.
17. public class JUnitMetier_2 {
18.
19. // couches dao
20.
static private IMetier metier;
21.
22.
@BeforeClass
23.
public static void init(){
24.
// log
25.
log("init");
26.
// instanciation couche [metier]
27.
ApplicationContext ctx = new ClassPathXmlApplicationContext("[Link]");
28.
metier = (IMetier) [Link]("metier");
29.
// couches dao
30.
IEmployeDao employeDao=(IEmployeDao) [Link]("employeDao");
31.
ICotisationDao cotisationDao=(ICotisationDao) [Link]("cotisationDao");
32.
IIndemniteDao indemniteDao=(IIndemniteDao) [Link]("indemniteDao");
33.
// on vide la base
34.
[Link]().stream().forEach((employe) -> {
35.
[Link](employe);
36.
});
37.
[Link]().stream().forEach((cotisation) -> {
38.
[Link](cotisation);
39.
});
40.
[Link]().stream().forEach((indemnite) -> {
41.
[Link](indemnite);
42.
});
43.
// on remplit la base
44.
// Indemnite(Long id, int version, int indice, double baseHeure, double entretienJour, double repasJour, double
indemnitesCp)
45.
Indemnite indemnite1 = [Link](new Indemnite(null, 1, 1, 1.93, 2, 3, 12));
46.
Indemnite indemnite2 = [Link](new Indemnite(null, 1, 2, 2.1, 2.1, 3.1, 15));
47.
// Employe(Long id, int version, String prenom, String nom, String ss, String adresse, String ville, String cp,
Indemnite indemnite)
48.
[Link](new Employe(null, 1, "Marie", "Jouveinal", "254104940426058", "5 rue des oiseaux", "St Corentin",
"49203", indemnite2));
49.
[Link](new Employe(null, 1, "Justine", "Laverti", "260124402111742", "La brlerie", "St Marcel", "49014",
indemnite1));
50.
// Cotisation(Long id, int version, double secu, double retraite, double csgd, double csgrds)
51.
[Link](new Cotisation(null, 1, 9.39, 7.88, 6.15, 3.49));
52.
}
53.
54.
@AfterClass
55.
static public void terminate() {
56.
}
57.
58.
// logs
59.
static private void log(String message) {
60.
[Link]("----------- " + message);
61.
}
62.
63.
// test
64.
@Test
65.
public void test01(){
66.
...
67.
}
68. }

La classe [JUnitMetier_2] est une copie de la classe [JUnitMetier_1] o cette fois, des assertions ont t places dans la mthode
test01.
Travail faire : crire la mthode test01.
Lors de l'excution de la classe [JUnitMetier_2], on obtient les rsultats suivants si tout va bien :

4.10

La couche [ui] de l'application [PAM] version console

Maintenant que la couche [mtier] a t crite, il nous reste crire la couche [ui] [1] :

125/290

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Spring

Nous crerons deux implmentations diffrentes de la couche [ui] : une version console et une version graphique swing :

4.10.1

La classe [[Link]]

Nous nous intressons tout d'abord l'application console implmente par la classe [[Link]] ci-dessus. Son
fonctionnement a t dcrit au paragraphe 4.3, page 65. Le squelette de la classe [Main] pourrait tre le suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.

package [Link];
import [Link];
import [Link];
import [Link];
import
import
import
import

[Link];
[Link];
[Link];
[Link];

public class Main {


public static void main(String[] args) {
// donnes locales
final String syntaxe = "pg num_securite_sociale nb_heures_travailles nb_jours_travaills";
// on vrifie le nombre de paramtres
// liste des erreurs
List<String> erreurs = new ArrayList<>();
// le second paramtre doit tre un nombre rel >0
// erreur ?
if (argumentErron) {
[Link]([Link]("Le nombre d'heures travailles [%s] est erron", args[1]));
}
// le troisime paramtre doit tre un nombre entier >0
// erreur ?
if (argumentErron) {
[Link]([Link]("Le nombre de jours travaills [%s] est erron", args[2]));
}
// des erreurs ?
// c'est bon - on peut demander la feuille de salaire
FeuilleSalaire feuilleSalaire;
try {
// instanciation couche [metier]
// calcul de la feuille de salaire
} catch (PamException ex) {
[Link]([Link]("L'erreur suivante s'est produite : %s", [Link]()));
return;

126/290

45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.

} catch (Exception ex) {


[Link]([Link]("L'erreur suivante s'est produite : %s", ex));
return;
}
// affichage dtaill
String output = "Valeurs saisies :\n";
output += ajouteInfo("N de scurit sociale de l'employ", args[0]);
output += ajouteInfo("Nombre d'heures travailles", args[1]);
output += ajouteInfo("Nombre de jours travaills", args[2]);
output += ajouteInfo("\nInformations Employ", "");
output += ajouteInfo("Nom", [Link]().getNom());
output += ajouteInfo("Prnom", [Link]().getPrenom());
output += ajouteInfo("Adresse", [Link]().getAdresse());
output += ajouteInfo("Ville", [Link]().getVille());
output += ajouteInfo("Code Postal", [Link]().getCp());
output += ajouteInfo("Indice", "" + [Link]().getIndemnite().getIndice());
output += ajouteInfo("\nInformations Cotisations", "");
output += ajouteInfo("CSGRDS", "" + [Link]().getCsgrds() + " %");
output += ajouteInfo("CSGD", "" + [Link]().getCsgd() + " %");
output += ajouteInfo("Retraite", "" + [Link]().getRetraite() + " %");
output += ajouteInfo("Scurit sociale", "" + [Link]().getSecu() + " %");
output += ajouteInfo("\nInformations Indemnits", "");
output += ajouteInfo("Salaire horaire", "" + [Link]().getIndemnite().getBaseHeure() + " euro");
output += ajouteInfo("Entretien/jour", "" + [Link]().getIndemnite().getEntretienJour() + " euro");
output += ajouteInfo("Repas/jour", "" + [Link]().getIndemnite().getRepasJour() + " euro");
output += ajouteInfo("Congs Pays", "" + [Link]().getIndemnite().getIndemnitesCp() + " %");
output += ajouteInfo("\nInformations Salaire", "");
output += ajouteInfo("Salaire de base", "" + [Link]().getSalaireBase() + " euro");
output += ajouteInfo("Cotisations sociales", "" + [Link]().getCotisationsSociales() + "
euro");
74.
output += ajouteInfo("Indemnits d'entretien", "" + [Link]().getIndemnitesEntretien() + "
euro");
75.
output += ajouteInfo("Indemnits 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. }

Travail faire : complter le code ci-dessus.

4.10.2

Excution

Pour excuter la classe [[Link]], on procdera de la faon suivante :

127/290

en [1], slectionner les proprits du projet,


en [2], slectionner la proprit [Run] du projet,
utiliser le bouton [3] pour dsigner la classe (dite classe principale) excuter,
celle-ci a besoin de trois arguments pour s'excuter (n SS, nombre d'heures travailles, nombre de jours travaills). Ces
arguments sont placs en [5],
ceci fait, on peut excuter le projet [6]. La configuration prcdente fait que c'est la classe [[Link]] qui va tre
excute.

Les rsultats de l'excution sont obtenus dans la fentre [output] :

128/290

4.11

La couche [ui] de l'application [PAM] version graphique

Nous implmentons maintenant la couche [ui] avec une interface graphique :

Couche
[ui]

Couche
[metier]

Objets image
de la BD

Couche
[DAO]

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Spring

129/290

4.11.1

en [1], la classe [PamJFrame] de l'interface graphique ;


en[2] : l'interface graphique ;

Un rapide tutoriel

Pour crer l'interface graphique, on pourra procder de la faon suivante :

[1] : on cre un nouveau fichier avec le bouton [1] [New File...] ;


[2] : on choisit la catgorie du fichier [Swing GUI Forms], c.a.d. formulaires graphiques ;
[3] : on choisit le type [JFrame Form], un type de formulaire vide ;

[5] : on donne un nom au formulaire qui sera aussi une classe ;


[6] : on place le formulaire dans un paquetage ;
[7] : le formulaire est ajout l'arborescence du projet ;
[8] : le formulaire est accessible selon deux perspectives : [Design] [8] qui permet de dessiner les diffrents composants du
formulaire, [Source] [10 ci-dessous] qui donne accs au code Java du formulaire. Au final, un formulaire est une classe Java
comme une autre. La perspective [Design] est une facilit pour dessiner le formulaire. A chaque ajout de composant en
mode [Design], du code Java est ajout dans la perspective [Source] pour le prendre en compte ;

[11] : la liste des composants Swing disponibles pour un formulaire est trouve dans la fentre [Palette] ;
[12] : la fentre [Inspector] prsente l'arborescence des composants du formulaire. Les composants ayant une
reprsentation visuelle se retrouveront dans la branche [JFrame], les autres dans la branche [Other Components] ;

130/290

en [13], nous slectionnons un composant [JLabel] par un clic simple ;


en [14], nous le dposons sur le formulaire en mode [Design] ;
en [15], nous dfinissons les proprits du JLabel (text, font) ;

en [16], le rsultat obtenu ;


en [17], on demande la prvisualisation du formulaire ;
en [18], le rsultat ;
en [19], le label [JLabel1] a t ajout l'arborescence des composants dans la fentre [Inspector] ;

en [20] et [21] : dans la perspective [Source] du formulaire, du code Java a t ajout pour grer le JLabel ajout ;

131/290

Un tutoriel sur la construction de formulaires avec Netbeans est disponible l'url


[[Link]

4.11.2

L'interface graphique [PamJFrame]

On construira l'interface graphique suivante :

en [1], l'interface graphique ;


en [2], l'arborescence de ses composants : un JLabel et six conteneurs JPanel ;

JLabel1

JPanel1

JPanel2

132/290

JPanel3

JPanel4

JPanel5

Travail faire : construire l'interface graphique prcdente en s'aidant du tutoriel [[Link]

4.11.3

Les vnements de l'interface graphique

Lectures conseilles : chapitre [Interfaces graphiques] de [ref2].


Nous grerons le clic sur le bouton [jButtonSalaire]. Pour crer la mthode de gestion de cet vnement, on pourra procder
comme suit en mode [Design] de l'interface graphique [1-4]:

133/290

Le gestionnaire du clic sur le bouton [JButtonSalaire] est gnr :


1.
2.
3.

private void jButtonSalaireActionPerformed([Link] evt) {


// TODO add your handling code here:

Le code Java qui associe la mthode prcdente au clic sur le bouton [JButtonSalaire] est lui aussi gnr :
1.
2.
3.
4.
5.
6.

});

[Link]("Salaire");
[Link](new [Link]() {
public void actionPerformed([Link] evt) {
jButtonSalaireActionPerformed(evt);
}

Ce sont les lignes 2-5 qui indiquent que le clic (evt de type ActionPerformed) sur le bouton [jButtonSalaire] (ligne 2) doit tre gr par
la mthode [jButtonSalaireActionPerformed] (ligne 4).
Nous grerons galement, l'vnement [caretUpdate] (dplacement du curseur de saisie) sur le champ de saisie [jTextFieldHT]. Pour
crer le gestionnaire de cet vnement, nous procdons comme prcdemment :

Le gestionnaire de l'vnement [caretUpdate] sur le champ de saisie [jTextFieldHT] est gnr :


private void jTextFieldHTCaretUpdate([Link] evt) {
...
}

Le code Java qui associe la mthode prcdente l'vnement [caretUpdate] sur le champ de saisie [jTextFieldHT] est lui aussi
gnr :
1.
2.
3.
4.
5.

[Link](new [Link]() {
public void caretUpdate([Link] evt) {
jTextFieldHTCaretUpdate(evt);
}
});

Les lignes 1-4 indiquent que l'vnement [caretUpdate] (ligne 2) sur le bouton [jTextFieldHT] (ligne 1) doit tre gr par la mthode
[ jTextFieldHTCaretUpdate] (ligne 3).

4.11.4

Initialisation de l'interface graphique

Revenons l'architecture de notre application :

Couche
[ui]

Couche
[metier]

Objets image
de la BD

Couche
[DAO]

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Spring

La couche [ui] a besoin d'une rfrence sur la couche [metier]. Rappelons comment cette rfrence avait t obtenue dans
l'application console :
1.
2.

// instanciation couche [metier]


ApplicationContext ctx = new ClassPathXmlApplicationContext("[Link]");

134/290

3.

IMetier metier = (IMetier) [Link]("metier");

La mthode est la mme dans l'application graphique. Il faut que lorsque celle-ci s'initialise, la rfrence [IMetier metier] de la ligne
3 ci-dessus soit galement initialise. Le code gnr pour l'interface graphique est pour l'instant le suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.

package [Link];

lignes 42-45 : la mthode statique [main] qui lance l'application ;


ligne 44 : une instance de l'interface graphique [PamJFrame] est cre et rendue visible ;
lignes 16-18 : le constructeur de l'interface graphique ;
ligne 17 : appel la mthode [initComponents] dfinie ligne 26. Cette mthode est auto-gnre partir du travail fait en
mode [Design]. On ne doit pas y toucher ;
ligne 30 : la mthode qui va grer le dplacement du curseur de saisie dans le champ [jTextFieldHT] ;
ligne 35 : la mthode qui va grer le clic sur le bouton [jButtonSalaire] ;

import
import
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

public class PamJFrame extends [Link] {


/** Creates new form PamJFrame */
public PamJFrame() {
initComponents();
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
private void initComponents() {
...
}// </editor-fold>//GEN-END:initComponents
private void jTextFieldHTCaretUpdate([Link] evt) {//GEN-FIRST:event_jTextFieldHTCaretUpdate
...
}//GEN-LAST:event_jTextFieldHTCaretUpdate

private void jButtonSalaireActionPerformed([Link] evt) {//GENFIRST: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 heuresTravailles=0;
58. ..
59. }

Pour ajouter au code prcdent nos propres initialisations, nous pouvons procder comme suit :
1.
2.
3.
4.
5.

/** Creates new form PamJFrame */


public PamJFrame() {
initComponents();
doMyInit();
}

135/290

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 heuresTravailles=0;
13.
14.
// initialisations propritaires
15.
public final void doMyInit(){
16.
// init contexte
17.
try{
18.
// instanciation couche [metier]
19.
20.
// on rcupre la liste des employs
21.
}catch (PamException ex){
22.
[Link]([Link]());
23.
return;
24.
}
25.
// composants
26.
// bouton salaire inhib
27.
28.
// textAreaStatus visible
29.
30.
// spinner jours travaills initialis
31.
[Link](new SpinnerNumberModel(0,0,31,1));
32.
// combobox employs 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 mthode propritaire pour faire nos propres initialisations. Celles-ci sont dfinies par le code des
lignes 15-39 ;

Travail faire : en vous aidant des commentaires, complter le code de la procdure [doMyInit].

4.11.5

Gestionnaires d'vnements

Travail faire : crire la mthode [jTextFieldHTCaretUpdate]. Cette mthode doit faire en sorte que si la donne prsente dans le
champ [jTextFieldHT] n'est pas un nombre rel >=0, alors le bouton [jButtonSalaire] doit tre inactif.
Travail faire : crire la mthode [jButtonSalaireActionPerformed] qui doit afficher la feuille de salaire de l'employ slectionn
dans [jComboBoxEmployes].

4.11.6

Excution de l'interface graphique

Pour excuter l'interface graphique, on modifiera la configuration [Run] du projet :

en [1-2], mettre la classe de l'interface graphique

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'excuter le projet.

136/290

4.12

Implmentation de la couche JPA avec EclipseLink

Nous nous intressons l'architecture suivante o la couche JPA est dsormais implmente par EclipseLink [1] :

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

Interface
[JPA]

Implmentation
[EclipseLink]

Couche
[JDBC]

1
7

4.12.1

Spring

Le projet Netbeans

Le nouveau projet Netbeans est obtenu par recopie du projet prcdent [ 1-9] :

137/290

Le projet [5] doit tre modifi en deux points pour l'adapter la nouvelle couche JPA / EclipseLink :
1.
2.

en [11], les fichiers de configuration de Spring doivent tre modifis. On y trouve en effet la configuration de la couche
JPA ;
en [12], les bibliothques du projet doivent tre modifies : celles d'Hibernate doivent tre remplaces par celles de
EclipseLink.

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

138/290

35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.

<!--[[Link] -->
<!-- This plugin ensures the EclipseLink static weaving -->
<plugin>
<artifactId>staticweave-maven-plugin</artifactId>
<groupId>[Link]</groupId>
<version>1.0.0</version>
<executions>
<execution>
<goals>
<goal>weave</goal>
</goals>
<phase>process-classes</phase>
<configuration>
<logLevel>ALL</logLevel>
<includeProjectClasspath>true</includeProjectClasspath>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>[Link]</groupId>
<artifactId>eclipselink</artifactId>
<version>2.6.3</version>
</dependency>
</dependencies>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!-- This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build
itself. -->
<plugin>
<groupId>[Link].m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>
[Link]
</groupId>
<artifactId>
staticweave-maven-plugin
</artifactId>
<versionRange>
[1.0.0,)
</versionRange>
<goals>
<goal>weave</goal>
</goals>
</pluginExecutionFilter>
<action>
<execute>
<runOnIncremental>true</runOnIncremental>
</execute>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>

65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101. <properties>
102.
<[Link]>UTF-8</[Link]>
103.
<[Link]>1.8</[Link]>
104.
<[Link]>1.8</[Link]>
105. </properties>
106. </project>

lignes 155-19 : la dpendance pour l'implmentation JPA EclipseLink ;


lignes 33-99 : les plugins ncessaires pour qu'EclipseLink respecte le mode [LAZY] des annotations [@ManyToOne] et
[@OneToMany] ;

Les fichiers de configuration de Spring doivent tre modifis pour indiquer que l'implmentation JPA a chang. Dans les deux
fichiers, seule la section configurant la couche JPA change. Par exemple dans [[Link]] on a actuellement le
code suivant :
1.
2.

<!-- configuration JPA -->


<bean id="entityManagerFactory" class="[Link]">

139/290

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

Les lignes 1-20 configurent la couche JPA. L'implmentation JPA utilise est Hibernate (ligne 5). Pour passer une implmentation
JPA / EclipseLink, les lignes ci-dessus sont remplaces 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>

4.12.2

ligne 5 : l'implmentation JPA utilise est EclipseLink ;


ligne 6 : la proprit databasePlatform fixe le SGBD cible, ici MySQL ;
ligne 11 : pour gnrer les tables de la base de donnes lorsque la couche JPA est instancie ;
ligne 12 : pour visualiser sur la console les ordres SQL mis par la couche JPA ;
lignes 13-15 : pour indiquer qu'un weaving statique est fait pour les entit JPA ( la compilation). Il est ncessaire de mettre
ces lignes car sinon un weaving dynamique est tent l'excution et alors diverses erreurs en dcoulent ;

Mise en oeuvre des tests

Avant de tester l'application entire, il est bon de vrifier que les tests JUnit passent avec la nouvelle implmentation JPA.
Nous construisons le projet (Clean and Build) puis nous excutons le test [JUnitInitDB] :

140/290

en [1-3], le test JUnitInitDB est excut ;


en [4-5], il russit ;

L'excution du test donne les rsultats console suivants :


1.
2.
3.
4.
5.
6.
7.
8.

Employs ---------------------Employ=[id=7, version=1, prnom=Marie, nom=Jouveinal, adresse=5 rue des oiseaux, ville=St Corentin, code postal=49203,
indemniteId=null]
Employ=[id=8, version=1, prnom=Justine, nom=Laverti, adresse=La brlerie, ville=St Marcel, code postal=49014,
indemniteId=null]
Indemnits ---------------------Indemnite[id=7, version=1, indice=1, base heure=1.93, entretien jour=2.0, repas jour=3.0, indemnits CP=12.0]
Indemnite[id=8, version=1, indice=2, base heure=2.1, entretien jour=2.1, repas jour=3.1, indemnits CP=15.0]
Cotisations ---------------------Cotisations=[id=4, version=1, csgrds=3.49, csgd=6.15, secu=9.39, retraite=7.88]

On peut remarquer, lignes 2 et 3 qu'on n'a pas rcupr la cl trangre [indemniteId] des deux employs alors qu'avec Hibernate
on les avait. On retrouve de nouveau une diffrence de rsultats selon l'implmentation JPA. Ici, le fait qu'on n'ait pas pu obtenir la
cl trangre [indemniteId] des employs n'a pas d'importance. Au paragraphe 4.6.5, page 91, nous avons vu qu'EclipseLink
retrouvait l'entit [Indemnite [Link]] ds que ce champ de l'entit [Employe] tait rfrenc dans le code. Si nous
changeons la mthode [[Link]] de la faon suivante :
1.
2.
3.
4.

// toString
@Override
public String toString() {
return [Link]("Employ=[id=%s, version=%s, prnom=%s, nom=%s, adresse=%s, ville=%s, code postal=%s,
[Link]=%s]",
id, version, prenom, nom, adresse, ville, cp, [Link]());
}

5.
6.

les rsultats du test [JUnitInitDB] deviennent alors les suivants :


1.
2.
3.
4.
5.
6.
7.
8.

Employs ---------------------Employ=[id=9, version=1, prnom=Marie, nom=Jouveinal, adresse=5 rue des oiseaux, ville=St Corentin, code postal=49203,
[Link]=10]
Employ=[id=10, version=1, prnom=Justine, nom=Laverti, adresse=La brlerie, ville=St Marcel, code postal=49014,
[Link]=9]
Indemnits ---------------------Indemnite[id=9, version=1, indice=1, base heure=1.93, entretien jour=2.0, repas jour=3.0, indemnits CP=12.0]
Indemnite[id=10, version=1, indice=2, base heure=2.1, entretien jour=2.1, repas jour=3.1, indemnits CP=15.0]
Cotisations ---------------------Cotisations=[id=5, version=1, csgrds=3.49, csgd=6.15, secu=9.39, retraite=7.88]

[Link]

JUnitDao

Note : la classe [JUnitDao] est disponible dans le support de cours.


Travail faire : vrifiez que le test [JUnitDao] passe.

4.12.3

Les autres tests

Une fois la couche [DAO] teste et considre correcte, on pourra passer aux tests de la couche [metier] et ceux du projet luimme dans sa version console ou graphique. On prendra soin auparavant de commenter les lignes de [[Link]] et
[[Link]] qui suppriment les tables de la base de donnes chaque excution 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]" />
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.
</props>
14.
</property>
-->
15.
<property name="loadTimeWeaver">
16.
<bean class="[Link]"/>
17.
</property>
18. </bean>

Lorsqu'on a besoin de remplir la base, il suffit d'excuter le test [JUnitInitDB].

141/290

Le changement d'implmentation JPA n'influe en rien sur les couches [metier] et [ui] et donc si ces couches fonctionnaient avec
Hibernate, elles fonctionneront avec EclipseLink quelques prcautions prs : il ne faut pas que la couche [DAO] lance des
exceptions propres l'implmentation JPA utilise. Celles-ci doivent tre encapsules dans des exceptions propres l'application.

4.12.4

Travail faire

Travail faire : refaire les tests des applications console et swing avec diffrents SGBD : Postgresql, Oracle XE, SQL Server.

142/290

5 Version 2 : Architecture OpenEJB / JPA


5.1

Introduction aux principes du portage

Note : le TD est lire jusqu'au paragraphe 5.2, page 148 o commence le travail pratique. Il n'y a rien faire avant.
Nous prsentons ici les principes qui vont gouverner le portage d'une application JPA / Spring / Hibernate vers une application
JPA / OpenEJB / EclipseLink. On attendra le paragraphe 5.2, page 148 pour crer les projets Maven.

5.1.1

Les nouvelles architectures

L'implmentation actuelle avec Spring / Hibernate

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

71

Interface
[JPA]

Implmentation
[Hibernate]
2

Couche
[JDBC]

Implmentation
[EclipseLink]
2

Couche
[JDBC]

Implmentation
[EclipseLink]
2

Couche
[JDBC]

Spring

Les deux implmentations construire avec OpenEJB / EclipseLink

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

Interface
[JPA]

OpenEjb

Dans cette implmentation, la couche [ui] sera un client local de la couche [mtier].

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

Interface
[JPA]

OpenEjb

Dans cette implmentation, la couche [ui] sera un client distant de la couche [mtier].

5.1.2

5.1.3

Les bibliothques des projets


les couches [DAO] et [metier] ne sont plus instancies par Spring. Elles le sont par le conteneur OpenEJB ;
les bibliothques du conteneur Spring et sa configuration disparaissent au profit des bibliothques du conteneur OpenEJB
et sa configuration ;
les bibliothques de la couche JPA / Hibernate sont remplaces par celles de la couche JPA / EclipseLink ;

Configuration de la couche JPA / EclipseLink / OpenEJB

le fichier [META-INF/[Link]] configurant la couche JPA devient le suivant :

1.

<?xml version="1.0" encoding="UTF-8"?>

143/290

2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.

<persistence version="2.1"
xmlns="[Link]
xmlns:xsi="[Link]
xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-pam-jpa-eclipselinkPU" transaction-type="JTA">
<provider>[Link]</provider>
<class>[Link]</class>
<class>[Link]</class>
<class>[Link]</class>
<properties>
<property name="[Link]" value="drop-and-create"/>
<property name="[Link]" value="FINE"/>
</properties>
</persistence-unit>
</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'implmentation JPA utilise est EclipseLink
lignes 8-10 : les entits gres par la couche JPA
lignes 11-14 : proprits du provider EclipseLink
ligne 12 : chaque excution, les tables seront cres
Les caractristiques JDBC de la source de donnes JTA utilise par le conteneur OpenEJB seront prcises par le fichier
de configuration [conf/[Link]] suivant :

1.
2.
3.
4.
5.
6.
7.
8.

<?xml version="1.0"?>
<openejb>
<Resource id="Default JDBC Database">
JdbcDriver [Link]
JdbcUrl jdbc:mysql://localhost:3306/dbpam_hibernate
UserName root
</Resource>
</openejb>

ligne 3 : on utilise l'id Default JDBC Database lorsqu'on travaille avec un conteneur OpenEJB embarqu (embedded)
dans l'application elle-mme ;
ligne 5 : nous utilisons le base MySQL [dbpam_hibernate] ;

5.1.4

Implmentation de la couche [DAO] par des EJB

Les classes implmentant la couche [DAO] deviennent des EJB. Prenons l'exemple de la classe [CotisationDao] :
L'interface [ICotisationDao] dans la version Spring tait la suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.

package dao;
import [Link];
import [Link];
import [Link];
public interface ICotisationDao {
// crer une nouvelle cotisation
Cotisation create(Cotisation cotisation);
// modifier une cotisation existante
Cotisation edit(Cotisation cotisation);
// supprimer une cotisation existante
void destroy(Cotisation cotisation);
// chercher une cotisation particulire
Cotisation find(Long id);

// obtenir tous les objets Cotisation


List<Cotisation> findAll();

L'EJB va implmenter cette mme interface sous deux formes diffrentes : une locale et une distante. L'interface locale
peut tre utilise par un client s'excutant dans la mme JVM, l'interface distante par un client s'excutant dans une autre
JVM.
L'interface locale :

144/290

1.
2.
3.
4.
5.
6.
7.

package dao;

ligne 6 : l'interface [ICotisationDaoLocal] hrite de l'interface [ICotisationDao] pour en reprendre toutes les mthodes.
Elle n'en ajoute pas de nouvelles ;
ligne 5 : l'annotation @Local en fait une interface locale pour l'EJB qui l'implmentera ;

import [Link];
@Local
public interface ICotisationDaoLocal extends ICotisationDao {
}

L'interface distante :
1.
2.
3.
4.
5.
6.
7.

package dao;

ligne 6 : l'interface [ICotisationDaoRemote] hrite de l'interface [ICotisationDao] pour en reprendre toutes les mthodes.
Elle n'en ajoute pas de nouvelles.
ligne 5 : l'annotation @Remote en fait une interface distante pour l'EJB qui l'implmentera.

import [Link];
@Remote
public interface ICotisationDaoRemote extends ICotisationDao {
}

La couche [DAO] est implmente par un EJB implmentant les deux interfaces (ce n'est pas obligatoire) :
1.
2.
3.
4.
5.
6.

@Stateless
@TransactionAttribute([Link])
public class CotisationDao implements ICotisationDaoLocal, ICotisationDaoRemote {
@PersistenceContext
private EntityManager em;

ligne 1 : l'annotation @Stateless qui fait de la classe un EJB ;


ligne 2 : l'annotation @TransactionAttribute qui fait que chaque mthode de la classe s'excutera au sein d'une
transaction ;
ligne 5 : l'annotation @PersistenceContext qui injecte dans la classe [CotisationDao] l'EntityManager de la couche JPA.
Elle est identique ce qu'on avait dans la version Spring ;
Lorsque l'interface locale de la couche [DAO] est utilise, le client de cette interface s'excute dans la mme JVM.

utilisateur

Couche interface
utilisateur [ui]

Couche mtier
[metier]

Couche d'accs aux


donnes [DAO]

Donnes

JVM
Ci-dessus, les couches [metier] et [DAO] changent des objets par rfrence. Lorsqu'une couche change l'objet partag,
l'autre couche voit ce changement.
Lorsque l'interface distante de la couche [DAO] est utilise, le client de cette interface s'excute habituellement dans une
autre JVM.
1
utilisateur

Couche
interface
utilisateur [ui]
JVM 1

Couche
mtier
[metier]

3
Rseau
tcp /ip

Couche d'accs aux


donnes [DAO]

Donnes

JVM 2

145/290

Ci-dessus, les couches [metier] et [DAO] changent des objets par valeur (srialisation / dsrialisation 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.

5.1.5

Implmentation de la couche [metier] par un EJB

La classe implmentant la couche [metier] devient elle aussi un EJB implmentant une interface locale et distante. L'interface
initiale [IMetier] tait la suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.

package metier;
import [Link];
import [Link];
public interface IMetier {
// obtenir la feuille de salaire
FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int nbJoursTravaills );
// liste des employs
List<Employe> findAllEmployes();
}

On cre une interface locale et une interface distante partir de l'interface prcdente :
1.
2.
3.
4.
5.
6.
7.
8.

package metier;

1.
2.
3.
4.
5.
6.
7.
8.

package metier;

import [Link];
@Local
public interface IMetierLocal extends IMetier{
}

import [Link];
@Remote
public interface IMetierRemote extends IMetier{
}

L'EJB de la couche [metier] implmente ces deux interfaces :


1.
2.
3.
4.
5.
6.
7.
8.
9.

@Stateless
@TransactionAttribute([Link])
public class Metier implements IMetierLocal, IMetierRemote {
// rfrences sur la couche [dao]
@EJB
private ICotisationDaoLocal cotisationDao = null;
@EJB
private IEmployeDaoLocal employeDao = null;

lignes 1-2 : dfinissent un EJB dont chaque mthode s'excute dans une transaction ;
ligne 7 : une rfrence sur l'interface locale de l'EJB [CotisationDao] ;
ligne 6 : l'annotation @EJB demande ce que le conteneur EJB injecte une rfrence sur l'interface locale de l'EJB
[CotisationDao] ;
lignes 8-9 : on refait la mme 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 initialiss avec des rfrences sur les
interfaces locales des deux EJB de la couche [DAO]. On fait donc ici l'hypothse que les couches [metier] et [DAO]
s'excuteront dans la mme JVM.
1
utilisateur

2
Couche
interface
utilisateur [ui]
JVM 1

3
Rseau
tcp /ip

Couche
mtier
[metier]

Couche d'accs
aux donnes
[DAO]

Donnes

JVM 2

146/290

5.1.6

Les clients des EJB


1
utilisateur

2
Couche
interface
utilisateur [ui]
JVM 1

Couche
mtier
[metier]

Rseau
tcp /ip

Couche d'accs
aux donnes
[DAO]

Donnes

JVM 2

Dans le schma ci-dessus, pour communiquer avec la couche [metier], la couche [ui] doit obtenir une rfrence sur l'interface
distante de l'EJB de la couche [metier].

utilisateur

Couche interface
utilisateur [ui]

Couche mtier
[metier]

Couche d'accs aux


donnes [DAO]

Donnes

JVM
Dans le schma ci-dessus, pour communiquer avec la couche [metier], la couche [ui] doit obtenir une rfrence sur l'interface locale
de l'EJB de la couche [metier]. La mthode pour obtenir ces rfrences diffre d'un conteneur l'autre. Pour le conteneur
OpenEJB, on pourra procder comme suit :
Rfrence sur l'interface locale :
1.
2.
3.
4.
5.
6.
7.

// on configure le conteneur Open EJB embarqu


Properties properties = new Properties();
[Link](Context.INITIAL_CONTEXT_FACTORY, "[Link]");
// initialisation du contexte JNDI avec les proprits prcdentes
InitialContext initialContext = new InitialContext(properties);
// instanciation couche mtier locale
IMetierLocal metier = (IMetierLocal) [Link]("MetierLocal");

lignes 2-5 : le conteneur OpenEJB est initialis ;


ligne 5 : on a un contexte JNDI (Java Naming and Directory Interface) qui permet d'obtenir des rfrences sur les EJB.
Chaque EJB est dsign par un nom JNDI :

pour l'interface locale on ajoute Local au nom de l'EJB (ligne 7) ;

pour l'interface distante on ajoute Remote au nom de l'EJB ;


Avec Java EE 5, ces rgles changent selon le conteneur EJB. C'est une difficult. Java EE 6 a introduit une
notation JNDI portable sur tous les serveurs d'applications.

Le code prcdent rcupre des rfrences sur les interfaces locales des EJB via leurs noms JNDI. Nous avons dit prcdemment
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 honore que si elle appartient une classe charge 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 charge par le conteneur EJB. On est
donc oblig d'utiliser les noms JNDI des EJB.
Ci-dessous, le code pour avoir une rfrence sur l'interface distante de l'EJB [Metier] :
1.
2.
3.
4.
5.
6.
7.

// on configure le conteneur Open EJB embarqu


Properties properties = new Properties();
[Link](Context.INITIAL_CONTEXT_FACTORY,
"[Link]");
// initialisation du contexte JNDI avec les proprits prcdentes
InitialContext initialContext = new InitialContext(properties);
// instanciation couche mtier distante
IMetierRemote metier = (IMetierRemote) [Link]("MetierRemote");

147/290

5.2

Travail pratique

On se propose de porter l'application Netbeans Spring / Hibernate vers une architecture OpenEJB / EclipseLink.
L'implmentation actuelle avec Spring / Hibernate

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

Interface
[JPA]

Implmentation
[Hibernate]
2

Couche
[JDBC]

Interface
[JPA]

Implmentation
[EclipseLink]

Couche
[JDBC]

Spring
L'implmentation construire avec OpenEJB / EclipseLink

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

OpenEjb
5.2.1

Configuration initiale du projet Netbeans [mv-pam-openejb-eclipselink]

charger le projet Maven [mv-pam-spring-hibernate]


crer un nouveau projet Maven Java [mv-pam-openejb-eclipselink] [1]

dans l'onglet [Files] [2], crer un dossier [conf] [3] sous la racine du projet ;
placer dans ce dossier, le fichier [[Link]] [4-10] suivant :

1.
2.
3.
4.
5.
6.
7.
8.

<?xml version="1.0"?>
<openejb>
<Resource id="Default JDBC Database">
JdbcDriver [Link]
JdbcUrl jdbc:mysql://localhost:3306/dbpam_hibernate
UserName root
</Resource>
</openejb>

148/290

crer 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.
2.
3.
4.
5.

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.1"
xmlns="[Link]
xmlns:xsi="[Link]
xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-pam-jpa-eclipselinkPU" transaction-type="JTA">
<provider>[Link]</provider>
<class>[Link]</class>
<class>[Link]</class>
<class>[Link]</class>
<properties>
<property name="[Link]" value="drop-and-create"/>
<property name="[Link]" value="FINE"/>
<!-- weaving -->
<property name="[Link]" value="static"/>
<property name="[Link]" value="true"/>
<property name="[Link]" value="true"/>
</properties>
</persistence-unit>
</persistence>

6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.

ligne 3 : on fera attention l'URL de cette ligne. Si on reprend celle prsente dans les fichiers [[Link]] que nous
avons utiliss jusqu' maintenant et qui avaient t gnrs par Netbeans, on a une erreur signale par OpenEJB et difficile
dboguer ;
ligne 13 : on demande des logs fins EclipseLink ;
ligne 12 : les tables seront cres 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 bibliothques OpenEJB, EclipseLink ainsi que le pilote JDBC de MySQL au fichier [[Link]] du projet :

149/290

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

5.2.2

lignes 39-43 : la dpendance OpenEJB ;


lignes 15-24 : les dpendances EclipseLink ;
lignes 26-30 : la dpendance du pilote JDBC de MySQL ;
lignes 32-37 : la dpendance sur JUnit ;
lignes 45-49 : la dpendance pour l'application swing ;

Portage de la couche [DAO]

Nous allons faire le portage de la couche [DAO] par copie de paquetages du projet [mv-pam-spring-hibernate] vers le projet [mvpam-openejb-eclipselink].

copier les packages [dao, exception, jpa] ;

150/290

Les erreurs signales ci-dessus viennent du fait que la couche [DAO] copie utilise Spring et que les bibliothques Spring ne font
plus partie du projet.

[Link]

L'EJB [CotisationDao]

Nous suivons ce qui a t expliqu au paragraphe 5.1.4, page 144. Nous crons les interfaces locale et distante du futur EJB
[CotisationDao] :
L'interface locale ICotisationDaoLocal :
1.
2.
3.
4.
5.
6.
7.

package dao;
import [Link];
@Local
public interface ICotisationDaoLocal extends ICotisationDao {
}

Pour avoir les bons import de paquetages, faire [clic droit sur le code / Fix Imports].
L'interface distante ICotisationDaoRemote :
1.
2.
3.
4.
5.
6.
7.

package dao;
import [Link];
@Remote
public interface ICotisationDaoRemote extends ICotisationDao {
}

Puis nous modifions la classe [CotisationDao] pour en faire un EJB :


1.
2.
3.
4.
5.
6.

@Stateless
@TransactionAttribute([Link])
public class CotisationDao implements ICotisationDaoLocal, ICotisationDaoRemote {
@PersistenceContext
private EntityManager em;

L'import que faisait cette classe sur le framework Spring disparat. Faire un [Clean and Build] du projet :

151/290

En [1], il n'y a plus d'erreurs sur la classe [CotisationDao].

[Link]

Les EJB [EmployeDao] et [IndemniteDao]

On rpte la mme dmarche pour les autres lments de la couche [DAO] :


interfaces IEmployeDaoLocal, IEmployeDaoRemote drives de IEmployeDao ;
EJB EmployeDao implmentant ces deux interfaces ;
interfaces IIndemniteDaoLocal, IIndemniteDaoRemote drives de IIndemniteDao ;
EJB IndemniteDao implmentant ces deux interfaces ;

Ceci fait, il n'y a plus d'erreurs dans le projet [2].

[Link]

La classe [PamException]

La classe [PamException] reste ce qu'elle tait un dtail prs :


1.
2.
3.
4.
5.

@ApplicationException(rollback=true)
public class PamException extends RuntimeException implements Serializable{
// code d'erreur
private int code;

La ligne 1 est ajoute. Pour avoir les bons import, faire [clic droit+Fix imports].
Pour comprendre l'annotation de la ligne 1, il faut se rappeler que chaque mthode des EJB de notre couche [DAO] :

s'excute dans une transaction dmarre et termine par le conteneur EJB ;

lance une exception de type [PamException] ds que quelque chose se passe mal ;

utilisateur

Couche
interface
utilisateur [ui]

Couche
mtier
[metier]

Proxy
EJB

Couche d'accs aux


donnes [DAO]

Donnes

Lorsque la couche [metier] appelle une mthode M de la couche [DAO], cet appel est intercept par le conteneur EJB. Tout se passe
comme s'il y avait une classe intermdiaire entre la couche [metier] et la couche [DAO], appele ici[Proxy EJB], interceptant tous les
appels vers la couche [DAO]. Lorsque l'appel la mthode M de la couche [DAO] est intercepte, le Proxy EJB dmarre une
transaction puis passe la main la mthode M de la couche [DAO] qui s'excute alors dans cette transaction. La mthode M se
termine avec ou sans exception.

si la mthode M se termine sans exception, l'excution revient au proxy EJB qui termine la transaction en la validant par
un commit. Le flux d'excution est ensuite rendu la mthode appelante de la couche [metier] ;
si la mthode M se termine avec exception, l'excution 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'excution est ensuite rendu la

152/290

mthode appelante de la couche [metier] qui reoit donc une EJBException. L'annotation de la ligne 5 ci-dessus empche
cette encapsulation. La couche [metier] recevra donc une PamException. De plus, l'attribut rollback=true indique au
proxy EJB que lorsqu'il reoit une PamException, il doit invalider la transaction ;

[Link]

Entits srialisables

Revenons sur l'architecture o la couche [UI] est un client distant de la couche [mtier] :

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

Interface
[JPA]

Implmentation
[EclipseLink]

Couche
[JDBC]

OpenEjb

Les couches [ui] et [metier] vont s'changer des objets. Dans la pratique, ces deux couches sont dans deux JVM diffrentes. Si la
couche [ui] veut passer un objet la couche [mtier], elle ne peut pas passer la rfrence de cet objet. La couche [mtier] ne peut en
effet rfrencer des objets qui ne sont pas dans sa JVM. La couche [ui] va alors passer la valeur de l'objet et non sa rfrence. On
appelle cela la srialisation de l'objet. La couche [mtier] va recevoir cette valeur et va crer un nouvel objet partir d'elle. On
appelle cela la dsrialisation de l'objet. La couche [ui] et la couche [mtier] ont alors deux objets identiques mais chacun dans sa
JVM.
Dans notre exemple, les types suivants peuvent tre changs entre les couches [ui] et [metier] : [Employe, Cotisation, Indemnite,
FeuilleSalaire, ElementsSalaire, PamException]. Ces classes doivent pouvoir tre srialises. Cela est obtenu par la simple
dclaration :
1.
2.
3.

public [Classe] extends ... implements Serializable{


..
}

On fera donc en sorte que les classes cites implmentent l'interface [Serializable]. Il n'y a rien d'autre faire pour qu'une classe
puisse tre srialise.

[Link]

Tests de la couche [DAO]

Notre couche [DAO] implmente par des EJB peut tre teste. Nous commenons par copier le package [dao] de [Test Packages]
du projet [mv-pam-springhibernate] dans le projet en cours de construction [1] :

Nous ne conservons que le test [JUnitInitDB] qui initialise la base avec quelques donnes [2]. Nous renommons la classe
[ JUnitInitDbLocal] [3]. La classe [JUnitInitDBLocal] utilisera l'interface locale des EJB de la couche [DAO].
Nous modifions tout d'abord la classe [JUnitInitDBLocal] de la faon suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.

public class JUnitInitDBLocal {


static private IEmployeDaoLocal employeDao = null;
static private ICotisationDaoLocal cotisationDao = null;
static private IIndemniteDaoLocal indemniteDao = null;
@BeforeClass
public static void init() throws Exception {
// on configure le conteneur Open EJB embarqu

153/290

10.
11.
12.
13.
14.
15.
16.
17.
18. }
19. ...

Properties properties = new Properties();


[Link](Context.INITIAL_CONTEXT_FACTORY, "[Link]");
// initialisation du contexte JNDI avec les proprits prcdentes
InitialContext initialContext = new InitialContext(properties);
// instanciation couches DAO locales
employeDao = (IEmployeDaoLocal) [Link]("EmployeDaoLocal");
cotisationDao = (ICotisationDaoLocal) [Link]("CotisationDaoLocal");
indemniteDao = (IIndemniteDaoLocal) [Link]("IndemniteDaoLocal");

lignes 3-5 : des rfrences sur les interfaces locales des EJB de la couche [DAO] ;
ligne 7 : @BeforeClass annote la mthode excute au dmarrage du test JUnit ;
lignes 10-13 : initialisation du conteneur OpenEJB. Cette initialisation est propritaire et change avec chaque conteneur
EJB ;
ligne 13 : on a un contexte JNDI (Java Naming and Directory Interface) qui permet d'accder aux EJB via des noms. Avec
OpenEJB, l'interface locale d'un EJB E est dsigne par ELocal et l'interface distante par ERemote ;
lignes 15-17 : on demande au contexte JNDI, une rfrence sur les interfaces locales des EJB [EmployeDao,
CotisationDao, IndemniteDao].

Avant d'excuter le test [JUnitDBLocal], avec la connexion Netbeans [dbpam_hibernate], supprimez les tables de la base pour
vrifier qu'elles vont bien tre recres puis excutez le test :

On construit le projet (Clean and Build), on lance le serveur MySQL si besoin est, on excute le test JUnitInitDBLocal [2-3]. On
rappelle que le fichier [[Link]] a t configur pour recrer les tables chaque excution.

en [4], le test a t russi ;


en [5], on rafrachit la connexion Netbeans. On voit les tables cres par la couche JPA. Le test avait pour but de les
remplir. On visualise le contenu de l'une d'entre-elles [6] :

154/290

en [7], le contenu de la table [EMPLOYES] ;

Le conteneur OpenEJB a affich des logs dans la console :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.

INFOS - ********************************************************************************
INFOS - OpenEJB [Link]
INFOS - Startup: Fri Sep 02 [Link] CEST 2016
INFOS - Copyright 1999-2015 (C) Apache OpenEJB/TomEE Project, All Rights Reserved.
INFOS - Version: 4.7.4
INFOS - Build date: 20160304
INFOS - Build time: 09:05
INFOS - ********************************************************************************
INFOS - [Link] = D:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejb-eclipselink
INFOS - [Link] = D:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejb-eclipselink
INFOS - Created new singletonService [Link]@668bc3d5
INFOS - Succeeded in installing singleton service
INFOS - openejb configuration file is 'D:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejbeclipselink\conf\[Link]'
INFOS - Configuring Service(id=Default Security Service, type=SecurityService, provider-id=Default Security Service)
INFOS - Configuring Service(id=Default Transaction Manager, type=TransactionManager, provider-id=Default Transaction
Manager)
INFOS - Configuring Service(id=Default JDBC Database, type=Resource, provider-id=Default JDBC Database)
INFOS - Found EjbModule in classpath: d:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejbeclipselink\target\classes
INFOS - Beginning load: d:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejb-eclipselink\target\classes
INFOS - Configuring enterprise application: D:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejbeclipselink\[Link]
INFOS - Auto-deploying ejb EmployeDao: EjbDeployment(deployment-id=EmployeDao)
INFOS - Auto-deploying ejb Metier: EjbDeployment(deployment-id=Metier)
INFOS - Auto-deploying ejb CotisationDao: EjbDeployment(deployment-id=CotisationDao)
INFOS - Auto-deploying ejb IndemniteDao: EjbDeployment(deployment-id=IndemniteDao)
INFOS - Configuring Service(id=Default Stateless Container, type=Container, provider-id=Default Stateless Container)
INFOS - Auto-creating a container for bean EmployeDao: Container(type=STATELESS, id=Default Stateless Container)
INFOS - Configuring PersistenceUnit(name=mv-pam-jpa-eclipselinkPU,
provider=[Link])
INFOS - Auto-creating a Resource with id 'Default JDBC DatabaseNonJta' of type 'DataSource for 'mv-pam-jpa-eclipselinkPU'.
INFOS - Configuring Service(id=Default JDBC DatabaseNonJta, type=Resource, provider-id=Default JDBC Database)
INFOS - Adjusting PersistenceUnit mv-pam-jpa-eclipselinkPU <jta-data-source> to Resource ID 'Default JDBC Database' from
'null'
INFOS - Adjusting PersistenceUnit mv-pam-jpa-eclipselinkPU <non-jta-data-source> to Resource ID 'Default JDBC
DatabaseNonJta' from 'null'
INFOS - Enterprise application "D:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejb-eclipselink\[Link]"
loaded.
INFOS - Creating TransactionManager(id=Default Transaction Manager)
INFOS - Creating SecurityService(id=Default Security Service)
INFOS - Creating Resource(id=Default JDBC Database)
INFOS - Creating Resource(id=Default JDBC DatabaseNonJta)
INFOS - Creating Container(id=Default Stateless Container)
INFOS - Assembling app: D:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejb-eclipselink\[Link]
[EL Fine]: 2016-09-02 [Link].84--Thread(Thread[main,5,main])--Configured server platform:
[Link]
[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].
[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].
[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].
[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].
[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.
[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.
[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.
AVERTISSEMENT - JAVA AGENT NOT INSTALLED. The JPA Persistence Provider requested installation of a ClassFileTransformer
which requires a JavaAgent. See [Link]
[EL Info]: 2016-09-02 [Link].152--ServerSession(1544078442)--Thread(Thread[main,5,main])--EclipseLink, version: Eclipse
Persistence Services - 2.6.3.v20160428-59c81c5
[EL Fine]: 2016-09-02 [Link].481--Thread(Thread[main,5,main])--Detected database platform:
[Link]

155/290

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/istia1617/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
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(deploymentid=EmployeDao)
80. INFOS - Jndi(name=EmployeDaoRemote) --> Ejb(deployment-id=EmployeDao)
81. INFOS - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/EmployeDao![Link]) --> Ejb(deploymentid=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(deploymentid=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(deploymentid=IndemniteDao)
95. INFOS - Jndi(name=IndemniteDaoRemote) --> Ejb(deployment-id=IndemniteDao)
96. INFOS - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/IndemniteDao![Link]) --> Ejb(deploymentid=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)

156/290

107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.

INFOS - Started Ejb(deployment-id=Metier, ejb-name=Metier, container=Default Stateless Container)


INFOS - Started Ejb(deployment-id=IndemniteDao, ejb-name=IndemniteDao, container=Default Stateless Container)
INFOS - Started Ejb(deployment-id=EmployeDao, ejb-name=EmployeDao, container=Default Stateless Container)
INFOS - Started Ejb(deployment-id=CotisationDao, ejb-name=CotisationDao, container=Default Stateless Container)
INFOS - Deployed Application(path=D:\data\istia-1617\netbeans\support-td\chap-05\mv-pam-openejb-eclipselink\[Link])
[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
[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
[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
[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 (?, ?, ?, ?, ?, ?)
bind => [1.93, 2.0, 12.0, 1, 3.0, 1]
[EL Fine]: 2016-09-02 [Link].793--ClientSession(195253450)--Connection(1280263013)--Thread(Thread[main,5,main])--SELECT
LAST_INSERT_ID()
[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 (?, ?, ?, ?, ?, ?)
bind => [2.1, 2.1, 15.0, 2, 3.1, 1]
[EL Fine]: 2016-09-02 [Link].824--ClientSession(2096268257)--Connection(551994588)--Thread(Thread[main,5,main])--SELECT
LAST_INSERT_ID()
[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 (?, ?, ?, ?, ?, ?, ?, ?)
bind => [5 rue des oiseaux, 49203, Jouveinal, Marie, 254104940426058, 1, St Corentin, 2]
[EL Fine]: 2016-09-02 [Link].84--ClientSession(1250816994)--Connection(1033425208)--Thread(Thread[main,5,main])--SELECT
LAST_INSERT_ID()
[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 (?, ?, ?, ?, ?, ?, ?, ?)
bind => [La brlerie, 49014, Laverti, Justine, 260124402111742, 1, St Marcel, 1]
[EL Fine]: 2016-09-02 [Link].84--ClientSession(954936400)--Connection(146799499)--Thread(Thread[main,5,main])--SELECT
LAST_INSERT_ID()
[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 (?, ?, ?, ?, ?)
bind => [6.15, 3.49, 7.88, 9.39, 1]
[EL Fine]: 2016-09-02 [Link].856--ClientSession(956365425)--Connection(645015141)--Thread(Thread[main,5,main])--SELECT
LAST_INSERT_ID()
Employs ---------------------[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
Employ=[id=1, version=1, prnom=Marie, nom=Jouveinal, adresse=5 rue des oiseaux, ville=St Corentin, code postal=49203,
[Link]=null]
Employ=[id=2, version=1, prnom=Justine, nom=Laverti, adresse=La brlerie, ville=St Marcel, code postal=49014,
[Link]=null]
Indemnits ---------------------[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
Indemnite[id=1, version=1, indice=1, base heure=1.93, entretien jour=2.0, repas jour=3.0, indemnits CP=12.0]
Indemnite[id=2, version=1, indice=2, base heure=2.1, entretien jour=2.1, repas jour=3.1, indemnits CP=15.0]
Cotisations ---------------------[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
Cotisations=[id=1, version=1, csgrds=3.49, csgd=6.15, secu=9.39, retraite=7.88]

ligne 16 : la base de donnes est dcouverte ;


lignes 20-23 : les EJB sont dcouverts ;
lignes 26-30 : exploitation du fichier de configuration [[Link]] ;
ligne 32 : cration du gestionnaire de transactions ;
lignes 39-45 : exploitation des entits JPA par EclipseLink ;
lignes 48-67 : les noms JNDI des EJB de l'application. Il est indispensable de connatre ces noms qui ne sont pas standard
d'un conteneur EJB un autre ;
lignes 48-67 : EclipseLink se connecte la base de donnes [dbpam_hibernate] ;
lignes 69-76 : EclipseLink excute la proprit [drop-and-create] trouve 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 : cration et dmarrage des EJB ;
ligne 111 : l'application est dploye. Elle va dsormais tre excute ;
lignes 112-139 : logs d'EclipseLink au cours de l'excution de [JUnitInitDBLocal] et affichages faits par l'application ellemme ;

Les affichages faits par le test [JUnitInitDBLocal] sont les suivants :


1.
2.

Employs ---------------------Employ=[id=1, version=1, prnom=Marie, nom=Jouveinal, adresse=5 rue des oiseaux, ville=St Corentin, code postal=49203,
[Link]=null]

157/290

3.
4.
5.
6.
7.
8.

Employ=[id=2, version=1, prnom=Justine, nom=Laverti, adresse=La brlerie, ville=St Marcel, code postal=49014,
[Link]=null]
Indemnits ---------------------Indemnite[id=1, version=1, indice=1, base heure=1.93, entretien jour=2.0, repas jour=3.0, indemnits CP=12.0]
Indemnite[id=2, version=1, indice=2, base heure=2.1, entretien jour=2.1, repas jour=3.1, indemnits CP=15.0]
Cotisations ---------------------Cotisations=[id=1, version=1, csgrds=3.49, csgd=6.15, secu=9.39, retraite=7.88]

Lignes 2 et 3, on remarquera que le champ [indemniteId] de l'employ ramen en mode LAZY vaut null.
Nous refaisons le mme test, en utilisant cette fois-ci l'interface distante des EJB.

En [1], la classe [JUnitInitDBLocal] a t duplique (copy / paste) dans [JUnitInitDBRemote]. Dans cette classe, nous remplaons
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 proprits prcdentes
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. }

Ceci fait, la nouvelle classe de test peut tre excute. Auparavant, avec la connexion Netbeans [dbpam_hibernate], supprimez les
tables de la base pour vrifier qu'elles vont bien tre recres.

Excutez le test puis avec la connexion Netbeans [dbpam_hibernate], vrifiez 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]. Vrifiez que ces tests passent.

5.2.3

Portage de la couche [metier]

Nous allons faire le portage de la couche [metier] par copie de packages du projet [mv-pam-spring-hibernate] vers le projet [mvpam-openejb-eclipselink].

158/290

Les erreurs signales ci-dessus [1] viennent du fait que la couche [metier] copie utilise Spring et que les bibliothques Spring ne
font plus partie du projet.

[Link]

L'EJB [Metier]

Nous suivons la mme dmarche que celle dcrite pour l'EJB [CotisationDao]. Nous crons tout d'abord en [2] les interfaces locale
et distante du futur EJB [Metier]. Elles drivent toutes deux de l'interface initiale [IMetier].
1.
2.
3.
4.
5.
6.
7.
8.

package metier;

1.
2.
3.
4.
5.
6.
7.
8.

package metier;

import [Link];
@Local
public interface IMetierLocal extends IMetier{
}

import [Link];
@Remote
public interface IMetierRemote extends IMetier{
}

Ceci fait, en [3] nous modifions la classe [Metier] afin qu'elle devienne un EJB :
1.
2.
3.
4.
5.
6.
7.
8.
9.

@Stateless
@TransactionAttribute([Link])
public class Metier implements IMetierLocal, IMetierRemote {

ligne 1 : l'annotation @Stateless fait de la classe un EJB ;


ligne 2 : chaque mthode de la classe s'excutera dans une transaction ;
ligne 3 : l'EJB [Metier] implmente les deux interfaces locale et distante que nous venons de dfinir ;
ligne 7 : l'EJB [Metier] va utiliser l'EJB [CotisationDao] via l'interface locale de celui-ci. Cela signifie que les couches
[metier] et [DAO] doivent s'excuter dans la mme JVM ;
ligne 6 : l'annotation @EJB fait en sorte que le conteneur EJB injecte lui-mme la rfrence sur l'interface locale de l'EJB
[CotisationDao]. L'autre faon que nous avons rencontre est d'utiliser un contexte JNDI ;
lignes 8-9 : le mme mcanisme est utilis pour la version locale de l'EJB [EmployeDao] ;

[Link]

// rfrences sur la couche [dao]


@EJB
private ICotisationDaoLocal cotisationDao = null;
@EJB
private IEmployeDaoLocal employeDao = null;

Tests de la couche [metier]

Notre couche [metier] implmente par un EJB peut tre teste. Nous commenons par copier le package [metier] de [Test
Packages] du projet [mv-pam-spring-hibernate] dans le projet en cours de construction [1] :

159/290

en [1], le rsultat de la copie ;


en [2], on supprime le 1er test ;
en [3], le test restant est renomm [JUnitMetierLocal] ;

La classe [JUnitMetierLocal] devient la suivante :


1. public class JUnitMetierLocal {
2.
3.
// couche mtier
4.
static private IMetierLocal 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 proprits prcdentes
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 mtier locale
20.
metier = (IMetierLocal) [Link]("MetierLocal");
21. ...

ligne 4 : une rfrence sur l'interface locale de l'EJB [Metier] ;


lignes 12-14 : configuration du conteneur OpenEJB identique celle faite dans le test de la couche [DAO] ;
lignes 16-20 : on demande au contexte JNDI de la ligne 14, des rfrences sur les trois EJB de la couche [DAO] et sur
l'EJB de la couche [metier]. Les EJB de la couche [DAO] vont servir initialiser la base, l'EJB de la couche [metier] faire
des tests de calculs de salaire.

L'excution du test [JUnitMetierLocal] donne le rsultat suivant [1] :

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.
2.
3.
4.
5.
6.

public class JUnitMetierRemote {


// couche mtier
static private IMetierRemote metier;
@BeforeClass

160/290

7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.

public static void init() throws NamingException {


// log
log("init");
// on configure le conteneur Open EJB embarqu
Properties properties = new Properties();
[Link](Context.INITIAL_CONTEXT_FACTORY, "[Link]");
// initialisation du contexte JNDI avec les proprits prcdentes
InitialContext initialContext = new InitialContext(properties);
// instanciation couches DAO locales
IEmployeDaoLocal employeDao = (IEmployeDaoLocal) [Link]("EmployeDaoLocal");
ICotisationDaoLocal cotisationDao = (ICotisationDaoLocal) [Link]("CotisationDaoLocal");
IIndemniteDaoLocal indemniteDao = (IIndemniteDaoLocal) [Link]("IndemniteDaoLocal");
// instanciation couche mtier distante
metier = (IMetierRemote) [Link]("MetierRemote");
// on vide la base
...

lignes 4 et 20 : on utilise l'interface distante de l'EJB [Metier] ;


lignes 16-18 : on utilise les interfaces locales de la couche [DAO] ;

Ceci fait, le projet peut tre construit et le test [JUnitMetierRemote] excut :

5.2.4

Portage de la couche [console]

Nous allons faire le portage de la couche [console] par copie de packages du projet [mv-pam-spring-hibernate] vers le projet [mvpam-openejb-eclipselink].

Les erreurs signales ci-dessus [1] viennent du fait que la couche [metier] copie utilise Spring et que les bibliothques Spring ne
font plus partie du projet. En [2], la classe [Main] est renomme [MainLocal]. Elle utilisera l'interface locale de l'EJB [Metier].
Le code de la classe [MainLocal] volue de la faon suivante :
1. public static void main(String[] args) {
2.
// donnes locales
3.
final String syntaxe = "pg num_securite_sociale nb_heures_travailles nb_jours_travaills";
4.
// on vrifie le nombre de paramtres
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

161/290

20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38. ...

FeuilleSalaire feuilleSalaire;
try {
// on configure le conteneur Open EJB embarqu
Properties properties = new Properties();
[Link](Context.INITIAL_CONTEXT_FACTORY, "[Link]");
// initialisation du contexte JNDI avec les proprits prcdentes
InitialContext initialContext = new InitialContext(properties);
// instanciation couche mtier locale
IMetierLocal metier = (IMetierLocal) [Link]("MetierLocal");
// calcul de la feuille de salaire
feuilleSalaire = [Link](args[0], nbHeuresTravailles, nbJoursTravaills);
} catch (PamException ex) {
[Link]([Link]("L'erreur suivante s'est produite : %s", [Link]()));
return;
} catch (Exception ex) {
[Link]([Link]("L'erreur suivante s'est produite : %s", ex));
return;
}

Les modifications se situent dans les lignes 22-28. C'est la faon d'avoir une rfrence sur la couche [metier] qui change (ligne 28).
Nous n'expliquons pas le nouveau code qui a dj t vu dans des exemples prcdents. Une fois ces modifications faites, le projet
ne prsente plus d'erreurs (cf [3]).
Nous configurons le projet pour qu'il soit excut avec des arguments [1-3] :

Pour que l'application console s'excute normalement, il faut qu'il y ait des donnes dans la base. Pour cela, il faut modifier le fichier
[META-INF/[Link]] :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.1"
xmlns="[Link]
xmlns:xsi="[Link]
xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-pam-jpa-eclipselinkPU" transaction-type="JTA">
<provider>[Link]</provider>
<class>[Link]</class>
<class>[Link]</class>
<class>[Link]</class>
<properties>
<!-<property name="[Link]" value="drop-and-create"/>-->
<property name="[Link]" value="FINE"/>
</properties>
</persistence-unit>
</persistence>

La ligne 12 qui faisait que les tables de la base de donnes taient recres chaque excution est mise en commentaires. Le projet
doit tre reconstruit (Clean and Build) pour que cette modification soit prise en compte. Si besoin est, excutez le test [JUnitInitDB]
du projet [mv-pam-openejb-eclipselink] pour remplir les tables. Ceci fait, on peut excuter le programme. Si tout va bien, on obtient
un affichage console analogue au suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.

.......
INFO - Created EJB(deployment-id=IndemniteDao, ejb-name=IndemniteDao, container=Default Stateless Container)
INFO - Deployed Application(path=[Link])
[EL Info]: 2009-09-30 [Link].109--ServerSession(16658781)--EclipseLink, version: Eclipse Persistence Services 1.1.2.v20090612-r4475
[EL Info]: 2009-09-30 [Link].937--ServerSession(16658781)--file:/C:/temp/09-09-28/pam-console-metier-dao-openejbeclipselink-0910/build/classes/-JPA login successful
Valeurs saisies :
N de scurit sociale de l'employ : 254104940426058
Nombre d'heures travailles : 150
Nombre de jours travaills : 20

162/290

10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.

Informations Employ :
Nom : Jouveinal
Prnom : Marie
Adresse : 5 rue des oiseaux
Ville : St Corentin
Code Postal : 49203
Indice : 2
Informations Cotisations :
CSGRDS : 3.49 %
CSGD : 6.15 %
Retraite : 7.88 %
Scurit sociale : 9.39 %
Informations Indemnits :
Salaire horaire : 2.1 euro
Entretien/jour : 2.1 euro
Repas/jour : 3.1 euro
Congs Pays : 15.0 %
Informations Salaire :
Salaire de base : 362.25 euro
Cotisations sociales : 97.48 euro
Indemnits d'entretien : 42.0 euro
Indemnits de repas : 62.0 euro
Salaire net : 368.77 euro
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 duplique dans [MainRemote]. Le code de [MainRemote] est modifi pour utiliser l'interface
distante de la couche [metier] :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19. }

// c'est bon - on peut demander la feuille de salaire


FeuilleSalaire feuilleSalaire;
try {
// on configure le conteneur Open EJB embarqu
Properties properties = new Properties();
[Link](Context.INITIAL_CONTEXT_FACTORY, "[Link]");
// initialisation du contexte JNDI avec les proprits prcdentes
InitialContext initialContext = new InitialContext(properties);
// instanciation couche mtier distante
IMetierRemote metier = (IMetierRemote) [Link]("MetierRemote");
// calcul de la feuille de salaire
feuilleSalaire = [Link](args[0], nbHeuresTravailles, nbJoursTravaills);
} catch (PamException ex) {
[Link]([Link]("L'erreur suivante s'est produite : %s", [Link]()));
return;
} catch (Exception ex) {
[Link]([Link]("L'erreur suivante s'est produite : %s", ex));
return;

La modification est faite ligne 10. Le projet est configur [2] pour excuter la classe [MainRemote]. Son excution donne les mmes
rsultats que prcdemment.

5.2.5

Portage de la couche [swing]

Travail faire : portez dans le projet [mv-pam-openejb-eclipselink] l'application Swing [PamJFrame] du projet [mv-pam-spring-jpahibernate] en une version locale [PamJFrameLocal] et une version distante [PamJFrameRemote].

163/290

5.3

Conclusion

Nous avons montr comment porter une architecture Spring / Hibernate vers une architecture OpenEJB / EclipseLink.
L'architecture Spring / Hibernate

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Interface
[JPA]

Implmentation
[EclipseLink]

Couche
[JDBC]

Spring
L'architecture OpenEJB / EclipseLink

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

OpenEjb
Le portage a pu se faire sans trop de difficults parce que l'application initiale avait t structure en couches. Ce point est
important comprendre.

164/290

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.
L'implmentation actuelle avec OpenEJB / EclipseLink

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

Interface
[JPA]

Implmentation
[EclipseLink]

Couche
[JDBC]

OpenEjb
Ci-dessus, la couche [ui] utilise l'interface distante de la couche [metier].
Nous avons test deux contextes d'excution : local et distant. Dans ce dernier mode, la couche [ui] tait cliente de la couche [metier],
couche implmente par des EJB. Pour fonctionner en mode client / serveur, dans lequel le client et le serveur s'excutent dans
deux JVM diffrentes, nous allons placer les couches [metier, DAO, JPA] sur le serveur Java EE Glassfish. Ce serveur est livr avec
Netbeans.
L'implmentation construire avec le serveur Glassfish

Couche
[ui]

Jvm1 - Java SE

6.1
6.1.1

Couche
[metier]

Couche
[DAO]

Couche
[JPA /
EclipseLink]

Couche
[JDBC]

Jvm2 Java EE - serveur Glassfish

la couche [ui] s'excutera dans un environnement Java SE (Standard Edition) ;


les couches [metier, DAO, JPA] s'excuteront dans un environnement Java EE (Enterprise Edition) sur un serveur
Glassfish ;
le client communiquera avec le serveur via un rseau tcp-ip. Les changes rseau sont transparents pour le dveloppeur, si
ce n'est qu'il doit tre quand mme conscient que le client et le serveur s'changent des objets srialiss pour communiquer
et non des rfrences d'objets. Le protocole rseau utilis pour ces changes s'appelle RMI (Remote Method Invocation),
un protocole utilisable uniquement entre deux applications Java ;
l'implmentation JPA utilise sur le serveur Glassfish sera EclipseLink ;

La partie serveur de l'application client / serveur PAM


L'architecture de l'application

Nous tudions ici la partie serveur qui sera hberge par le conteneur EJB3 du serveur Glassfish :

165/290

Client
Java SE

Conteneur
Ejb3

Jpa / Eclipselink

Donnes

serveur Java EE

Il s'agit de faire un portage vers le serveur Glassfish de ce qui a dj t fait et test avec le conteneur OpenEJB. C'est l l'intrt de
OpenEJB et en gnral des conteneurs EJB embarqus : ils nous permettent de tester l'application dans un environnement
d'excution simplifi. Lorsque l'application a t teste, il ne reste plus qu' la porter sur un serveur cible, ici le serveur Glassfish.

[Link]

Le projet Netbeans

Commenons par crer un nouveau projet Netbeans :

en [1], nouveau projet ;


en [2], choisir la catgorie Maven et en [3] le type EJB Module. Il s'agit en effet de construire un projet qui sera hberg et
excut par un conteneur EJB, celui du 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 ;
en [9], choisir le serveur d'application sur lequel il sera excut. Celui choisi ici est l'un de ceux visibles dans l'onglet
[Runtime / Servers] ;

166/290

en [10], choisir la version de Java EE ;

en [11], le nouveau projet ;

[Link]

Configuration Maven

La configuration Maven du projet gnr par Netbeans est la suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
<modelVersion>4.0.0</modelVersion>
<groupId>[Link]</groupId>
<artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>ejb</packaging>
<name>mv-pam-ejb-metier-dao-eclipselink</name>
<properties>
<[Link]>${[Link]}/endorsed</[Link]>
<[Link]>UTF-8</[Link]>
</properties>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>[Link]</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<compilerArguments>
<endorseddirs>${[Link]}</endorseddirs>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>[Link]</groupId>
<artifactId>maven-ejb-plugin</artifactId>
<version>2.3</version>
<configuration>
<ejbVersion>3.1</ejbVersion>
</configuration>
</plugin>
<plugin>
<groupId>[Link]</groupId>

167/290

50.
<artifactId>maven-dependency-plugin</artifactId>
51.
<version>2.6</version>
52.
<executions>
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 dpendance 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 dpendance [java-ee] ne sera pas incluse dans l'archive jar du projet. A l'excution, elle sera
trouve dans les archives du serveur Glassfish ;
lignes 26-74 : divers plugins Maven ncessaires la gnration de l'archive du projet EJB ;

La configuration Maven prcdente n'inclut pas les plugins ncessaires au weaving des entits JPA / EclipseLink. Nous les rajoutons ;
1. <build>
2.
<plugins>
3.
<!-- gnr 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>

168/290

49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.

[Link]

<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>
[Link]
</groupId>
<artifactId>
staticweave-maven-plugin
</artifactId>
<versionRange>
[1.0.0,)
</versionRange>
<goals>
<goal>weave</goal>
</goals>
</pluginExecutionFilter>
<action>
<execute>
<runOnIncremental>true</runOnIncremental>
</execute>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>

Configuration de la couche de persistance

Par configuration de la couche de persistance, nous entendons l'criture du fichier [[Link]] qui dfinit :

l'implmentation JPA utiliser ;

la dfinition de la source de donnes exploite par la couche JPA. Celle-ci sera une source JDBC gre par le serveur
Glassfish ;

Couche
[ui]

Java SE

Couche
[metier]

Couche
[DAO]

Couche
[JPA /
Eclipselink]

Couche
[JDBC]

SGBD

BD

Java EE - serveur Glassfish

On pourra procder comme suit. Tout d'abord, dans l'onglet [Runtime / Databases], on crera [1] une connexion sur la base
MySQL5 [dbpam_hibernate] :

Ceci fait, on peut passer la cration de la ressource JDBC utilise par le module EJB :

169/290

en [1], crer un nouveau fichier on s'assurera que le projet EJB est slectionn avant de faire cette opration ;
en [2], le projet EJB ;
en [3], on slectionne la catgorie [Glassfish] ;
en [4], on veut crer 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 acclrer les changes de l'application avec la base de donnes ;
en [6], donner un nom JNDI la ressource JDBC cre. Ce nom peut tre quelconque mais il a souvent la forme
jdbc/nom ; Ce nom JNDI sera utilis dans le fichier [[Link]] pour dsigner la source de donnes que
l'implmentation JPA doit utiliser ;
en [7], donner un nom qui peut tre quelconque au pool de connexions qui va tre cr ;
dans la liste droulante [8], choisir la connexion JDBC cre prcdemment sur la base MySQL / dbpam_hibernate ;
en [9], un rcapitulatif des proprits du pool de connexions - on ne touche rien ;

170/290

en [10], on peut prciser plusieurs des proprits du pool de connexions - on laisse ici les valeurs par dfaut ;
en [11], l'issue de l'assistant de cration 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 dplace [13] ;

Le contenu du fichier [[Link]] est le suivant :


1.
2.

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE resources PUBLIC "-//[Link]//DTD GlassFish Application Server 3.1 Resource Definitions//EN"
"[Link]
3. <resources>
4.
<jdbc-resource enabled="true" jndi-name="jdbc/dbpam_hibernate" object-type="user" poolname="dbpamHibernateConnectionPool">
5.
<description/>
6.
</jdbc-resource>
7.
<jdbc-connection-pool allow-non-component-callers="false" associate-with-thread="false" connection-creation-retryattempts="0" connection-creation-retry-interval-in-seconds="10" connection-leak-reclaim="false" connection-leak-timeout-inseconds="0" connection-validation-method="auto-commit" datasource-classname="[Link]"
fail-all-connections="false" idle-timeout-in-seconds="300" is-connection-validation-required="false" is-isolation-levelguaranteed="true" lazy-connection-association="false" lazy-connection-enlistment="false" match-connections="false" maxconnection-usage-count="0" max-pool-size="32" max-wait-time-in-millis="60000" name="dbpamHibernateConnectionPool" nontransactional-connections="false" pool-resize-quantity="2" res-type="[Link]" statement-timeout-in-seconds="1" steady-pool-size="8" validate-atmost-once-period-in-seconds="0" wrap-jdbc-objects="false">
8.
<property name="URL" value="jdbc:mysql://localhost:3306/dbpam_hibernate?zeroDateTimeBehavior=convertToNull"/>
9.
<property name="User" value="root"/>
10.
<property name="Password" value=""/>
11.
</jdbc-connection-pool>
12. </resources>

Le fichier [[Link]] est un fichier XML qui reprend toutes les donnes collectes par l'assistant. Il va tre utilis par
Netbeans pour, lors du dploiement du module EJB sur le serveur Glassfish, demander la cration de la ressource JDBC dont a
besoin ce module.
On peut dsormais crer le fichier [[Link]] qui va configurer la couche JPA du module EJB :

171/290

en [1], crer une nouvelle ressource ;


en [2], le projet EJB ;
en [4], on slectionne la catgorie [Persistence] ;
en [5], on veut crer une unit de persistance ;

en [6], donner un nom l'unit de persistance ;


en [7], plusieurs implmentations JPA sont proposes. On choisira ici [EclipseLink]. D'autres implmentations sont
utilisables condition de mettre les bibliothques qui les implmentent avec celles du serveur Glassfish ;
dans la liste droulante [8], choisir la source de donnes JDBC [jdbc/dbpam_hibernate] qui vient d'tre cre ;
en [9], indiquer que les transactions sont gres par le conteneur EJB ;
en [10], indiquer qu'aucune opration ne doit tre faite sur la source de donnes lors du dploiement du module EJB sur le
serveur. En effet, le module EJB va utiliser une base [dbpam_hibernate] dj cre ;
la fin de l'assistant, un fichier [[Link]] a t cr [11]. Son contenu est le suivant :

1.
2.
3.
4.
5.
6.
7.
8.
9.

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link] xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-pam-ejb-metier-dao-eclipselinkPU" transaction-type="JTA">
<jta-data-source>java:app/jdbc/dbpam_hibernate</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties/>
</persistence-unit>
</persistence>

ligne 3 : le nom de l'unit de persistance [ mv-pam-ejb-metier-dao-eclipselinkPU ] et le type de transactions (JTA pour un


conteneur EJB) ;
ligne 4 : le nom JNDI de la source de donnes utilise par la couche de persistance : [java:app/jdbc/dbpam_hibernate].
Les tests montrent que ce nom n'est pas toujours reconnu. On remplacera la ligne 4 par la suivante :
1.

<jta-data-source>jdbc/dbpam_hibernate</jta-data-source>

172/290

ligne 5 : les entits JPA ne sont pas prcises. Elles seront cherches dans le Classpath du module EJB ;
le nom de l'implmentation JPA (Hibernate, EclipseLink, ...) utilise n'est pas indique. Dans ce cas, Glassfish utilise par
dfaut EclipseLink ;

Nous faisons voluer le fichier [[Link]] de la faon suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link] xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-pam-ejb-metier-dao-eclipselinkPU" transaction-type="JTA">
<jta-data-source>jdbc/dbpam_hibernate</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="[Link]" value="FINE"/>
<!-- weaving -->
<property name="[Link]" value="static"/>
<property name="[Link]" value="true"/>
<property name="[Link]" value="true"/>
</properties>
</persistence-unit>
</persistence>

ligne 7 : nous fixons un niveau de logs EclipseLink ;


lignes 9-11 : nous indiquons que le mode d'enrichissement des entits JPA est fait statiquement la compilation ;

[Link]

Insertion des couches [JPA, DAO, metier]

Maintenant que le fichier [[Link]] a t dfini, nous pouvons passer l'insertion dans le projet, des couches [metier, dao,
JPA] de l'application :

Couche
[ui]

Java SE

Couche
[metier]

Couche
[DAO]

Couche
[JPA /
EclipseLink]

Couche
[JDBC]

SGBD

BD

Java EE - serveur Glassfish

Ces trois couches sont identiques ce qu'elles taient avec OpenEJB. On peut procder un simple copier / coller entre les deux
projets. C'est ce que nous faisons maintenant :

[Link]

en [1], le rsultat 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 ;

Dploiement de l'application sur le serveur Glassfish

Nous dployons maintenant le module EJB sur le serveur Glassfish :

173/290

en [1-3], nous lanons le serveur Glassfish ;

en [4], le module EJB est dploy ;


en [5-6], aprs dploiement, le module EJB apparat dans la branche [Applications] du serveur Glassfish ;

Le dploiement a galement cr la ressource JNDI [jdbc / dbpam_hibernate] [8] ainsi que le pool de connexions
[dbpamHibernateConnectionPool] [9]. On rappelle que ces informations sont prsentes dans le fichier [setup / [Link]] ;

Lors du dploiement, le serveur Glassfish logue dans la console des informations intressantes :
1.

...

174/290

2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.

Infos:
[Link] actually got transformed
Infos:
[Link] actually got transformed
Infos:
[Link] actually got transformed
Infos:
EclipseLink, version: Eclipse Persistence Services - 2.6.1.v20150605-31e8258
Infos:
/file:/D:/data/istia-1617/netbeans/support-td/chap-06/mv-pam-ejb-metier-dao-eclipselink/target/classes/_mv-pamejb-metier-dao-eclipselinkPU login successful
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]]
Infos:
Glassfish-specific (Non-portable) JNDI names for EJB IndemniteDao:
[[Link]#[Link], [Link]]
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]]
Infos:
Glassfish-specific (Non-portable) JNDI names for EJB Metier: [[Link],
[Link]#[Link]]
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]]
Infos:
Glassfish-specific (Non-portable) JNDI names for EJB CotisationDao: [[Link],
[Link]#[Link]]
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]]
Infos:
Glassfish-specific (Non-portable) JNDI names for EJB EmployeDao: [[Link]#[Link],
[Link]]
...

On notera aux lignes

7, 9, 11et 13 les noms portables JNDI des EJB dploys. Java EE 6 a introduit la notion de nom JNDI portable. Cela
dnote un nom JNDI reconnu par tous les serveurs Java EE 6. Avec Java EE 5, les noms JNDI sont spcifiques au serveur
utilis ;

8, 10, 12, 14 : les noms JNDI des EJB dploys sous une forme spcifique Glassfish ;
Ces noms seront utiles l'application console que nous allons crire pour utiliser le module EJB dploy.

6.1.2

Client console - version 1

Maintenant que nous avons dploy la partie serveur de notre application client / serveur, nous en venons tudier la partie client
[1] :

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Java SE

Couche
[JPA /
EclipseLink]

Couche
[JDBC]

SGBD

BD

Java EE - serveur Glassfish

Nous crons un nouveau projet Maven de type [Java Application] nomm [mv-pam-client-ejb-metier-dao-eclipselink] :

en [1], le projet du client ;

Le client console va utiliser :

la couche [metier] distante ;

les entits JPA [Employe, Indemnite, Cotisation] ;


Pour qu'il ait accs la dfinition de ces lments, nous crons une dpendance vers le projet EJB :

175/290

4
5
6

en [1-3], on ajoute une dpendance ;


en [4], on slectionne l'onglet [Open Projects] ;
en [5], on dsigne le projet EJB. Il faut que celui-ci soit charg dans Netbeans pour apparatre ;

Le fichier [[Link]] est alors le suivant :


1.
2.

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
<modelVersion>4.0.0</modelVersion>
<groupId>[Link]</groupId>
<artifactId>mv-pam-client-ejb-metier-dao-eclipselink</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!-- projet EJB -->
<dependency>
<groupId>${[Link]}</groupId>
<artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
<version>${[Link]}</version>
</dependency>
</dependencies>

3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
<properties>
18.
<[Link]>UTF-8</[Link]>
19.
<[Link]>1.8</[Link]>
20.
<[Link]>1.8</[Link]>
21.
</properties>
22. </project>

lignes 10-14 : la dpendance sur le projet Maven du module EJB. Nous voulons rcuprer ici les dfinitions des entits JPA
et celles des diffrentes interfaces ainsi que celle de la classe d'exception [PamException] ;

A partir du projet [mv-pam-openejb-eclipselink], nous copions maintenant la classe [MainRemote] :

Si nous compilons Clean and Build) le projet client, nous obtenons des erreurs :
1.
2.
3.
4.
5.
6.
7.
8.

COMPILATION ERROR :
------------------------------------------------------------ui/console/[Link]:[76,25] cannot access [Link]
class file for [Link] not found
ui/console/[Link]:[77,25] cannot access [Link]
class file for [Link] not found
ui/console/[Link]:[78,25] cannot access [Link]
class file for [Link] not found

176/290

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

Maintenant, le [Clean and Build] passe.


La classe [MainRemote] doit obtenir une rfrence sur l'EJB de la couche [metier]. Le code de la classe [MainRemote] volue de la
faon suivante :
1.
2.
3.
4.
5.
6.
7.
8.

...

// c'est bon - on peut demander la feuille de salaire


FeuilleSalaire feuilleSalaire;
try {
// contexte JNDI du serveur Glassfish
InitialContext initialContext = new InitialContext();
// instanciation couche mtier
IMetierRemote metier = (IMetierRemote) [Link]("java:global/mv-pam-ejb-metier-dao-eclipselink/Metier!
[Link]");
// calcul de la feuille de salaire
feuilleSalaire = [Link](args[0], nbHeuresTravailles, nbJoursTravaills);
} catch (PamException ex) {
[Link]([Link]("L'erreur suivante s'est produite : %s", [Link]()));
return;
} catch (Exception ex) {
[Link]([Link]("L'erreur suivante s'est produite : %s", ex));
return;
}

9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19. ..

ligne 6 : initialisation du contexte JNDI du serveur Glassfish ;


ligne 8 : on demande ce contexte JNDI une rfrence sur l'interface distante de la couche [metier]. D'aprs les logs de
Glassfish, on sait que l'interface distante de la couche [metier] a deux noms possibles :

1.

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]]
Infos:
Glassfish-specific (Non-portable) JNDI names for EJB Metier: [[Link],
[Link]#[Link]]

2.

Ligne 1, le nom JNDI utilisable avec tout serveur d'applications JAVA EE 6. Ligne 2, le nom JNDI spcifique Glassfish.
Dans le code, ligne 8, nous utilisons le nom JNDI portable ;

le reste du code ne change pas ;

Nous configurons ensuite la configuration d'excution du projet client [1-4] :

177/290

En [3], on configure le projet pour qu'il excute la classe [MainRemote] avec les arguments [4].
Lorsqu'on excute le projet [5], on obtient l'erreur suivante :
L'erreur suivante s'est produite : [Link]: Need to specify class name in environment or system
property, or as an applet parameter, or in an application resource file: [Link]

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 faon suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.

public static void main(String[] args) throws NamingException {


..
// c'est bon - on peut demander la feuille de salaire
FeuilleSalaire feuilleSalaire;
//
try {
// contexte JNDI du serveur Glassfish
InitialContext initialContext = new InitialContext();
// instanciation couche mtier
IMetierRemote metier = (IMetierRemote) [Link]("java:global/mv-pam-ejb-metier-dao-eclipselink/Metier!
[Link]");
// calcul de la feuille de salaire
feuilleSalaire = [Link](args[0], nbHeuresTravailles, nbJoursTravaills);
//
} catch (PamException ex) {
//
[Link]([Link]("L'erreur suivante s'est produite : %s", [Link]()));
//
return;
//
} catch (Exception ex) {
//
[Link]([Link]("L'erreur suivante s'est produite : %s", ex));
//
return;
//
}
// affichage dtaill
...

lignes 12-18 : on n'intercepte plus l'ventuelle exception ;

178/290

pour cette raison, ligne 1, l'exception non gre doit faire l'objet d'un [throws] ;

L'excution du projet donne alors le rsultat 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]
at [Link]([Link])
at [Link]([Link])
at [Link]([Link])
at [Link]([Link])
at [Link]([Link])

2.
3.
4.
5.
6.

Ligne 6, on a le n de la ligne errone. C'est la ligne n 2 suivante :


1.
2.

// instanciation couche mtier


IMetierRemote metier = (IMetierRemote) [Link]("java:global/mv-pam-ejb-metier-dao-eclipselink/Metier!
[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 accs aux EJB via leurs
noms JNDI. Pour obtenir ce contexte JNDI, il faut ajouter une dpendance ncessaire 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 excute le projet de nouveau. Cette fois-ci, l'ajout de la dpendance 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]:ejbcontainer: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 comprhensible. Son origine est tonnante : une dpendance EclipseLink ncessite par la nouvelle
dpendance n'a pas t trouve. Elle a t cherche dans le dpt [[Link] Il est
possible de spcifier explicitement les dpts qui doivent tre explors lorsque Maven cherche des dpendances. C'est ce que nous
faisons maintenant. Le fichier [[Link]] volue de la faon 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>

179/290

20.
</dependency>
21.
</dependencies>
22.
23.
<!-- repositories -->
24.
<repositories>
25.
<repository>
26.
<url>[Link]
27.
<id>eclipselink</id>
28.
<layout>default</layout>
29.
<name>Repository for library Library[eclipselink]</name>
30.
</repository>
31.
</repositories>
32. ...

Aux lignes 25-30, nous avons dsign un dpt supplmentaire o Maven doit chercher les dpendances. Le dpt ajout est celui
d'EclipseLink. Cette mthode est utiliser lorsqu'on veut par exemple tlcharger des versions beta de dpendances. Celles-ci ne se
trouvent pas sur le dpt central de Maven mais dans des dpts spcialiss.
Cette fois-ci, l'excution du projet ne plante pas (mais le temps de rponse du serveur EJB peut tre particulirement long) et donne
le rsultats suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.

run:
Valeurs saisies :
N de scurit sociale de l'employ : 254104940426058
Nombre d'heures travailles : 150
Nombre de jours travaills : 20
Informations Employ :
Nom : Jouveinal
Prnom : Marie
Adresse : 5 rue des oiseaux
Ville : St Corentin
Code Postal : 49203
Indice : 2
Informations Cotisations :
CSGRDS : 3.49 %
CSGD : 6.15 %
Retraite : 7.88 %
Scurit sociale : 9.39 %
Informations Indemnits :
Salaire horaire : 2.1 euro
Entretien/jour : 2.1 euro
Repas/jour : 3.1 euro
Congs Pays : 15.0 %
Informations Salaire :
Salaire de base : 362.25 euro
Cotisations sociales : 97.48 euro
Indemnits d'entretien : 42.0 euro
Indemnits de repas : 62.0 euro
Salaire net : 368.77 euro
BUILD SUCCESSFUL (total time: 2 seconds)

Si dans les proprits, on met un n de scurit sociale incorrect, on obtient le rsultat suivant :
1.
2.
3.

run:
L'erreur suivante s'est produite : L'employ de n[254104940426058x] est introuvable
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 srialis. Vrifiez les points suivants :

les entits changes entre le client et le serveur [Employe, Cotisation, Indemnite, FeuilleSalaire, ElementsSalaire,
PamException] doivent implmenter l'interface [Serializable] ;

l'entit JPA [Indemnite] ne doit pas voir l'annotation [@OneToMany]. Si elle est prsente, enlevez-la ainsi que le champ
qu'elle annote ;

si les points prcdents sont remplis, alors regardez les logs de Glassfish. La cause la plus probable est qu'une exception
non srialisable s'est produite ct serveur. Lorsque le serveur veut l'envoyer au client, une exception de srialisation se
produit alors.

6.1.3

Client console - version 2

Dans les versions prcdentes, l'environnement JNDI du serveur Glassfish tait configur partir d'un fichier [[Link]]
trouv dans les nombreuses dpendances .jar du projet. Son contenu par dfaut est le suivant :

180/290

1.
2.
3.
4.
5.
6.
7.
8.

# accs JNDI Sun Application Server


[Link]=[Link]
[Link]=[Link]
# Required to add a [Link] for CosNaming that
# supports dynamic RMI-IIOP.
[Link]=[Link]
[Link]=localhost
[Link]=3700

Les lignes 7 et 8 dsignent 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 paramtres, on peut
construire son propre fichier [[Link]] ou utiliser une configuration Spring. Nous montrons cette deuxime technnique.
Nous commenons par crer un nouveau projet partir du projet [pam-client-metier-dao-jpa-eclipselink] initial.

en [1], le nouveau projet ;

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 crer un fichier de configuration Spring qui doit tre dans le Classpath du projet. Pour crer la branche
[Other Sources] on peut procder de la faon suivante :

en [3], utilisez le nom anglais [resources] avec un seul s ;

Le rsultat obtenu est le suivant :

La branche [Other Sources] a t cre.


Nous construisons mainteant le fichier [spring-config-client] qui va configurer Spring [1-8] :

181/290

en [9], le fichier [[Link]] de configuration de Spring. Son contenu est le suivant :

1.
2.
3.
4.
5.
6.

<?xml version="1.0" encoding="UTF-8"?>


<beans xmlns="[Link]
xmlns:xsi="[Link]
xmlns:jee="[Link]
xsi:schemaLocation="[Link] [Link]
[Link] [Link]
">

7.
8.
9.
10. </beans>

Nous lui ajoutons le contenu 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="
7.
[Link]
8.
[Link]
9.
[Link]
10.
[Link]
11. ">

182/290

12.
13.
<!-- mtier -->
14.
<jee:jndi-lookup id="metier" jndi-name="java:global/mv-pam-ejb-metier-dao-eclipselink/Metier![Link]">
15.
<jee:environment>
16.
[Link]=[Link]
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 ncessite la dfinition du schma
auquel elle appartient, lignes 4, 9 et 10.

ligne 14 : la balise <jee:jndi-lookup> permet d'obtenir la rfrence d'un objet auprs d'un service JNDI. Ici, on associe le
bean nomm " metier " la ressource JNDI associe 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 dfinir les
paramtres de connexion au service JNDI ;

Pour que le fichier [[Link]] puisse tre exploit, il faut ajouter la dpendance 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 dpendance sur [spring-context] qui ramne elle-mme d'autres dpendances qui sont automatiquement
charges ;

La classe principale [MainRemote] volue de la faon suivante :


1. ...
2.
// c'est bon - on peut demander la feuille de salaire
3.
FeuilleSalaire feuilleSalaire;
4.
try {
5.
// instanciation couche [metier]
6.
ApplicationContext ctx = new ClassPathXmlApplicationContext("[Link]");
7.
IMetier metier = (IMetier) [Link]("metier");
8.
// calcul de la feuille de salaire
9.
feuilleSalaire = [Link](args[0], nbHeuresTravailles, nbJoursTravaills);
10.
} catch (PamException ex) {
11.
[Link]([Link]("L'erreur suivante s'est produite : %s", [Link]()));
12.
return;
13.
} catch (Exception ex) {
14.
[Link]([Link]("L'erreur suivante s'est produite : %s", ex));
15.
return;
16.
}
17. ...

Lignes 6-7, la rfrence de type [IMetier] sur la couche [metier] est demande 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 rfrence sur la couche [metier].

183/290

L'interface [IMetier] est la classe parent des interfaces [IMetierLocal] et [IMetierRemote]. Cette solution amne de la souplesse dans
notre architecture. En effet, si l'EJB de la couche [metier] devenait local, c.a.d. excut dans la mme 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 tudie au paragraphe 4.10.
Le lecteur est invit tester cette nouvelle version.

6.1.4

Le client Swing

Nous construisons maintenant le client swing de notre application client / serveur EJB.

Le fichier [[Link]] doit avoir la dpendance ncessaire aux applications swing :


1.
2.
3.
4.
5.

<dependency>
<groupId>[Link]</groupId>
<artifactId>swing-layout</artifactId>
<version>1.0.3</version>
</dependency>

Ci-dessus, la classe [PamJFrame] avait t crite initialement pour s'excuter dans un environnement Spring / JPA :

Couche
[ui]
swing

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Spring

Maintenant cette classe doit devenir le client distant d'un EJB dploy sur le serveur Glassfish.

Couche
[ui]
swing

Jvm1 - Java SE

Couche
[metier]

Couche
[DAO]

Couche
[JPA /
EclipseLink]

Couche
[JDBC]

Jvm2 Java EE - serveur Glassfish v3

Travail faire : en suivant l'exemple du client console [[Link]] du projet, modifier la faon utilise par la mthode
[doMyInit] (cf paragraphe 4.11.4, page 134) de la classe [PamJFrame] pour acqurir une rfrence sur la couche [metier] qui est
maintenant distante.

6.1.5

Portage des tests unitaires

Nous construisons maintenant les classes qui vont tester les couches [metier] et [DAO] distantes :

184/290

Les tests unitaires avait t crits initialement pour s'excuter dans un environnement Spring / JPA :

Couche
[tests]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Spring

Maintenant les classes de test doivent devenir des clients distants des EJB dploys sur le serveur Glassfish.

Couche
[tests]

Jvm1 - Java SE

Couche
[metier]

Couche
[DAO]

Couche
[JPA /
EclipseLink]

Couche
[JDBC]

Jvm2 Java EE - serveur Glassfish v3

Travail faire : en suivant l'exemple du client console [[Link]] du projet, modifiez la faon utilise par la mthode
[init] des diffrents tests pour acqurir une rfrence sur les couches [DAO] et [metier] qui sont maintenant distantes, puis excutez
les tests.
Note 1 : Le fichier [[Link]] sera le suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.

<?xml version="1.0" encoding="UTF-8"?>


<beans xmlns="[Link]
xmlns:xsi="[Link]
xmlns:jee="[Link]
xsi:schemaLocation="[Link] [Link]
[Link] [Link]
">
<!-- mtier -->
<jee:jndi-lookup id="metier" jndi-name="java:global/mv-pam-ejb-metier-dao-eclipselink/Metier![Link]">
<jee:environment>
[Link]=[Link]
[Link]=[Link]
[Link]=[Link]
[Link]=localhost
[Link]=3700
</jee:environment>
</jee:jndi-lookup>
<!-- cotisationDao -->

185/290

22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.

<jee:jndi-lookup id="cotisationDao" jndi-name="java:global/mv-pam-ejb-metier-dao-eclipselink/CotisationDao!


[Link]">
<jee:environment>
[Link]=[Link]
[Link]=[Link]
[Link]=[Link]
[Link]=localhost
[Link]=3700
</jee:environment>
</jee:jndi-lookup>
<!-- employeDao -->
<jee:jndi-lookup id="employeDao" jndi-name="java:global/mv-pam-ejb-metier-dao-eclipselink/EmployeDao!
[Link]">
<jee:environment>
[Link]=[Link]
[Link]=[Link]
[Link]=[Link]
[Link]=localhost
[Link]=3700
</jee:environment>
</jee:jndi-lookup>
<!-- indemniteDao -->
<jee:jndi-lookup id="indemniteDao" jndi-name="java:global/mv-pam-ejb-metier-dao-eclipselink/IndemniteDao!
[Link]">
<jee:environment>
[Link]=[Link]
[Link]=[Link]
[Link]=[Link]
[Link]=localhost
[Link]=3700
</jee:environment>
</jee:jndi-lookup>

45.
46.
47.
48.
49.
50.
51.
52.
53.
54. </beans>

Note 2 :
A l'excution initiale de [JunitDaoRemote] tous les tests ont chou. Il en a t de mme pour les autres tests. L'erreur se passait
ct serveur. Il tait indiqu que la colonne [EMPLOYES.INDEMNITE_ID] ne pouvait avoir la valeur NULL. Aprs avoir ttonn
un bon moment la recherche de la cause de cette erreur, il s'est avr que l'erreur provenait de la dfinition suivante trouve dans
l'entit JPA [Employe] :
1.
2.
3.

// cl trangre
@Column(name = "INDEMNITE_ID", updatable = false, insertable = false, nullable = false)
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
problme 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 donnes on garde bien l'attribut [NOT
NULL] de la colonne [EMPLOYES.INDEMNITES_ID]. Il y a ici une incohrence entre l'attribut du champ [indemniteId]
(nullable=true par dfaut) de l'entit [Employe] et celui (NOT NULL) de la colonne correspondante
[EMPLOYES.INDEMNITES_ID] dans la base de donnes.
On retrouve de nouveau la difficult d'avoir des configurations 100% portables d'un environnement un autre. Cette erreur
corrige, tous les tests passent.
Note 3 :
On peut se demander, si lorsqu'on demande au serveur la liste des employs, ceux-ci sont envoys avec ou sans leurs indemnits.
Ct serveur, l'entit JPA [Employe] a un champ [Indemnite indemnite] avec l'attribut [LAZY]. Donc lorsqu'on demande les
employs, on ne devrait pas avoir leurs indemnits. On peut vrifier ce point, en excutant le test [JUnitInitDB] en mode dbogage :

186/290

on fixe un point d'arrt en [1] ci-dessus afin de pouvoir examiner la variable [employes] de la ligne 43 [2] ;

lorsqu'on examine dans la liste [employes], l'lment [Jouveinal] [3], on voit que son champ [Indemnite indemnite] vaut
null, ce qui montre que l'attribut [LAZY] de ce champ a t observ ;

187/290

7 Version 4 client / serveur dans une architecture de service web SOAP


Dans cette nouvelle version, l'application [Pam] va s'excuter en mode client / serveur dans une architecture de service web.
Revenons sur l'architecture de l'application prcdente :

Couche
[ui]

RMI

S
2

Couche
[metier]

Couche
[DAO]

Couche
[JPA /
EclipseLink]

Couche
[JDBC]

SGBD

BD

Java EE - serveur Glassfish

Java SE

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 remplace par une
couche [C, HTTP / SOAP, S] :

Couche
[ui]

S
2

Java SE

HTTP /
SOAP

Couche
[metier]

Couche
[DAO]

Couche
[JPA /
EclipseLink]

Couche
[JDBC]

SGBD

BD

Java EE - serveur Glassfish

Le protocole HTTP / SOAP a l'avantage sur le protocole RMI / EJB prcdent d'tre multi-plateformes. Ainsi le service web peut
tre crit en Java et dploy sur le serveur Glassfish. Le client lui, pourrait tre un client .NET ou PHP.
Nous allons dvelopper cette architecture selon trois modes diffrents :
1.
2.
3.

le service web sera assur par l'EJB [Metier] ;


le service web sera assur par une application web utilisant l'EJB [Metier] ;
le service web sera assur par une application web utilisant Spring ;

Un service web peut tre implment de diverses faons au sein d'un serveur Java EE :

par une classe annote @WebService qui s'excute dans un conteneur web :

Client
du
service web tcp-ip

Conteneur web

Conteneur
Ejb3

Jpa

Donnes

serveur Java EE

par un EJB annot @WebService qui s'excute dans un conteneur EJB

Client
du
service web

Conteneur
Ejb3

Jpa

Donnes

serveur Java EE

Nous commenons par cette dernire architecture.

188/290

7.1
7.1.1

Service web SOAP implment par un EJB


La partie serveur

Commenons par crer un nouveau projet maven copie du projet EJB [mv-pam-ejb-metier-dao-jpa-eclipselink] :

Dans l'architecture suivante :

Couche
[ui]

S
2

Java SE

HTTP /
SOAP

Couche
[metier]

Couche
[DAO]

Couche
[JPA /
EclipseLink]

Couche
[JDBC]

SGBD

BD

Java EE - serveur Glassfish

la couche [metier] va tre le service web contact par la couche [ui]. Cette classe n'a pas besoin d'implmenter une interface. Ce sont
des annotations qui transforment un POJO (Plain Ordinary Java Object) en service web. La classe [Metier] qui implmente la
couche [metier] ci-dessus, est transforme de la faon suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.

package metier;

ligne 4, l'annotation @WebService fait de la classe [Metier] un service web. Un service web expose des mthodes ses
clients. Celles-ci peuvent tre annotes par l'attribut @WebMethod, mais c'est facultatif. Par dfaut, les mthodes
publiques d'une classe annote par [@WebService] deviennent automatiquement des mthodes du service web ;
lignes 16 et 22 : les deux mthodes de la classe [Metier] deviennent des mthodes du service web ;
ligne 26 : il est important que les getters et setters soient supprims sinon ils seront exposs dans le service web et cela
cause des erreurs de scurit ;

...
@WebService
@Stateless()
@TransactionAttribute([Link])
public class Metier implements IMetierLocal,IMetierRemote {
// rfrences sur les couches [DAO]
@EJB
private ICotisationDaoLocal cotisationDao = null;
@EJB
private IEmployeDaoLocal employeDao=null;
// obtenir la feuille de salaire
@WebMethod
public FeuilleSalaire calculerFeuilleSalaire(String SS,
...
}
// liste des employs
@WebMethod
public List<Employe> findAllEmployes() {
...
}
// important - pas de getters et setters pour les EJB qui deviennent des services web
}

L'ajout de ces annotations est dtect par Netbeans qui fait alors voluer la nature du projet :

189/290

En [1], une arborescence [Web Services] est apparue dans le projet. On y trouve le service web MetierService et ses deux
mthodes. L'application serveur peut tre dploye [3]. Le serveur MySQL soit tre lanc et sa base [dbpam_hibernate] exister et
tre remplie. Il peut tre ncessaire auparavant de supprimer [2] les EJB du projet client / serveur EJB tudi prcdemment pour
viter des conflits de noms. En effet, notre nouveau projet amne avec lui les mmes EJB que ceux du projet prcdent.

En [1], nous voyons notre application serveur dploye sur le serveur Glassfish. Une fois le service web dploy, il peut tre test :

en [1], dans le projet courant, nous testons le service web [MetierService] ;


le service web est accessible via diffrentes URL. L'URL [2] permet de tester le service web ;
en [3], un lien sur le fichier XML dfinissant le service web. Les clients du service web ont besoin de connatre l'URL de ce
fichier. C'est partir de lui qu'est gnre la couche cliente (stubs) du service web ;

190/290

en [4,5], un formulaire permettant de tester les mthodes exposes par le service web. Celles-ci sont prsentes avec leurs
paramtres que l'utilisateur peut dfinir ;

Par exemple, testons la mthode [findAllEmployes] qui n'a besoin d'aucun paramtre :

Ci-dessus, nous testons la mthode. Nous recevons alors la rponse ci-dessous (vue partielle). On remarque qu'on obtient les
employs avec leurs indemnits, 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 rponse SOAP, le service web a accd la mthode [[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 mme faon la mthode [4] en lui passant les trois paramtres qu'elle attend.

191/290

Note : si vous n'obtenez pas le rsultat ci-dessus mais que vous n'avez pas d'exception, vrifiez que toutes les entits changes
entre le client et le serveur [Employe, Cotisation, Indemnite, FeuilleSalaire, ElementsSalaire, PamException] ont des setters
publics.

7.1.2

La partie cliente

Couche
[ui]

Java SE
[Link]

C
1

RMI

S
2

Couche
[metier]

Couche
[DAO]

Couche
[JPA /
EclipseLink]

Couche
[JDBC]

SGBD

BD

Java EE - serveur Glassfish

Le projet Netbeans du client console

Nous crons maintenant un projet Maven de type [Java Application] pour la partie client de l'application [1]. Une fois le projet cr,
nous indiquons qu'il sera client du service web que nous venons de dployer sur le serveur Glassfish :

en [2], nous slectionnons le nouveau projet et activons le bouton [New File] ;


en [3], nous indiquons que nous voulons crer un client de service web ;

avec [6], nous allons dsigner le projet Netbeans du service web ;


dans la fentre [7], sont lists tous les projets ayant une branche [Web Services], ici uniquement le projet [mv-pam-wsmetier-dao-eclipselink] ;
un projet peut dployer plusieurs services web. En [8], nous dsignons le service web auquel on veut se connecter ;

192/290

en [9], est affiche l'URL de dfinition du service web. Cette URL est utilise par les outils logiciels qui gnrent la couche
cliente qui va s'interfacer avec le service web ;

Couche
[ui]
3

S
2

Java SE

C
HTTP /
SOAP

Couche
[metier]
4

Couche
[DAO]

Couche
[JPA /
EclipseLink]

Couche
[JDBC]

SGBD

BD

Java EE - serveur Glassfish

la couche cliente [C] [1] qui va tre gnre est constitue d'un ensemble de classes Java qui vont tre mises dans un mme
paquetage. Le nom de celui-ci est fix en [10] ;
une fois l'assistant de cration du client du service web termin avec le bouton [Finish], la couche [C] ci-dessus est cre ;

Ceci est reflt par un certain nombre de changements dans le projet :

en [12] ci-dessus, apparat 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], apparat une arborescence [Web Service References] qui liste les services web pour lesquels une couche cliente a
t gnre ;

On notera que dans la couche [C] [12] gnre, nous retrouvons des classes qui ont t dployes ct serveur : Indemnite,
Cotisation, Employe, FeuilleSalaire, ElementsSalaire, Metier. Metier est le service web et les autres classes sont des classes
ncessaires ce service. On pourra avoir la curiosit de consulter leur code. On verra que la dfinition des classes qui, instancies,
reprsentent des objets manipuls par le service, consiste en la dfinition des champs de la classe et de leurs accesseurs ainsi qu'
l'ajout d'annotations permettant la srialisation de la classe en flux XML. La classe Metier est devenue une interface avec dedans
les deux mthodes qui ont t annotes @WebMethod. Chacune de celles-ci donne naissance deux classes, par exemple
[[Link]] et [[Link]], o l'une encapsule l'appel la mthode et l'autre son rsultat.
Enfin, la classe MetierService est la classe qui permet au client d'avoir une rfrence sur le service web Metier distant :
1.
2.
3.
4.

@WebEndpoint(name = "MetierPort")
public Metier getMetierPort() {
return [Link](new QName("[Link] "MetierPort"), [Link]);

La mthode getMetierPort de la ligne 2 permet d'obtenir une rfrence sur le service web Metier distant.

[Link]

Le client console du service web Metier

Il ne nous reste plus qu' crire le client du service web Metier. Nous recopions la classe [MainRemote] du projet [mv-pam-clientmetier-dao-jpa-eclipselink-2] qui tait un client distant d'un serveur EJB, dans le nouveau projet.

193/290

en [1], la classe du client du service web. La classe [MainRemote] prsente des erreurs. Pour les corriger, on commencera
par supprimer toutes les instructions [import] existantes dans la classe et on les rgnerera par l'option [Fix Imports]. En
effet, certaines des classes utilises par la classe [MainRemote] font dsormais partie du package [client] gnr ;
en [2] ci-dessous, le morceau de code o la couche [metier] est instancie. Elle l'est avec du code Spring pour obtenir une
rfrence sur un EJB distant ;

Nous faisons voluer le code de la faon suivante :

le code Spring est supprim ;


la classe [PamException] n'existant pas ct client, nous supprimons le catch associ pour ne garder que le catch sur la classe
mre [Exception] ;

en [3], il nous reste obtenir une rfrence sur la service web distant [Metier] afin de pouvoir appeler sa mthode
[calculerFeuilleSalaire] ;

194/290

en [4], avec la souris, nous tirons (drag) la mthode [calculerFeuilleSalaire] du service web [Metier] pour la dposer (drop)
en [4]. Du code est gnr [6]. Ce code gnrique peut tre ensuite adapt par le dveloppeur ;
tout d'abord, le code gnr en [5] est erron parce que les classes gnres dans une tape prcdente sont dans le
package [client] et non [metier] (lignes 56, 57) ;
ligne 63, on voit que [calculerFeuilleSalaire] est une mthode de la classe [[Link]().getMetierPort()] (lignes 5657). Maintenant que nous savons comment obtenir la couche [metier], le code prcdent peut tre rcrit de la faon
suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11. }

// c'est bon - on peut demander la feuille de salaire


FeuilleSalaire feuilleSalaire;
// couche [metier]
[Link] metier = new [Link]().getMetierPort();
try {
// calcul de la feuille de salaire
feuilleSalaire = [Link](args[0], nbHeuresTravailles, nbJoursTravaills);
} catch (Exception ex) {
[Link]([Link]("L'erreur suivante s'est produite : %s", ex));
return;

La ligne 4 rcupre une rfrence sur le client local du service web MetierService. Ceci fait, le code de la classe ne change pas.
Nous sommes prts pour les tests :

s'assurer que le SGBD MySQL5 est lanc, que la base dbpam_eclipselink est cre et initialise ;
s'assurer que le service web est dploy sur le serveur Glassfish ;
construire le client (Clean and Build) ;
configurer l'excution du projet client ;

excuter le client ;

L'excution du client est ici beaucoup plus rapide que ce qu'elle tait pour le client du serveur EJB. Les rsultats dans la console
sont les suivants :

195/290

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.

...
Valeurs saisies :
N de scurit sociale de l'employ : 254104940426058
Nombre d'heures travailles : 150
Nombre de jours travaills : 20
Informations Employ :
Nom : Jouveinal
Prnom : Marie
Adresse : 5 rue des oiseaux
...

Avec la configuration suivante :

on obtient les rsultats suivants :


1.

[Link]

L'erreur suivante s'est produite : [Link]: Client received SOAP Fault from
server: L'employ de n[254104940426058x] est introuvable Please see the server log to find more detail regarding exact
cause of the failure.

Le client swing du service web Metier

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 dploy sur le serveur Glassfish.

7.2

Service web SOAP implment par une application web

Nous nous plaons maintenant dans le cadre de l'architecture suivante :

Client
du
service web tcp-ip

Conteneur web

Conteneur
EJB3

Jpa

Donnes

serveur Java EE

Le service web est assur par une application web excute au sein du conteneur web du serveur Glassfish. Ce service web va
s'appuyer sur l'EJB [Metier] dploy lui dans le conteneur EJB3.

7.2.1

La partie serveur

Nous crons une application web :

en [1], nous crons un nouveau projet ;


en [2], ce projet est de type [Web Application] ;
en [3], nous lui donnons le nom [mv-pam-ws-ejb-metier-dao-eclipselink] ;

196/290

en [4], nous choisissons le serveur Glassfish et la version Java EE 7 Web ;


en [5], le projet cr ;

Sur le schma ci-dessous, l'application web cre va s'excuter dans le conteneur web. Elle va utiliser l'EJB [Metier] qui lui, sera
dploy dans le conteneur EJB du serveur.

Client
du
service web tcp-ip

Conteneur web

Conteneur
EJB3

Jpa

Donnes

serveur Java EE

Pour que l'application web cre ait accs aux classes associes l'EJB [Metier], nous ajoutons aux bibliothques de l'application
web [mv-pam-ws-ejb-metier-dao-eclipselink], la dpendance du serveur EJB [mv-pam-ejb-metier-dao-eclipselink] dj tudi.

en [1], on ajoute un projet aux dpendances du projet web,


en [2], on slectionne le projet [mv-pam-ejb-metier-dao-eclipselink],
en [3], la porte de la dpendance est provided, c'est dire qu'elle sera fournie par l'environnement d'excution,
en [4], la dpendance a t ajoute.

Pour crer le mme service web que prcdemment, il nous faut :

crer une classe tague @Webservice ;


avec deux mthodes calculerFeuilleSalaire et findAllEmployes ;

Nous crons une classe [PamWsEjbMetier] dans un package [[Link]] :

197/290

La classe [PamWsEjbMetier] est la suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.

package [Link];

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 dpendances du projet ;
ligne 11 : la classe est un service web qui va par dfaut exposer les mthodes des lignes 18 et 23 ;
ligne 12 : elle implmente l'interface IMetier dfinie dans le module EJB ;
lignes 14-15 : l'interface locale de l'EJB [Metier] est injecte dans le champ de la ligne 15. Nous utilisons l'interface locale
car l'application web et le module EJB s'excutent dans la mme JVM ;
lignes 18 et 23 : les mthodes calculerFeuilleSalaire et findAllEmployes dlguent leur traitement aux mthodes de mme nom
de l'EJB [Metier]. La classe ne sert donc qu' exposer des clients distants les mthodes de l'EJB [Metier] comme des
mthodes d'un service web.

import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

@WebService
public class PamWsEjbMetier implements IMetier{
@EJB
private IMetierLocal metier;
@Override
public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int nbJoursTravaills) {
return [Link](SS, nbHeuresTravailles, nbJoursTravaills);
}
@Override
public List<Employe> findAllEmployes() {
return [Link]();
}
}

Dans Netbeans, l'application web est reconnue comme exposant un service web :

Pour dployer le service web sur le serveur Glassfish, il nous faut la fois dployer :

le module web dans le conteneur web du serveur ;


le module EJB dans le conteneur EJB du serveur ;

Pour cela, nous avons besoin de crer une application de type [Enterprise Application] qui va dployer les deux modules en mme
temps. Pour ce faire, il faut que les deux projets soient chargs dans Netbeans [2].
Ceci fait, nous crons un nouveau projet [3] :

198/290

en [4], nous choisissons un projet de type [Enterprise Application] ;


en [5], nous donnons un nom au projet ;

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 dj
crs et chargs dans Netbeans. Donc nous ne demandons pas la cration 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 mme temps [mv-pamwebapp]. Nous ne nous en occuperons pas ;
en [10], nous ajoutons des dpendances au projet d'entreprise [mv-pam-webapp-ear] :

en [11], on ajoute le projet web de type war ;


en [12], on ajoute le projet EJB de type ejb ;

199/290

en [13], le projet d'entreprise avec ses deux dpendances.

Nous construisons le projet d'entreprise par un Clean and Build. Nous sommes quasiment prts le dployer sur le serveur Glassfish.
Auparavant il peut tre ncessaire de dcharger les applications dj charges 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
dploye [15]. En [16], on peut voir qu'elle a bien t dploye sur le serveur Glassfish. Comme l'application dploye est une
application web, une page par dfaut est affiche dans le navigateur par dfaut du poste :

Nous pouvons tester le service web qui vient d'tre dploy :

200/290

en [1], nous demandons tester le service web [PamWsEjbMetier] ;


en [2], la page de test. Nous laissons au lecteur le soin de conduire les tests ;

Notes :

7.2.2

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 proprits du projet [Properties / Run] du projet web et fixer le serveur qui va excuter
le service web ;

La partie cliente

Travail faire : en suivant la dmarche dcrite au paragraphe [Link], page 192, construire un client console du service web
prcdent.

7.3

Service web SOAP implment avec Spring, Hibernate et Tomcat

Nous nous plaons maintenant dans le cadre de l'architecture suivante :

Client
du
service web tcp-ip

Conteneur web

JPA

Donnes

serveur Tomcat

Le service web est assur par une application web excute au sein du conteneur web du serveur Tomcat. L'architecture de
l'application sera la suivante :

Couche
[web]

Couche
[metier]

Couche
[DAO]

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Spring

Nous nous appuierons sur le projet [mv-pam-spring-hibernate] construit au paragraphe 4.10, page 125 :

201/290

7.3.1

La partie serveur

Nous crons une application Maven de type web nomme [mv-pam-ws-spring-tomcat] [1]:

Nous ajoutons une dpendance Maven sur le projet [mv-pam-spring-hibernate] [6-8] :

202/290

Nous compltons le fichier [[Link]] pour y inclure les nouvelles dpendances 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>

lignes 4-8 : la dpendance sur le projet [mv-pam-spring-hibernate] ;


lignes 10-19 : les dpendances sur le framework Apache CXF [[Link] Celui-ci facilite la cration de
services web SOAP ;
lignes 21-25 : la dpendance sur le fragmework [Spring web] ;

Revenons l'architecture de l'application :

Couche
[web]

Couche
[metier]

Couche
[DAO]

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Spring

L'application web que nous allons construire est une application web standard du monde JEE. A ce titre, elle est configure par un
fichier [WEB-INF / [Link]] que nous crons maintenant :

203/290

Les appels au service web que nous allons construire sont grs par une servlet du framework CXF. Cela se traduit dans le fichier
[WEB-INF / [Link]] de la faon 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

204/290

25.
</session-timeout>
26.
</session-config>
27. </web-app>

le framework CXF a une dpendance sur Spring (les archives Spring ncessaires sont automatiquement tlcharges).
Lignes 8-10 : un listener est dclar. La classe correspondante [ContextLoaderListener] va tre charge en mme temps
que l'application web ;
lignes 12-16 : la servlet CXF qui va grer les appels au service web que nous allons crer ;
lignes 17-20 : les URL traites par la servlet CXF seront du type /ws/*. Les autres ne seront pas traites par CXF ;

Pour dfinir le service web, nous dfinissons une interface et son implmentattion :

L'interface [IWsMetier] sera la suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.

package [Link];

ligne 7 : l'interface [IWsMetier] drive de l'interface [IMetier] de la couche [mtier] du projet [mv-pam-spring-hibernate] ;
ligne 6 : l'interface [IWsMetier] est celle d'un service web ;

import [Link];
import [Link];
@WebService
public interface IWsMetier extends IMetier{
}

La classe d'implmentation de cette interface est la suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.

package [Link];
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

@WebService
public class PamWsMetier implements IWsMetier {
// couche mtier
private IMetier metier;
// constructeur
public PamWsMetier(){
}
public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillees, int nbJoursTravailles) {
return [Link](SS, nbHeuresTravaillees, nbJoursTravailles);
}
public List<Employe> findAllEmployes() {
return [Link]();
}
// getters et setters
public void setMetier(IMetier metier) {
[Link] = metier;
}
}

205/290

ligne 11 : la classe [PamWsMetier] implmente l'interface dfinie prcdemment,


ligne 10 : dfinit la classe comme un service web,
ligne 14 : la couche [mtier] sera injecte par Spring,
lignes 22, 26 : les mthodes sont implmentes l'aide de la couche [mtier].

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 dfaut 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 amenes par les
dpendances Apache CXF ;
lignes 4, 8-9 : des espaces de noms spcifiques Apache CXF sont dclars ;
ligne 15 : on importe le fichier de configuration Spring du projet [mv-pam-spring-hibernate] ;
lignes 18-24 : dfinissent le bean du service web avec sa dpendance sur la couche [mtier] (ligne 19) ;
lignes 21-24 : dfinissent le service web lui-mme,

ligne 21 : le bean Spring implmentant le service web est celui dfini lignes 18-20 ;

ligne 26 : dfinit l'URL laquelle le service web sera disponible, ici /metier. Combine la forme que
doivent avoir les URL traites par Apache CXF (cf fichier [Link]), cette URL devient /ws/metier.

Notre projet est prt tre excut. Nous l'excutons (Run). Nous obtenons la page web suivante :

Nous demandons maintenant l'URL [/mv-pam-ws-spring-tomcat-hibernate/ws] dans un navigateur :

206/290

La page liste tous les services web dploys. Ici, il n'y en a qu'un. Nous suivons le lien WSDL :

Le texte affich [1] est celui d'un fichier XML qui dfinit les fonctionnalits du service web, comment l'appeler et quelles rponses il
envoie. On notera l'URL [2] de ce fichier WSDL. Tous les clients du service web ont besoin de la connatre.

7.3.2

La partie cliente

Travail faire : en suivant la dmarche dcrite au paragraphe [Link], page 192, construire un client console [1] du service web
prcdent ainsi qu'un test de la couche [metier] [2].
Note 1 : pour indiquer l'URL du fichier WSDL du service web, on suivra la procdure [3-5]. On mettra en [4], l'URL note
prcdemment en [2] ci-dessus.

207/290

Note 2 :
Considrons le test [JUnitMetierRemote] suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.

package metier;
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

public class JUnitMetierRemote {


// couche mtier
static private [Link] metier;
@BeforeClass
public static void init() {
// couche [metier]
metier = new [Link]().getPamWsMetierPort();
}
@AfterClass
static public void terminate() {
}
// logs
static private void log(String message) {
[Link]("----------- " + message);
}
// test
@Test
public void test01() {
[Link](72.4, [Link]("260124402111742", 30, 5).getElementsSalaire().getSalaireNet(),
1e-6);
[Link](368.77, [Link]("254104940426058", 150,
20).getElementsSalaire().getSalaireNet(), 1e-6);
boolean erreur = false;
try {
[Link]("xx", 150, 20);
} catch (Exception ex) {
erreur = true;
}
[Link](erreur);
}

35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
@Test
45.
public void test02() {
46.
// employs
47.
List<Employe> employes = [Link]();
48.
[Link](employes);
49.
}
50. }

Le test [tes02] choue avec le message d'erreur suivant dans la console :

208/290

Lorsque ct client distant, on voit une erreur de type [Marshalling / Unmarshalling] [1], le plus probable est qu'il y a eu une erreur
ct 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 lie au mode [LAZY] du champ JPA [[Link]]. Comme il avait t remarqu au
paragraphe 7.1.1, page 189, la mthode [getAllEmployes] du service SOAP renvoie les employs avec leurs indemnits. 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 mthode
[[Link]]. Hibernate et EclipseLink ne se comportent alors pas de la mme faon :

Hibernate : l'excution de la mthode [[Link]] provoque l'excution d'une requte JPA pour rcuprer
l'indemnit de l'employ. Cette requte n'est possible pour Hibernate que s'il y a un contexte de persistance. Or pour
l'excution de la mthode [metier].getAllEmployes, un contexte de persistance Hibernate est cr puis ferm. Lorsque le
service web SOAP fait appel la mthode [[Link]], le contexte de persistance n'existe plus et Hibernate
lance alors une exception ;

EclipseLink : dans les mmes conditions, EclipseLink ne lance pas d'exception et ramne correctement l'indemnit ;
L'erreur [4] [no Session] indique qu'Hibernate n'a pas pu faire la requte 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 fcheux.
Quoiqu'on fasse, lorsqu'on demandera les employs, on les aura avec leurs indemnits, ce qui parfois consommera de la bande
passante inutilement. De faon gnrale, lorsqu'on exploite une base de donnes, des relations de cl trangre peuvent relier
plusieurs tables entre elles. Le service web SOAP, qui suit les liens de cl trangre, ramnera des donnes dont on n'a pas
forcment besoin. Nous n'essaierons pas de rsoudre ce problme avec le protocole

7.4

Service web SOAP implment avec Spring, EclipseLink et Tomcat

Nous nous proposons maintenant de remplacer, dans le service web prcdent, Hibernate par EclipseLink. EclipseLink ne rsoud
pas le problme voqu prcdemment, savoir de ramener systmatiquement un employ avec son indemnit. Nanmoins, on n'a
pas besoin d'enlever le mode [LAZY] du champ [[Link]] pour que les tests unitaires passent.
Nous commenons par dupliquer le projet [mv-pam-ws-spring-tomcat-hibernate] dans le projet [mv-pam-ws-spring-tomcateclipselink] [1] :

209/290

en [2-4], on enlve la dpendance sur le projet [mv-pam-spring-hibernate] ;

en [5-9] : on cre une dpendance sur le projet [mv-pam-spring-eclipselink] du paragraphe 4.12, page 137 (le projet doit
tre charg dans Netbeans pour apparatre en [9]) ;

A ce stade, le projet ne prsente 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 gnr par l'IDE et tait le suivant :
1.
2.

<?xml version="1.0" encoding="UTF-8"?>


<Context path="/mv-pam-ws-spring-tomcat-hibernate"/>

210/290

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 demandes 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 gnre par dfaut un
contexte qui porte le nom du projet ;

De faon similaire, le fichier [META-INF / [Link]] du projet [mv-pam-sw-spring-tomcat-eclipselink] sera le suivant :


1.
2.

<?xml version="1.0" encoding="UTF-8"?>


<Context path="/mv-pam-ws-spring-tomcat-eclipselink"/>

Ceci fait, on peut excuter le projet :

On peut se demander d'o vient la page qui est affiche en [3-4]. Elle provient du fichier [[Link]] du projet [5-7] :

La page [[Link]] est la page affiche par dfaut lorsqu'on demande la racine du contexte [/mv-pam-ws-spring-tomcateclipselink]. Cela peut tre configur dans le fichier [WEB-INF / [Link]] du projet web [8].
Demandons maintenant l'URL [/mv-pam-ws-spring-tomcat-eclipselink/ws] :

211/290

puis suivons le lien [2]. Nous obtenons la page suivante :

Cette page reprsente la dfinition du service web dploy. Notez l'URL [4]. Vous allez en avoir besoin pour gnrer un client du
service web. Nous crons maintenant celui-ci.
Nous commenons par dupliquer le projet [pam-client-ws-spring-tomcat-hibernate] dans le projet [pam-client-ws-spring-tomcateclipselink] [1-2] :

Ensuite nous rgnrons le client du service web dans le nouveau projet. Pour cela, le service web [/mv-pam-ws-spring-tomcateclipselink] doit tre lanc :

212/290

Mettez en [8], l'URL de dfinition du service web dploy. Vous avez not cette URL prcdemment. Dans cet exemple, c'est l'URL
[[Link] Mettez en [9], le package [client]. C'est celui qui a
t utilis dans le prcdent projet [mv-pam-ws-spring-tomcat-hibernate] et l'application console a t construite pour l'utiliser.
A l'issue de l'assistant, une rfrence sur le service web distant a t ajoute au projet [9] et le code ncessaire pour communiquer
avec celui-ci a t gnr en [10].
Nous sommes prts pour excuter l'application console du client [1-8] :

Maintenant, excutons le test [JUnitMetierRemote] :

213/290

En [4], le test [test02] russit alors qu'avec Hibernate il avait chou :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.

package metier;
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

public class JUnitMetierRemote {


// couche mtier
static private [Link] metier;
@BeforeClass
public static void init() {
// couche [metier]
metier = new [Link]().getPamWsMetierPort();
}
@AfterClass
static public void terminate() {
}
// logs
static private void log(String message) {
[Link]("----------- " + message);
}
// test
@Test
public void test01() {
[Link](72.4, [Link]("260124402111742", 30, 5).getElementsSalaire().getSalaireNet(),
1e-6);
[Link](368.77, [Link]("254104940426058", 150,
20).getElementsSalaire().getSalaireNet(), 1e-6);
boolean erreur = false;
try {
[Link]("xx", 150, 20);
} catch (Exception ex) {
erreur = true;
}
[Link](erreur);
}

35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
@Test
45.
public void test02() {
46.
// employs
47.
List<Employe> employes = [Link]();
48.
[Link](employes);
49.
}
50. }

214/290

Dans le test [test02] des lignes 44-49, le service web ramne les employs avec leurs indemnits 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
rcuprer les employs a t ferm. Cela provoque une exception avec Hibernate mais pas avec EclipseLink.

7.5

Conclusion

Nous avons dploy des services web SOAP dans diffrents environnements :

Glassfish / EJB / EclipseLink ;


Tomcat / Spring / Hibernate ;
Tomcat / Spring / EclipseLink ;

Ils ont tous eu la particularit de ramener les employs avec leurs indemnits. Sans configuration supplmentaire, il n'est pas
possible de faire autrement. Nous ne creuserons pas davantage les services web SOAP. Dans les rfrences [ref4] [ref5], il est
montr comment crer des services web / jSON qui permettent de respecter l'ventuel mode [LAZY] prsent sur les champs
[@ManyToOne] ou [@OneToMany] des entits JPA. Ce type de services web a tendance supplanter les services web SOAP (sept
2016).

215/290

Introduction Java EE
par l'exemple
- Partie 2 -

[Link] at [Link], septembte 2016

216/290

8 Introduction Java Server Faces


On lira le document Introduction Java Server Faces, Primefaces et Primefaces
[[Link] On suivra le tutoriel sur Java Server Faces (JSF).

mobile

l'URL

217/290

9 Version 5 - Application PAM Web / JSF


9.1

Architecture de l'application

L'architecture de l'application web PAM sera la suivante :

Couche
[web /
JSF]

Couche
EJB3
[mtier]

Couche
EJB3
[DAO]

Objets image
de la BD

Implmentation
[EclipseLink]

Interface
[JPA]

Couche
[JDBC]

Serveur Glassfish

Dans cette version, le serveur Glassfish hbergera la totalit des couches de l'application :

la couche [web] est hberge par le conteneur de servlets du serveur (1 ci-dessous)

les autres couches [metier, DAO, JPA] sont hberges par le conteneur EJB3 du serveur (2 ci-dessous)

Conteneur web
[web / jsf]

Navigateur
HTTP

Jpa 3
Conteneur EJB3
[mtier, DAO] 2 EclipseLink

SGBD

serveur Java EE

Les lments [metier, DAO] de l'application s'excutant dans le conteneur EJB3 ont dj t crits dans l'application client / serveur
tudie au paragraphe 6.1, page 165 et dont l'architecture tait la suivante :
client
Couche
[ui]

serveur
Couche
[DAO]

Couche
[mtier]

Couche
[JPA /
EclipseLink]

Couche
[JDBC]

Jvm2 Java EE - serveur Glassfish

Jvm1 - Java SE

Les couches [metier, DAO] s'excutaient dans le conteneur EJB3 du serveur Glassfish et la couche [ui] dans une application console
ou swing sur une autre machine :
serveur

client

Conteneur Ejb3

Client
Java SE

Jpa / EclipseLink

Donnes

serveur Java EE

Dans l'architecture de la nouvelle application :

Conteneur web
[web / JSF2]

Navigateur
HTTP

Jpa 3
Conteneur EJB3
[mtier, DAO] 2 EclipseLink

SGBD

serveur Java EE

218/290

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 implmente avec Java Server Faces a une
architecture similaire la suivante :

Application web
couche [web]
1

Faces Servlet

Navigateur
4b

JSF1
JSF2
JSFn

2b

2a
3

Gestionnaire
d'vts

Modles

couche
[mtier, dao, jpa]

Donnes

2c
4a

Cette architecture implmente le Design Pattern MVC (Modle, Vue, Contrleur). Le traitement d'une demande d'un client se droule
de la faon suivante :
Si la demande est faite avec un GET, les deux tapes suivantes sont excutes :
1. demande - le client navigateur fait une demande au contrleur [Faces Servlet]. Celui-ci voit passer toutes les demandes des
clients. C'est la porte d'entre de l'application. C'est le C de MVC ;
2. rponse - le contrleur C demande la page JSF choisie de s'afficher. C'est la vue, le V de MVC. La page JSF utilise un modle
M pour initialiser les parties dynamiques de la rponse qu'elle doit envoyer au client. Ce modle est une classe Java qui peut faire
appel la couche [mtier] [4a] pour fournir la vue V les donnes dont elle a besoin ;
Si la demande est faite avec un POST, deux tapes supplmentaires s'insrent entre la demande et la rponse :
1. demande - le client navigateur fait une demande au contrleur [Faces Servlet] ;
2. traitement - le contrleur C traite cette demande. Une demande POST est accompagne de donnes qu'il faut traiter. Pour ce
faire, le contrleur se fait aider par des gestionnaires d'vnements spcifiques l'application crite [2a]. Ces gestionnaires
peuvent avoir besoin de la couche mtier [2b]. Le gestionnaire de l'vnement peut tre amen mettre jour certains modles
M [2c]. Une fois la demande du client traite, celle-ci peut appeler diverses rponses. Un exemple classique est :
une page d'erreurs si la demande n'a pu tre traite correctement ;
une page de confirmation sinon ;
Le gestionnaire d'vnement rend au contrleur [Faces Servlet] un rsultat de type chane de caractres appele cl de
navigation ;
3. navigation - le contrleur choisit la page JSF (= vue) envoyer au client. Ce choix se fait partir de la cl de navigation rendue
par le gestionnaire d'vnement ;
4. rponse - la page JSF choisie va envoyer la rponse au client. Elle utilise son modle M pour initialiser ses parties dynamiques.
Ce modle peut lui aussi faire appel la couche [mtier] [4a] pour fournir la page JSF les donnes dont elle a besoin ;
Dans un projet JSF :

le contrleur C est la servlet [[Link]]. On trouve celle-ci dans la bibliothque [[Link]] ;

les vues V sont implmentes par des pages JSF ;

les modles M et les gestionnaires d'vnements sont implments par des classes Java souvent appeles "backing
beans" ;

dans les version JSF 1.x la dfinition des beans ainsi que les rgles de navigation d'une page l'autre sont dfinies dans le
fichier [[Link]]. On y trouve la liste des vues et les rgles de transition de l'une l'autre. A partir de la version
JSF2, les dfinitions des beans peuvent se faire l'aide d'annotations et les transitions entre pages peuvent se faire en
" dur " dans le code des beans ;

9.2

Fonctionnement de l'application

Lorsque l'application est demande la premire fois, on obtient la page suivante :

219/290

On remplit alors le formulaire puis on demande le salaire :


B

On obtient le rsultat suivant :


C

220/290

Cette version calcule un salaire fictif. Il ne faut pas prter attention au contenu de la page mais sa mise en forme. Lorsqu'on utilise
le bouton [Raz], on revient la page [A].
Les saisies errones sont signales, comme le montre l'exemple suivant :
D

9.3

Le projet Netbeans

Nous allons construire une premire version de l'application o la couche [mtier] sera simule. Nous aurons l'architecture
suivante :

Application web
couche [web]
1

Faces Servlet

Navigateur
4b

JSF1
JSF2
JSFn

2b

2a
3

Gestionnaire
d'vts

couche
[metier]
simule

2c

Modles

4a

Lorsque les gestionnaires d'vnements ou les modles demanderont des donnes la couche [mtier] [2b, 4a], celle-ci leur donnera
des donnes fictives. Le but est d'obtenir une couche web rpondant correctement aux sollicitations de l'utilisateur. Lorsque ceci
sera atteint, il ne nous restera qu' installer la couche serveur dveloppe au paragraphe 6.1, page 165 :

Application web
couche [web]
1

Faces Servlet

Navigateur
4b

JSF1
JSF2
JSFn

2b

2a
3

Gestionnaire
d'vts

Modles

couche
[mtier, dao, jpa]

Donnes

2c
4a

Ce sera la version 2 de la version web de notre application PAM.


Le projet Netbeans de la version 1 est construit de la faon suivante [1-6] :

221/290

222/290

L'ajout du framework JSF au projet modifie le fichier [[Link]] qui configure l'application web [10] :

223/290

Le contenu du fichier [[Link]] est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.

<?xml version="1.0" encoding="UTF-8"?>


<web-app version="3.1" xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
<context-param>
<param-name>[Link].PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>[Link]</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/[Link]</welcome-file>
</welcome-file-list>
</web-app>

lignes 7-11 : dfinissent la servlet Java qui va traiter les demandes faites aux URL de l'application web. Celle-ci sera trouve
dans la dpendance [12] ;
lignes 12-15 : dfinissent le type des URL acceptes, celles de chemin [/contexte/faces/*] o * dsigne un chemin
quelconque ;
lignes 21-23 : dfinissent la page servis lorsque l'URL [/contexte] est demande sans prcision de chemin. C'est alors la
page [/contexte/faces/[Link]] qui est alors servie. Celle-ci sera trouve en [11] ci-dessus ;

Le contenu du fichier [[Link]] est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.

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


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[Link]
<html xmlns="[Link]
xmlns:h="[Link]
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
Hello from Facelets
</h:body>
</html>

Le fichier [[Link]] qui configure le projet Maven est le suivant :


1.
2.
3.
4.
5.

<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
<modelVersion>4.0.0</modelVersion>
<groupId>[Link]</groupId>

224/290

6.
<artifactId>mv-pam-jsf2-alone</artifactId>
7.
<version>1.0-SNAPSHOT</version>
8.
<packaging>war</packaging>
9.
10.
<name>mv-pam-jsf2-alone</name>
11.
12.
<properties>
13.
<[Link]>${[Link]}/endorsed</[Link]>
14.
<[Link]>UTF-8</[Link]>
15.
</properties>
16.
17.
<dependencies>
18.
<dependency>
19.
<groupId>javax</groupId>
20.
<artifactId>javaee-web-api</artifactId>
21.
<version>7.0</version>
22.
<scope>provided</scope>
23.
</dependency>
24.
</dependencies>
25.
26.
<build>
27.
<plugins>
28.
<plugin>
29.
<groupId>[Link]</groupId>
30.
<artifactId>maven-compiler-plugin</artifactId>
31.
<version>3.1</version>
32.
<configuration>
33.
<source>1.7</source>
34.
<target>1.7</target>
35.
<compilerArguments>
36.
<endorseddirs>${[Link]}</endorseddirs>
37.
</compilerArguments>
38.
</configuration>
39.
</plugin>
40.
<plugin>
41.
<groupId>[Link]</groupId>
42.
<artifactId>maven-war-plugin</artifactId>
43.
<version>2.3</version>
44.
<configuration>
45.
<failOnMissingWebXml>false</failOnMissingWebXml>
46.
</configuration>
47.
</plugin>
48.
<plugin>
49.
<groupId>[Link]</groupId>
50.
<artifactId>maven-dependency-plugin</artifactId>
51.
<version>2.6</version>
52.
<executions>
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>

ligne 22, on voit que l'unique dpendance a l'attribut [provided] : elle est ncessaire pour la compilation du projet mais ne
fera pas partie de l'archive produite pour le projet. A l'excution, cette dpendance sera trouve dans les bibliothques du
serveur Glassfish ;

L'excution du projet web donne le rsultat suivant :

225/290

C'est la page [[Link]] qui a t affiche.


Nous compltons le projet avec les lments qui suivent. Ces lments seront trouvs dans le support du TD.

en [1], les fichiers de configuration ;


en [2], les pages XHTML et la feuille de style ;
en [3], les classes de la couche [web] ;
en [4], les objets changs entre la couche [web] et la couche [mtier] et la couche [mtier] elle-mme ;
en [5], le fichier des messages pour l'internationalisation de l'application ;
en [6], les dpendances de l'application ;

Nous passons en revue certains de ces lments.

9.3.1

Les fichiers de configuration

Le fichier [[Link]] est celui gnr par dfaut par Netbeans avec de plus la configuration d'une page d'exception :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.

<?xml version="1.0" encoding="UTF-8"?>


<web-app version="3.1" xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
<!-- la servlet FacesServlet -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>[Link]</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- forme des URL traites -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<!-- pour avoir des logs sur les pages affiches, lors du dveloppement -->
<context-param>
<param-name>[Link].PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<!-- pour ignorer les commentaires des pages JSF -->

226/290

20.
<context-param>
21.
<param-name>[Link].FACELETS_SKIP_COMMENTS</param-name>
22.
<param-value>true</param-value>
23.
</context-param>
24.
<!-- dure 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>

ligne 32 : [[Link]] est la page d'accueil de l'application ;


lignes 34-42 : configuration de la page d'exception ;

La page [[Link]] est tire de [ref3]. Son code est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.

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


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[Link]
<html xmlns="[Link]
xmlns:h="[Link]
xmlns:f="[Link]
<f:view locale="#{[Link]}">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="[Link]"/>
</h:head>
<h:body style="background-image: url('${[Link]}/resources/images/[Link]');">
<h:form id="formulaire">
<h3><h:outputText value="#{msg['[Link]']}"/></h3>
<h:panelGrid columnClasses="col1,col2" columns="2" border="1">
<h:outputText value="#{msg['[Link]']}"/>
<h:outputText value="#{requestScope['[Link].status_code']}"/>
<h:outputText value="#{msg['[Link]']}"/>
<h:outputText value="#{requestScope['[Link]']}"/>
<h:outputText value="#{msg['[Link]']}"/>
<h:outputText value="#{requestScope['[Link].request_uri']}"/>
<h:outputText value="#{msg['[Link]']}"/>
<h:outputText value="#{requestScope['[Link].servlet_name']}"/>
</h:panelGrid>
</h:form>
</h:body>
</f:view>
</html>

Toute exception non explicitement gre par le code de l'application web provoquera l'affichage d'une page analogue celle cidessous :

Le fichier [[Link]] est le suivant :


1.
2.
3.

<?xml version="1.0" encoding="UTF-8"?>


<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"

227/290

4.
5.
6.

xmlns="[Link]
xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]

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

On notera les points suivants :

9.3.2

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 : dfinit le fichier [[Link]] comme devant tre explor en priorit pour les messages d'erreur affichs
par les balises <h:messages> et <h:message>. Cela permet de redfinir certains messages d'erreur par dfaut de JSF ;

La feuille de style

Le fichier [[Link]] est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.

.libelle{
background-color: #ccffff;
font-family: 'Times New Roman',Times,serif;
font-size: 14px;
font-weight: bold
}
body{
background-color: #ffccff
}
.error{
color: #ff3333
}
.info{
background-color: #99cc00
}
.titreInfos{
background-color: #ffcc00
}

Voici des exemples de code JSF utilisant ces styles :


<h:outputText value="#{msg['[Link]']}" styleClass="titreInfos"/>

<h:panelGrid columns="3" rowClasses="libelle,info">

<h:message for="heuresTravailles" styleClass="error"/>

9.3.3

Le fichier des messages

Le fichier des messages [messages_fr.properties] est le suivant :


1.
2.
3.
4.
5.

[Link]=Feuille de salaire
[Link]\u00e9=Employ\u00e9
[Link]\[Link]\u00e9=Heures travaill\u00e9es
[Link]\[Link]\u00e9=Jours travaill\u00e9s
[Link]\[Link]=Indiquez le nombre d'heures travaill\u00e9es

228/290

6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.

[Link]\[Link]=Donn\u00e9e incorrecte
[Link]\[Link]=Indiquez le nombre de jours travaill\u00e9s
[Link]\[Link]=Donn\u00e9e incorrecte
[Link]\u00e9=Salaire
[Link]\u00e9=Raz
[Link]=L'exception suivante s'est produite
[Link]=Code HTTP de l'erreur
[Link]=Message de l'exception
[Link]=Url demand\u00e9e lors de l'erreur
[Link]=Nom de la servlet demand\u00e9e lorsque l'erreur s'est produite
[Link]\u00e9=Informations Employ\u00e9
[Link]=Nom
[Link]\u00e9nom=Pr\u00e9nom
[Link]=Adresse
[Link]=Ville
[Link]=Code postal
[Link]=Indice
[Link]=Informations Cotisations sociales
[Link]=CSGRDS
[Link]=CSGD
[Link]=Retraite
[Link]=S\u00e9curit\u00e9 sociale
[Link]=Informations Indemnit\u00e9s
[Link]=Salaire horaire
[Link]=Entretien / Jour
[Link]=Repas / Jour
[Link]\u00e9sPay\u00e9s=Cong\u00e9s pay\u00e9s
[Link]=Informations Salaire
[Link]=Salaire de base
[Link]=Cotisations sociales
[Link]=Indemnit\u00e9s d'entretien
[Link]=Indemnit\u00e9s de repas
[Link]=Salaire net

Ces messages sont tous utiliss dans la page [[Link]] l'exception de ceux des lignes 11-15 utiliss dans la page
[[Link]].

9.3.4

La porte des beans

Le bean [[Link]] a une porte request :


1.
2.
3.
4.
5.
6.
7.

import [Link];
import [Link];
import [Link];
@ManagedBean
@RequestScoped
public class Form implements Serializable {

Le bean [[Link]] a une porte application :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.

package [Link];
import [Link];
import [Link];
import [Link];
@ManagedBean
@ApplicationScoped
public class ChangeLocale implements Serializable{
// la locale des pages
private String locale="fr-FR";
public ChangeLocale() {
}
public String setFrenchLocale(){
locale="fr-FR";
return null;
}
public String setEnglishLocale(){
locale="en-US";
return null;
}
public String getLocale() {
return locale;
}
public void setLocale(String locale) {
[Link] = locale;
}

229/290

33.
34.
35. }

9.3.5

La couche [mtier]

La couche [mtier] implmente l'interface IMetierLocal suivante :


1.
2.
3.
4.
5.
6.
7.
8.

package metier;
import [Link];
@Local
public interface IMetierLocal extends IMetier{
}

Cette interface est celle utilise dans la partie serveur de l'application client / serveur dcrite au paragraphe 6.1, page 165. Ligne 6,
l'interface [IMetier] est la suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.

package metier;
import [Link];
import [Link];
public interface IMetier {
// obtenir la feuille de salaire
FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int nbJoursTravaills );
// liste des employs
List<Employe> findAllEmployes();
}

La classe Metier que nous allons utiliser pour tester la couche [web] implmente cette interface de la faon suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.

package metier;
import
import
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

public class Metier implements IMetierLocal, Serializable {


// liste des employes
private final Map<String, Employe> hashEmployes = new HashMap<>();
private List<Employe> listEmployes;

// 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 nbHeuresTravailles, int nbJoursTravaills) {
26.
// on rcupre 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 employs
37.
@Override
38.
public List<Employe> findAllEmployes() {
39.
if (listEmployes == null) {
40.
// on cre une liste de deux employs
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 brlerie", "St Marcel", "49014",
new Indemnite(null, 1, 1, 1.93, 2, 3, 12)));
44.
// dictionnaire des employes

230/290

45.
for (Employe e : listEmployes) {
46.
[Link]([Link](), e);
47.
}
48.
}
49.
// on rend la liste des employs
50.
return listEmployes;
51.
}
52. }

Nous laissons au lecteur le soin de dcrypter ce code. On notera la mthode utilise : afin de ne pas avoir mettre en place la partie
EJB de l'application, nous simulons la couche [mtier]. Lorsque la couche [web] sera dclare correcte, nous pourrons alors la
remplacer par la vritable couche [mtier].

9.4

Le formulaire [[Link]] et son modle [[Link]]

Nous construisons maintenant la page XHTML du formulaire ainsi que son modle.
Lectures conseilles dans [ref3] :

exemple n 3 (mv-jsf2-03) pour la listes des balises utilisables dans un formulaire ;

exemple n 4 (mv-jsf2-04) pour les listes droulantes remplies par le modle ;

exemple n 6 (mv-jsf2-06) pour la validation des saisies ;

exemple n 7 (mv-jsf2-07) pour la gestion du bouton [Raz] ;

9.4.1

tape 1

Travail faire : Construire le formulaire [[Link]] et son modle [[Link]] ncessaires pour obtenir la page suivante :

Les composants de saisie sont les suivants :


n
1

id

type JSF

modle

comboEmployes <h:selectOneMenu>

String comboEmployesValue

rle
contient la liste des employs sous la forme
"prnom nom".

heuresTravaillees <h:inputText>

List<Employe> getEmployes()
String heuresTravailles

joursTravailles

<h:inputText>

String joursTravaills

btnSalaire

<h:commandButton>

lance le calcul du salaire

btnRaz

<h:commandButton>

remet le formulaire dans son tat premier

nombre d'heures travailles - nombre rel


nombre de jours travaills - nombre entier

la mthode getEmployes rendra une liste d'employs qu'elle obtiendra auprs de la couche [mtier]. Les objets affichs par le
combo auront pour attribut itemValue, le n SS de l'employ et pour attribut itemLabel, une chane constitue du
prnom et du nom de l'employ ;
les boutons [Salaire] et [Raz] ne seront pour l'instant pas connects des gestionnaires d'vnement ;
la validit des saisies sera vrifie ;

231/290

Testez cette version. Vrifiez notamment que les erreurs de saisie sont bien signales.
Note : il est important que les attributs id des composants de la page ne comportent pas de caractres accentus. Cela peut
planter l'application.

9.4.2

tape 2

Travail faire : complter le formulaire [[Link]] et son modle [[Link]] pour obtenir la page suivante une fois que le
bouton [Salaire] a t cliqu :

Le bouton [Salaire] sera connect au gestionnaire d'vnement calculerSalaire du modle. Cette mthode utilisera la mthode
calculerFeuilleSalaire de la couche [mtier]. Cette feuille de salaire sera faite pour l'employ slectionn en [1].
Dans le modle, la feuille de salaire sera reprsente par le champ priv suivant :
private FeuilleSalaire feuilleSalaire;

disposant des mthodes get et set.


Pour obtenir les informations contenues dans cet objet, on pourra crire dans la page JSF, des expressions comme la suivante :
<h:outputText value="#{[Link]}"/>

L'expression de l'attribut value sera value comme suit :


[form].getFeuilleSalaire().getEmploye().getNom() o [form] reprsente une instance de la classe [[Link]]. Le lecteur pourra vrifier que
les mthodes get utilises ici existent bien respectivement dans les classes [Form], [FeuilleSalaire] et [Employe]. Si ce n'tait pas le cas,
une exception serait lance lors de l'valuation de l'expression.
Testez cette nouvelle version.

9.4.3

tape 3

Travail faire : complter le formulaire [[Link]] et son modle [[Link]] pour obtenir les informations supplmentaires
suivantes :

232/290

On suivra la mme dmarche que prcdemment. Il y a une difficult pour le signe montaire euro que l'on a en [1] par exemple.
Dans le cadre d'une application internationalise, il serait prfrable d'avoir le format d'affichage et le signe montaire de la locale
utilise (en, de, fr, ...). Cela peut s'obtenir de la faon suivante :
1.
2.
3.

<h:outputFormat value="{0,number,currency}">
<f:param value="#{[Link]}"/>
</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 affiche :

9.4.4

ligne 1 : affiche le paramtre {0} qui est un nombre (number) reprsentant une somme d'argent (currency) ;
ligne 2 : la balise <f:param> donne une valeur au paramtre {0}. Une deuxime balise <f:param> donnerait une valeur au
paramtre not {1} et ainsi de suite ;

tape 4

Lectures conseilles : exemple n 7 (mv-jsf2-07) dans [ref3].


Travail faire : complter le formulaire [[Link]] et son modle [[Link]] pour grer le bouton [Raz].
Le bouton [Raz] ramne le formulaire dans l'tat qu'il avait lorsqu'on l'a demand la premire fois par un GET. Il y a plusieurs
difficults ici. Certaines ont t expliques dans [ref3].
Le formulaire rendu par le bouton [Raz] n'est pas tout le formulaire mais seulement la partie saisie de celui-ci :

233/290

Ce rsultat peut tre obtenu avec une balise <f:subview> utilise de la faon suivante :
1.
2.
3.

<f:subview id="viewInfos" rendered="#{[Link]}">


... la partie du formulaire qu'on veut pouvoir ne pas afficher
</f:subview>

La balise <f:subview> encadre toute la partie du formulaire qui est susceptible d'tre affiche ou cache. Tout composant peut tre
affich ou cach grce 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 modle, alors l'affichage du composant peut tre contrl par programme.
Ci-dessus, on contrlera l'affichage de la vue viewInfos avec le champ suivant :
private boolean viewInfosIsRendered;

accompagn de ses mthodes get et set. Les mthodes grant les clics sur les bouton [Salaire] [Raz] mettront jour ce boolen selon
que la vue viewInfos doit tre affiche ou non.

234/290

10 Version 6 - Intgration de la couche web dans une architecture 3


couches JSF / EJB
10.1

Architecture de l'application

L'architecture de l'application web prcdente tait la suivante :

Application web
couche [web]
1

Navigateur
4b

JSF1
JSF2
JSFn

2b

2a

Faces Servlet
3

Gestionnaire
d'vts

couche
[metier]
simule

2c

Modles

4a

Nous remplaons la couche [mtier] simule, par les couches [mtier, DAO, JPA] implmentes par des EJB au paragraphe 6.1, page
165 :

Application web
couche [web]
1

Navigateur
4b

JSF1
JSF2
JSFn

10.2

2b

2a

Faces Servlet
3

Gestionnaire
d'vts

Modles

couches
[metier, DAO, JPA]

Donnes

2c
4a

Le projet Netbeans de la couche web

Le projet Netbeans de la version web n 2 est obtenue par copie du projet prcdent :

235/290

Nous avons peu de modifications faire pour adapter cette couche web son nouvel environnement : la couche [metier] [2] simule
doit tre remplace par la couche [metier, DAO, JPA] du serveur construit au paragraphe 6.1, page 165. Pour cela, nous faisons
deux choses :

nous supprimons les paquetages [exception, metier, JPA] qui taient prsents dans le prcdent projet ;
pour compenser cette suppression, nous ajoutons aux dpendances du projet web, le projet du serveur EJB construit au
paragraphe 6.1, page 165 ;

en [3-4], on ajoute une dpendance au projet ;


en [5], on slectionne le projet Maven de la couche [mtier]. En [6], on prcise son type et en [7] sa porte. Celle-ci est
provided pour indiquer que celui-ci sera fourni (provided) au module web par son environnement de travail. Nous verrons
prochainement qu'il lui sera fourni par une application d'entreprise ;

en [8], la dpendance a t ajoute ;

Le fichier [[Link]] [9] est alors le suivant :


1. <dependencies>
2.
<dependency>
3.
<groupId>${[Link]}</groupId>
4.
<artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
5.
<version>${[Link]}</version>
6.
<scope>provided</scope>
7.
<type>ejb</type>
8.
</dependency>
9.
<dependency>
10.
<groupId>javax</groupId>
11.
<artifactId>javaee-web-api</artifactId>
12.
<version>7.0</version>
13.
<scope>provided</scope>
14.
</dependency>
15.
</dependencies>

Il nous faut modifier le code du bean [[Link]] :


1.

@ManagedBean

236/290

2. @RequestScoped
3. public class Form {
4.
5.
public Form() {
6.
}
7.
8.
// couche mtier
9.
private IMetierLocal metier=new Metier();
10.
11.
// champs du formulaire
12. ...

La ligne 9 instanciait la couche [mtier] simule. Dsormais elle doit rfrencer la couche [mtier] relle. Le code prcdent devient
le suivant :
1. @ManagedBean
2. @RequestScoped
3. public class Form {
4.
5.
public Form() {
6.
}
7.
8.
// couche mtier
9.
@EJB
10.
private IMetierLocal metier;
11.
12.
// champs du formulaire

Ligne 9, l'annotation @EJB indique au conteneur de servlets qui va excuter la couche web, d'injecter dans le champ metier de la
couche 8, l'EJB qui implmente l'interface locale IMetierLocal.
Pourquoi l'interface locale IMetierLocal plutt que l'interface IMetierRemote ? Parce que la couche web et la couche EJB s'excutent
dans la mme JVM :

Application web
couche [web]
1

Faces Servlet

Navigateur
4b

JSF1
JSF2
JSFn

2b

2a
3

Gestionnaire
d'vts

Modles

Conteneur de servlets

couche
[metier, dao, jpa]

Donnes

2c
4a

Conteneur Ejb

Les classes du conteneur de servlets peuvent rfrencer directement les classes EJB du conteneur EJB.
C'est tout. Notre couche web est prte. La transformation a t simple parce qu'on avait pris soin de simuler la couche [mtier] par
une classe qui respectait l'interface IMetierLocal implmente par la couche [mtier] relle.

10.3

Le projet Netbeans de l'application d'entreprise

Une application d'entreprise permet le dploiement simultan sur un serveur d'applications, de la couche [web] et de la couche EJB
d'une application, respectivement dans le conteneur de servlets et dans le conteneur EJB.
Nous procdons de la faon suivante :

237/290

en [1], on cre un nouveau projet ;


en [2], on choisit la catgorie [Maven] ;
en [3], on choisit le type [Enterprise Application] ;

en [4], on donne un nom au projet ;


en [6], nous choisissons Java EE 7 ;
en [7], un projet d'entreprise peut comprendre jusqu' deux types de modules :

un module EJB ;

un module web ;
On peut demander en mme temps que la cration du projet d'entreprise, la cration de ces deux modules qui seront vides
au dpart. Un projet d'entreprise ne sert qu'au dploiement des modules qui en font partie. En-dehors de a, c'est une
coquille vide. Ici, nous voulons dployer :

un module web existant [mv-pam-jsf2-ejb]. Il est donc inutile de crer un nouveau module web ;

un module EJB existant [mv-pam-ejb-metier-dao-eclipselink]. L galement, il est inutile d'en crer un nouveau ;
En [7], nous crons un projet d'entreprise sans modules. Nous allons lui ajouter ses modules web et ejb ultrieurement ;

en [8], deux projets Maven ont t crs. Le projet d'entreprise est celui qui a le suffixe ear. L'autre projet est un projet
Maven parent du prcdent. Nous ne nous en occuperons pas.

Nous ajoutons le module web et le module EJB au projet d'entreprise :

238/290

en [1], ajout d'une nouvelle dpendance ;


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

en [8], le projet d'entreprise avec des deux dpendances ;

Avant le dploiement 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 dployer 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 [mvpam-webapp-ear] ;
excuter le projet d'entreprise ;

en [1], l'application d'entreprise est dploye ;


en [2-3], l'application d'entreprise [mv-pam-webapp-ear] a bien t dploye sur le serveur Glassfish ;

239/290

Dans le navigateur, on obtient la page suivante :

en [1], l'URL demande :


en [2], la liste des employs a t remplie avec les lments de la table [Employes] de la base dbpam_hibernate ;

Le lecteur est invit refaire les tests de la version web n 1. Voici un exemple d'excution :

240/290

11 Version 7 - Application web PAM multi-vues / multi-pages


Nous revenons ici l'architecture initiale o la couche [mtier] tait simule. Nous savons dsormais que celle-ci peut tre aisment
remplace par la couche [mtier] relle. La couche [mtier] simule facilite les tests.

Application web
couche [web]
1

Faces Servlet

Navigateur
4b

JSF1
JSF2
JSFn

2b

2a
3

Gestionnaires
d'vts

Modles

couche
[metier]
simule

2c
4a

Une application JSF est de type MVC (Modle Vue Contrleur) :

la servlet [Faces Servlet] est le contrleur gnrique fourni par JSF. Ce contrleur est tendu par les gestionnaires
d'vnements spcifiques l'application. Les gestionnaires d'vnements rencontrs jusqu'ici taient des mthodes des
classes servant de modles aux pages JSF ;
les pages JSF envoient les rponses au navigateur client. Ce sont les vues de l'application ;
les pages JSF comportent des lments dynamiques qu'on appelle le modle de la page. On rappelle que pour certains
auteurs, le modle recouvre les entits manipules par l'application, telles par exemple les classes FeuilleSalaire ou Employe.
Pour distinguer ces deux modles, on pourra parler de modle de l'application et modle d'une page JSF ;

Dans l'architecture JSF, le passage d'une page JSFi une page JSFj peut tre problmatique :

la page JSFi a t affiche. A partir de cette page, l'utilisateur provoque un POST par un vnement quelconque [1] ;
en JSF, ce POST sera trait [2a,2b] en gnral par une mthode C du modle M i de la page JSFi. On peut dire que la
mthode C est un contrleur secondaire ;
si l'issue de cette mthode, la page JSFj doit tre affiche, le contrleur C doit :
1. mettre jour [2c] le modle Mj de la page JSFj ;
2. rendre [2a] au contrleur principal, la cl de navigation qui permettra l'affichage de la page JSF j ;
L'tape 1 ncessite que le modle Mi de la page JSFi ait une rfrence sur modle Mj de la page JSFj. Cela complique un
peu les choses rendant les modles Mi dpendant les uns des autres. En effet, le gestionnaire C du modle M i qui met
jour le modle Mj doit connatre celui-ci. Si on est amen changer le modle M j, on sera alors amen changer le
gestionnaire C du modle Mi.
Il existe un cas o la dpendance des modles entre-eux peut tre vite : celui o il y a un unique modle M qui sert
toutes les pages JSF. Cette architecture n'est utilisable que dans les applications n'ayant que quelques vues mais elle se
rvle alors trs simple d'usage. C'est celle que nous utilisons maintenant.

Dans ce contexte, l'architecture de l'application est la suivante :

Application web
couche [web]
1

Faces Servlet

2a
3

JSF1
JSF2
JSFn

2b

[MC]
[Link]
Modle M
Gestionnaires
d'vts

couche
[metier]
simule

241/290

11.1

Les vues de l'application

Les diffrentes vues prsentes l'utilisateur seront les suivantes :


- la vue [VueSaisies] qui prsente le formulaire de simulation :

- la vue [VueSimulation] utilise pour afficher le rsultat dtaill de la simulation :

- la vue [VueSimulations] qui donne la liste des simulations faites par le client

242/290

- la vue [VueSimulationsVides] qui indique que le client n'a pas ou plus de simulations :

la vue [VueErreur] qui indique une ou plusieurs erreurs :

11.2

Le projet Netbeans de la couche [web]

Le projet Netbeans de cette version sera le projet Maven suivant :

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 dpendances du projet.

Nous passons en revue certains de ces lments.

243/290

11.2.1

Les fichiers de configuration

Les fichiers [[Link]] et [[Link]] sont identiques ceux du projet prcdent l'exception d'un dtail dans [[Link]] :
1.
2.
3.
4.
5.
6.
7.
8.

<?xml version="1.0" encoding="UTF-8"?>


<web-app version="3.0" xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
...
<welcome-file-list>
<welcome-file>faces/[Link]</welcome-file>
</welcome-file-list>
...
</web-app>

lignes 4-6 : la page d'accueil est la page [[Link]] ;

11.2.2

La feuille de style

Le fichier [[Link]] est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.

.simulationsHeader {
text-align: center;
font-style: italic;
color: Snow;
background: Teal;
}
.simuNum {
height: 25px;
text-align: center;
background: MediumTurquoise;
}
.simuNom {
text-align: left;
background: PowderBlue;
}
.simuPrenom {
width: 6em;
text-align: left;
color: Black;
background: MediumTurquoise;
}
.simuHT {
width: 3em;
text-align: center;
color: Black;
background: PowderBlue;
}
.simuJT {
width: 3em;
text-align: center;
color: Black;
background: MediumTurquoise;
}
.simuSalaireBase {
width: 9em;
text-align: center;
color: Black;
background: PowderBlue;
}
.simuIndemnites {
width: 3em;
text-align: center;
color: Black;
background: MediumTurquoise;
}
.simuCotisationsSociales {
width: 6em;
text-align: center;
background: PowderBlue;
}
.simuSalaireNet {
width: 10em;
text-align: center;
background: MediumTurquoise;
}
.erreursHeaders {
background: Teal;
background-color: #ff6633;
color: Snow;
font-style: italic;

244/290

64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.

text-align: center
}
.erreurClasse {
background: MediumTurquoise;
background-color: #ffcc66;
height: 25px;
text-align: center
}
.erreurMessage {
background: PowderBlue;
background-color: #ffcc99;
text-align: left
}

Voici des exemples de code JSF utilisant ces styles :


Vue Simulations
<h:dataTable value="#{[Link]}" var="simulation"
headerClass="simulationsHeaders"
columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisationsSociales,simuSalaireNet">

Le rsultat obtenu est le suivant :

Vue Erreur
<h:dataTable value="#{[Link]}" var="erreur"
headerClass="erreursHeaders" columnClasses="erreurClasse,erreurMessage">

11.2.3

Le fichier des messages

Le fichier des messages [messages_fr.properties] est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.

[Link]=Simulateur de calcul de paie


[Link]\u00e9=Employ\u00e9
[Link]\[Link]\u00e9=Heures travaill\u00e9es
[Link]\[Link]\u00e9=Jours travaill\u00e9s
[Link]\[Link]=Indiquez le nombre d'heures travaill\u00e9es
[Link]\[Link]=Donn\u00e9e incorrecte
[Link]\[Link]=Indiquez le nombre de jours travaill\u00e9s
[Link]\[Link]=Donn\u00e9e incorrecte
[Link]\u00e9=Salaire
[Link]\u00e9=Raz
[Link]=L'exception suivante s'est produite
[Link]=Code HTTP de l'erreur
[Link]=Message de l'exception
[Link]=Url demand\u00e9e lors de l'erreur
[Link]=Nom de la servlet demand\u00e9e lorsque l'erreur s'est produite
[Link]\u00e9=Informations Employ\u00e9
[Link]=Nom
[Link]\u00e9nom=Pr\u00e9nom
[Link]=Adresse
[Link]=Ville
[Link]=Code postal
[Link]=Indice
[Link]=Informations Cotisations sociales
[Link]=CSGRDS

245/290

25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.

11.2.4

[Link]=CSGD
[Link]=Retraite
[Link]=S\u00e9curit\u00e9 sociale
[Link]=Informations Indemnit\u00e9s
[Link]=Salaire horaire
[Link]=Entretien / Jour
[Link]=Repas / Jour
[Link]\u00e9sPay\u00e9s=Cong\u00e9s pay\u00e9s
[Link]=Informations Salaire
[Link]=Salaire de base
[Link]=Cotisations sociales
[Link]=Indemnit\u00e9s d'entretien
[Link]=Indemnit\u00e9s de repas
[Link]=Salaire net
[Link]=| Faire la simulation
[Link]=| Effacer la simulation
[Link]=| Enregistrer la simulation
[Link]=| Retour au simulateur
[Link]=| Voir les simulations
[Link]=| Terminer la session
[Link]=Nom
[Link]=Nom
[Link]=Pr\u00e9nom
[Link]=Heures travaill\u00e9es
[Link]=Jours Travaill\u00e9s
[Link]=Salaire de base
[Link]=Indemnit\u00e9s
[Link]=Cotisations sociales
[Link]=SalaireNet
[Link]=N\u00b0
[Link]=Une erreur s'est produite.
[Link]=Cha\u00eene des exceptions
[Link]=Type de l'exception
[Link]=Message associ\u00e9

La couche [mtier]

La couche [mtier] simule est modifie de la faon suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.

package metier;
import
import
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

public class Metier implements IMetierLocal, Serializable {


// liste des employes
private final Map<String, Employe> hashEmployes = new HashMap<>();
private List<Employe> listEmployes;

// 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,
26.
double nbHeuresTravailles, int nbJoursTravaills) {
27.
// on rcupre l'employ
28.
Employe e=[Link](SS);
29.
// on rend une exception si l'employ n'existe pas
30.
if(e==null){
31.
throw new PamException([Link]("L'employ de n SS [%s] n'existe pas",SS),1);
32.
}
33.
// on rend une feuille de salaire fiictive
34.
return new FeuilleSalaire(e, new Cotisation(null, 1, 9.39, 7.88, 6.15, 3.49), new ElementsSalaire(100, 100, 100, 100,
100));
35.
}
36.
37.
// liste des employs
38.
@Override
39.
public List<Employe> findAllEmployes() {
40.
if (listEmployes == null) {
41.
// on cre une liste de trois employs
42.
listEmployes = new ArrayList<>();

246/290

43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.

11.3

[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)));
[Link](new Employe(null, 1, "Justine", "Laverti", "260124402111742", "La brlerie", "St Marcel", "49014",
new Indemnite(null, 1, 1, 1.93, 2, 3, 12)));
// dictionnaire des employes
for (Employe e : listEmployes) {
[Link]([Link](), e);
}
}
// on ajoute un employ qui n'existera pas dans le dictionnaire
[Link](new Employe(null, 1, "X", "Y", "Z", "", "", "", new Indemnite(null, 1, 2, 2.1, 2.1, 3.1, 15)));
// on rend la liste des employs
return listEmployes;
}
}

ligne 17 : la liste des employs ;


ligne 16 : la mme liste sous forme de dictionnaire index par le n SS des employs ;
lignes 38-54 : la mthode findAllEmployes qui rend la liste des employs. Cette mthode cre une liste en dur et la rfrence
par le champ employs de la ligne 17 ;
lignes 41-58 : une liste et un dictionnaire de deux employs sont crs ;
ligne 51 : un employ est ajout la liste employes (ligne 17) mais pas au dictionnaire hashEmployes (ligne 16). Ceci
pour qu'il apparaisse dans le combo des employs mais qu'ensuite il ne soit pas reconnu par la mthode
calculerFeuilleSalaire (ligne 25) afin que celle-ci lance une exception (ligne 31).
lignes 24-35 : la mthode calculerFeuilleSalaire ;
ligne 28 : l'employ est cherch dans le dictionnaire hashEmployes via son n SS. S'il n'est pas trouv, une exception est
lance (lignes 30-32). Ainsi aurons-nous une exception pour l'employ de n SS X ajout ligne 51 dans la liste employs
mais pas dans le dictionnaire hashEmployes ;
ligne 34, une feuille de salaire fictive est cre et rendue ;

Les beans de l'application

Il y aura trois beans de trois portes diffrentes :

11.3.1

Le bean ApplicationData

Le bean ApplicationData sera de porte application :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.

package [Link];
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

@Named
@ApplicationScoped
public class ApplicationData implements Serializable {
// couche mtier
private IMetierLocal metier = new Metier();
// logger
private boolean logsEnabled = true;
private final Logger logger = [Link]("pam");
public ApplicationData() {
}
@PostConstruct
public void init() {
// log

247/290

27.
if (isLogsEnabled()) {
28.
[Link]("ApplicationData");
29.
}
30.
}
31.
32.
// getters et setters
33. ...
34. }

ligne 11 : l'annotation @Named fait de la classe un bean manag. On notera qu' la diffrence du projet prcdent, on n'a
pas utilis l'annotation @ManagedBean. La raison en est que la rfrence de cette classe doit tre injecte dans une autre
classe l'aide de l'annotation @Inject et que celle-ci n'injecte que des classes annotes @Named ;
ligne 12 : l'annotation @ApplicationScoped fait de la classe, un objet de porte application. On notera que la classe de
l'annotation est [[Link]] (ligne 6) et non [[Link]] comme
dans les beans du projet prcdent ;

Le bean ApplicationData sert deux choses :

11.3.2

ligne 16 : maintenir une rfrence sur la couche [mtier],


lignes 18-19 : dfinir un logueur qui pourra tre utilis par les autres beans pour faire des logs sur la console de Glassfish.

Le bean SessionData

Le bean SessionData sera de porte session :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.

package [Link];
import
import
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

@Named
@SessionScoped
public class SessionData implements Serializable {
// application
@Inject
private ApplicationData applicationData;
// simulations
private List<Simulation> simulations = new ArrayList<Simulation>();
private int numDerniereSimulation = 0;
private Simulation simulation;
// menus
private boolean menuFaireSimulationIsRendered = true;
private boolean menuEffacerSimulationIsRendered = true;
private boolean menuEnregistrerSimulationIsRendered;
private boolean menuVoirSimulationsIsRendered;
private boolean menuRetourSimulateurIsRendered;
private boolean menuTerminerSessionIsRendered = true;
// locale
private String locale="fr_FR";
// constructeur
public SessionData() {
}
@PostConstruct
public void init() {
// log
if ([Link]()) {
[Link]().info("SessionData");
}
}
// gestion des menus
public void setMenu(boolean menuFaireSimulationIsRendered, boolean menuEnregistrerSimulationIsRendered, boolean
menuEffacerSimulationIsRendered, boolean menuVoirSimulationsIsRendered, boolean menuRetourSimulateurIsRendered, boolean
menuTerminerSessionIsRendered) {
[Link](menuFaireSimulationIsRendered);
[Link](menuEnregistrerSimulationIsRendered);
[Link](menuVoirSimulationsIsRendered);
[Link](menuEffacerSimulationIsRendered);
[Link](menuRetourSimulateurIsRendered);
[Link](menuTerminerSessionIsRendered);
}

248/290

55.
56.
// getters et setters
57.
...
58. }

ligne 13 : la classe SessionData est un bean manag (@Named) qui pourra tre inject dans d'autres beans manags,
ligne 14 : il est de porte session (@SessionScoped),
lignes 18-19 : une rfrence sur le bean ApplicationData lui est inject (@Inject),
lignes 21-32 : les donnes 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 dernire simulation enregistre,
ligne 23 : la dernire simulation qui a t faite,
lignes 25-30 : les options du menu,
ligne 32 : la locale de l'application.

Lignes 39-44, la mthode init est excute aprs instanciation de la classe (@PostConstruct). Ici, elle n'est utilise que pour laisser
une trace de son excution. On doit pouvoir vrifier qu'elle n'est excute qu'une fois par utilisateur puisque la classe est de porte
session. Ligne 42, la mthode utilise le logueur dfini dans la classe ApplicationData. C'est pour cette raison qu'on avait besoin
d'injecter une rfrence sur ce bean (lignes 18-19).

11.3.3

Le bean Form

Le bean Form est de porte requte :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.

package [Link];
import
import
import
import
import
import
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link].*;
[Link];
[Link];

@Named
@RequestScoped
public class Form {
public Form() {
}
// autres beans
@Inject
private ApplicationData applicationData;
@Inject
private SessionData sessionData;
// le modle des vues
private String comboEmployesValue = "";
private String heuresTravailles = "";
private String joursTravaills = "";
private Integer numSimulationToDelete;
private List<Erreur> erreurs = new ArrayList<Erreur>();
private FeuilleSalaire feuilleSalaire;
// liste des employs
public List<Employe> getEmployes(){
return [Link]().findAllEmployes();
}
// action du menu
public String faireSimulation() {
...
}
public String enregistrerSimulation() {
...
}
public String effacerSimulation() {
...
}
public String voirSimulations() {
...

249/290

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

11.4

11.4.1

ligne 17, la classe est un bean manag (@Named),


ligne 18, de porte requte (@RequestScoped),
lignes 24-25 : injection d'une rfrence sur le bean de porte application ApplicationData,
lignes 26-27 : injection d'une rfrence sur le bean de porte session SessionData.

Les pages de l'application

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

250/290

27.
Gestion des assistantes maternelles
28.
</ui:insert>
29.
<ui:insert name="part2"/>
30.
</h:form>
31.
</h:body>
32.
</f:view>
33. </html>

Toute vue est constitue des lments suivants :

un entte affich par le fragment [[Link]] (ligne 24),


un corps form de deux fragments appels part1 (lignes 26-28) et part2 (ligne 29),
ligne 8 : l'attribut locale assure l'internationalisation des pages. Ici il n'y en aura qu'une : fr_FR,
lignes 14-19 : un code Javascript.

L'affichage de la page [[Link]] est le suivant :

11.4.2

en [1], l'entte [[Link]],


en [2], le fragment part1.

L'entte [[Link]]

Le code de la page [[Link]] 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.
8.
<ui:composition>
9.
<!-- entete -->
10.
<h:panelGrid columns="2">
11.
<h:panelGroup>
12.
<h2><h:outputText value="#{msg['[Link]']}"/></h2>
13.
</h:panelGroup>
14.
<h:panelGroup>
15.
<h:panelGrid columns="1">
16.
<h:commandLink id="cmdFaireSimulation" value="#{msg['[Link]']}"
action="#{[Link]}" rendered="#{[Link]}"/>
17.
<h:commandLink id="cmdEffacerSimulation" onclick="raz()" value="#{msg['[Link]']}"
action="#{[Link]}" rendered="#{[Link]}"/>
18.
<h:commandLink id="cmdEnregistrerSimulation" immediate="true" value="#{msg['[Link]']}"
action="#{[Link]}" rendered="#{[Link]}"/>
19.
<h:commandLink id="cmdVoirSimulations" immediate="true" value="#{msg['[Link]']}"
action="#{[Link]}" rendered="#{[Link]}"/>
20.
<h:commandLink id="cmdRetourSimulateur" immediate="true" value="#{msg['[Link]']}"
action="#{[Link]}" rendered="#{[Link]}"/>
21.
<h:commandLink id="cmdTerminerSession" immediate="true" value="#{msg['[Link]']}"
action="#{[Link]}" rendered="#{[Link]}"/>
22.
</h:panelGrid>
23.
</h:panelGroup>
24.
</h:panelGrid>
25.
<hr/>
26.
</ui:composition>
27. </html>

ligne 12 : le titre de l'application,

251/290

11.5
11.5.1

lignes 16-21 : les six liens correspondant aux six actions que peut faire l'utilisateur. Ces liens sont contrls (attribut
rendered) par des boolens du bean SessionData,
ligne 17 : un clic sur le lien [Effacer la simulation] provoque l'excution de la fonction Javascript raz. Celle-ci a t dfinie
dans le modle [[Link]],
ligne 18 : l'attribut immediate=true fait que la validit des donnes n'est pas vrifie avant excution de la mthode
[Form].enregistrerSimulation. C'est voulu. On peut vouloir enregistrer la dernire simulation faite il y a une minute, mme si
les donnes saisies depuis dans le formulaire (mais pas encore valides) ne sont pas valides. Il est fait de mme pour les
actions [Voir les simulations], [Effacer la simulation], [Retour au simulateur] et [Terminer la session].

Les cas d'utilisation de l'application


Affichage de la page d'accueil

La page d'accueil est la page [[Link]] suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.

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


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[Link]
<html xmlns="[Link]
xmlns:h="[Link]
xmlns:f="[Link]
xmlns:ui="[Link]

ligne 8 : elle s'affiche l'intrieur de la page [[Link]],


ligne 9 : la place du fragment nomm part1. Dans ce fragment, on affiche la page [[Link]] :

<ui:composition template="[Link]">
<ui:define name="part1">
<ui:include src="[Link]"/>
</ui:define>
</ui:composition>
</html>

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.
<h:panelGrid columns="3">
9.
<h:outputText value="#{msg['[Link]']}"/>
10.
<h:outputText value="#{msg['[Link]']}"/>
11.
<h:outputText value="#{msg['[Link]']}"/>
12.
<h:selectOneMenu id="comboEmployes" value="#{[Link]}">
13.
<f:selectItems .../>
14.
</h:selectOneMenu>
15.
<h:inputText id="heuresTravaillees" value="#{[Link]}" required="true"
requiredMessage="#{msg['[Link]']}" validatorMessage="#{msg['[Link]']}">
16.
<f:validateDoubleRange minimum="0" maximum="300"/>
17.
</h:inputText>
18.
<h:inputText id="joursTravailles" value="#{[Link]}" required="true"
requiredMessage="#{msg['[Link]']}" validatorMessage="#{msg['[Link]']}">
19.
<f:validateLongRange minimum="0" maximum="31"/>
20.
</h:inputText>
21.
<h:panelGroup></h:panelGroup>
22.
<h:message for="heuresTravaillees" styleClass="error"/>
23.
<h:message for="joursTravailles" styleClass="error"/>
24.
</h:panelGrid>
25.
<hr/>
26. </html>

Ce code affiche la vue suivante :

252/290

Travail faire : complter la ligne 13 du code XHTML. La liste des lments du combo des employs est fournie par une mthode
du bean [Form]. Ecrire cette mthode. Les lments affichs dans le combo auront leur proprit itemValue gale au n SS d'un
employ et la proprit itemLabel sera une chane forme du prnom et du nom de celui-ci.

Travail faire : comment doit tre initialis le bean [SessionData] pour que, lors de la requte GET initiale faite au formulaire, le
menu de l'entte soit celui montr ci-dessus ?

11.5.2

L'action [faireSimulation]

Le code JSF de l'action [faireSimulation] est le suivant :


<h:commandLink id="cmdFaireSimulation" value="#{msg['[Link]']}" action="#{[Link]}"
rendered="#{[Link]}"/>

L'action [faireSimulation] calcule une feuille de salaire :

A partir de la page prcdente, on obtient le rsultat suivant :

253/290

La simulation est affiche avec 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:f="[Link]
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:define name="part2">
13.
<h:outputText value="#{msg['[Link]']}" styleClass="titreInfos"/>
14.
<br/><br/>
15.
<h:panelGrid columns="3" rowClasses="libelle,info">
16.
<h:outputText value="#{msg['[Link]']}"/>
17.
<h:outputText value="#{msg['[Link]']}"/>
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]']}"/>

254/290

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]']}"/>
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>

ligne 8, la page [[Link]] s'insre dans la page [[Link]],


lignes 9-11 : la zone des saisies est affiche dans le fragment part1 de la page layout,
lignes 12-91 : la simulation est affiche dans le fragment part2 de la page layout.

Travail faire : crire la mthode [faireSimulation] de la classe [Form]. La simulation sera enregistre dans le bean SessionData.

11.5.3

La gestion des erreurs

On veut pouvoir grer proprement les exceptions qui peuvent survenir lors du calcul d'une simulation. Pour cela le code de la
mthode [faireSimulation] utilisera un try / catch :
1. // action du menu
2. public String faireSimulation(){
3.
try{
4.
// on calcule la feuille de salaire
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 prcdentes
12.
...
13.
// on cre la nouvelle liste des erreurs

255/290

14.
15.
16.
17.
18.
19.
20.
21.
22.}

...
// on affiche la vue vueErreur
...
// on met jour le menu
...
// on affiche la vue erreur
return "erreurs";
}

La liste des erreurs cre ligne 13 est la suivante :


1. // le modle des vues
2. ...
3. private List<Erreur> erreurs=new ArrayList<Erreur>();
4. ...

La classe Erreur est dfinie comme suit :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.

package [Link];
public class Erreur {
public Erreur() {
}
// champ
private String classe;
private String message;
// constructeur
public Erreur(String classe, String message){
[Link](classe);
[Link]=message;
}
// getters et setters
...
}

Les erreurs seront des exceptions dont on mmorise le nom de la classe dans le champ classe et le message dans le champ
message.
La liste des erreurs construite dans la mthode [faireSimulation] est constitue de :

l'exception initiale th de type Throwable qui s'est produite,

de sa cause [Link]() si elle en a une,

de la cause de la cause [Link]().getCause() si elle existe.

...
Voici un exemple de liste d'erreurs :

Ci-dessus, l'employ [Z Y] n'existe pas dans le dictionnaire des employs utilis par la couche [mtier] simule. Dans ce cas, la
couche [mtier] simule lance une exception (ligne 6 ci-dessous) :
1. public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int nbJoursTravaills) {
2.
// on rcupre 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.
}
8. ...
9. }

256/290

Le rsultat obtenu est le suivant :

Cette vue est affiche par la page [[Link]] suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.

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


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[Link]
<html xmlns="[Link]
xmlns:h="[Link]
xmlns:f="[Link]
xmlns:ui="[Link]
<ui:composition template="[Link]">
<ui:define name="part1">
<h3><h:outputText value="#{msg['[Link]']}"/></h3>
<h:dataTable value="#{[Link]}" var="erreur"
headerClass="erreursHeaders" columnClasses="erreurClasse,erreurMessage">
<f:facet name="header">
<h:outputText value="#{msg['[Link]']}"/>
</f:facet>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['[Link]']}"/>
</f:facet>
<h:outputText value="#{[Link]}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['[Link]']}"/>
</f:facet>
<h:outputText value="#{[Link]}"/>
</h:column>
</h:dataTable>
</ui:define>
</ui:composition>
</html>

Travail faire : complter la mthode [faireSimulation] afin que lors d'une exception, elle fasse afficher la vue [vueErreur].

11.5.4

L'action [effacerSimulation]

L'action [effacerSimulation] permet l'utilisateur de retrouver un formulaire vide :

Le code JSF du lien [effacerSimulation] est le suivant :


<h:commandLink id="cmdEffacerSimulation" onclick="raz()" value="#{msg['[Link]']}"
action="#{[Link]}" rendered="#{[Link]}"/>

257/290

Un clic sur le lien [EffacerSimulation] provoque d'abord l'appel de la fonction Javascript raz(). Cette mthode est dfinie dans la
page [[Link]] :
1.
2.
3.
4.
5.
6.
7.
8.

<script type="text/javascript">
function raz(){
// on change les valeurs postes
[Link]['formulaire'].elements['formulaire:comboEmployes'].value="0";
[Link]['formulaire'].elements['formulaire:heuresTravaillees'].value="0";
[Link]['formulaire'].elements['formulaire:joursTravailles'].value="0";
}
</script>

Les lignes 4-6 changent les valeurs postes. On notera que :

les valeurs postes sont des valeurs valides, c.a.d. qu'elles passeront les tests de validation des champs de saisie
heuresTravaillees et joursTravailles ;

la fonction raz ne poste pas le formulaire. En effet, celui-ci va tre post par le lien cmdEffacerSimulation. Ce post se fera
aprs excution de la fonction Javascript raz ;
Au cours du post, les valeurs postes vont suivre un cheminement normal : validation puis affectation aux champs du modle. Ceuxci sont les suivants dans la classe [Form] :
// le modle des vues
private String comboEmployesValue;
private String heuresTravailles;
private String joursTravaills;
...

Ces trois champs vont recevoir les trois valeurs postes {"0","0","0"}. Une fois cette affectation opre, la mthode effacerSimulation
va tre excute.
Travail faire : crire la mthode [effacerSimulation] de la classe [Form]. On fera en sorte que :
- seule la zone des saisies soit affiche,
- le combo soit positionn sur son 1er lment,
- les zones de saisie heuresTravaillees et joursTravailles affichent des chanes vides.

11.5.5

L'action [enregistrerSimulation]

Le code JSF du lien [enregistrerSimulation] est le suivant :


<h:commandLink id="cmdEnregistrerSimulation" immediate="true" value="#{msg['[Link]']}"
action="#{[Link]}" rendered="#{[Link]}"/>

L'action [enregistrerSimulation] associe 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>();

La classe Simulation est la suivante :


1. package [Link];
2.
3. import [Link];
4.
5. public class Simulation {
6.
7.
public Simulation() {
8.
}
9.
10.
// champs d'une simulation
11.
private Integer num;
12.
private FeuilleSalaire feuilleSalaire;
13.
private String heuresTravailles;
14.
private String joursTravaills;
15.
16.
// constructeur
17.
public Simulation(Integer num,String heuresTravailles, String joursTravaills, FeuilleSalaire feuilleSalaire){
18.
[Link](num);
19.
[Link](feuilleSalaire);
20.
[Link](heuresTravailles);
21.
[Link](joursTravaills);
22.
}
23.
24.
public double getIndemnites(){

258/290

25.

return [Link]().getIndemnitesEntretien()+
[Link]().getIndemnitesRepas();
26.
}
27.
28.
// getters et setters
29. ...
30. }

Cette classe permet de mmoriser une simulation faite par l'utilisateur :

ligne 11 : le n de la simulation,

ligne 12 : la feuille de salaire qui a t calcule,

ligne 13 : le nombre d'heures travailles,

ligne 14 : le nombre de jours travaills.


Voici un exemple d'enregistrement :

A partir de la page prcdente, on obtient le rsultat qui suit :

Le n de la simulation est un nombre incrment chaque nouvel enregistrement. Il appartient au bean SessionData :
1.
2.
3.
4.

// simulations
private List<Simulation> simulations = new ArrayList<Simulation>();
private int numDerniereSimulation = 0;
private Simulation simulation;

ligne 2 : le n de la dernire simulation faite.

La mthode [enregistrerSimulation] peut procder ainsi :

rcuprer le n de la dernire simulation dans le bean [SessionData] et l'incrmenter,


ajouter la nouvelle simulation la liste des simulations maintenue par la classe [SessionData],
faire afficher le tableau des simulations :

259/290

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

260/290

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>
75. </html>

ligne 8, la page [[Link]] s'insre l'intrieur de la page [[Link]] ;


ligne 9, la place du fragment nomm part1 ;
ligne 11, la balise <h:dataTable> utilise le champ #{[Link]} comme source de donnes, c.a.d. le champ
suivant :

1. // simulations
2. private List<Simulation> simulations;

- l'attribut var="simulation" fixe le nom de la variable reprsentant la simulation courante l'intrieur 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 :

Le code JSF de la colonne Nom est le suivant :


1.
2.
3.
4.
5.
6.

<h:column>
<f:facet name="header">
<h:outputText value="#{msg['[Link]']}"/>
</f:facet>
<h:outputText value="#{[Link]}"/>
</h:column>

lignes 2-4 : la balise <f:facet name="header"> dfinit le titre de la colonne ;


ligne 5 : le nom de l'employ est crit :

simulation fait rfrence l'attribut var de la balise <h:dataTable ...> :

<h:dataTable value="#{[Link]}" var="simulation" ...>

simulation dsigne la simulation courante de la liste des simulations : d'abord la 1re, puis la 2me, ... ;
[Link] fait rfrence au champ feuilleSalaire de la simulation courante ;
[Link] fait rfrence au champ employe du champ feuilleSalaire ;
[Link] fait rfrence au champ nom du champ employe ;

La mme technique est rpte pour toutes les colonnes du tableau. Il y a une difficult pour la colonne Indemnits qui est
gnre avec le code suivant :
1.
2.
3.
4.
5.
6.

<h:column>
<f:facet name="header">
<h:outputText value="#{msg['[Link]']}"/>
</f:facet>
<h:outputText value="#{[Link]}"/>
</h:column>

261/290

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 mthode [Link](). Il suffit donc que
cette mthode existe. Le champ indemnites peut lui ne pas exister. La mthode getIndemnites doit rendre le total des indemnits de
l'employ. Cela ncessite un calcul intermdiaire car ce total n'est pas disponible directement dans la feuille de salaire. La mthode
getIndemnites est donne page 258.
Travail faire : crire la mthode [enregistrerSimulation] de la classe [Form].

11.5.6

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] associe au lien permet l'utilisateur de revenir de la vue [vueSimulations] la vue [vueSaisies] :

Le rsultat obtenu :

Travail faire : crire la mthode [retourSimulateur] de la classe [Form]. Le formulaire de saisie prsent doit tre vide comme cidessus.

11.5.7

L'action [voirSimulations]

Le code JSF du lien [voirSimulations] est le suivant :


<h:commandLink id="cmdVoirSimulations" immediate="true" value="#{msg['[Link]']}" action="#{[Link]}"
rendered="#{[Link]}"/>

L'action [voirSimulations] associe au lien permet l'utilisateur d'avoir le tableau des simulations, ceci quelque soit l'tat de ses
saisies :

262/290

Le rsultat obtenu :

Travail faire : crire la mthode [voirSimulations] de la classe [Form].


On fera en sorte que si la liste des simulations est vide, la vue affiche soit [vueSimulationsVides] :

Pour afficher la vue ci-dessus, on utilisera la page [[Link]] suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.

11.5.8

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


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[Link]
<html xmlns="[Link]
xmlns:h="[Link]
xmlns:f="[Link]
xmlns:ui="[Link]
<ui:composition template="[Link]">
<ui:define name="part1">
<h2>Votre liste de simulations est vide.</h2>
</ui:define>
</ui:composition>
</html>

L'action [retirerSimulation]

L'utilisateur peut retirer des simulations de sa liste :

263/290

Le rsultat obtenu est le suivant :

Si ci-dessus, on retire la dernire simulation, on obtiendra le rsultat suivant :

Le code JSF de la colonne [Retirer] du tableau des simulations est le suivant :


1.
2.
3.
4.
5.
6.
7.
8.
9.

1.
2.
3.

<h:dataTable value="#{[Link]}" var="simulation"


headerClass="simulationsHeaders"
columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisationsSociales,simuSalaire
Net">
...
<h:column>
<h:commandLink value="Retirer" action="#{[Link]}">
<f:setPropertyActionListener target="#{[Link]}" value="#{[Link]}"/>
</h:commandLink>
</h:column>
</h:dataTable>

ligne 5 : le lien [Retirer] est associ la mthode [retirerSimulation] de la classe [Form]. Cette mthode a besoin de
connatre 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 dsigne un champ du modle auquel la valeur de l'attribut
value sera affecte. Ici le n de la simulation retirer #{[Link]} sera affecte au champ
numSimulationToDelete de la classe [Form] :

// le modle des vues


...
private Integer numSimulationToDelete;

Lorsque la mthode [retirerSimulation] de la classe [Form] s'excutera, elle pourra utiliser la valeur qui aura t stocke
auparavant dans le champ numSimulationToDelete.
Travail faire : crire la mthode [retirerSimulation] de la classe [Form].

264/290

11.5.9

L'action [terminerSession]

Le code JSF du lien [Terminer la session] est le suivant :


<h:commandLink id="cmdTerminerSession" immediate="true" value="#{msg['[Link]']}" action="#{[Link]}"
rendered="#{[Link]}"/>

L'action [terminerSession] associe au lien permet l'utilisateur d'abandonner sa session et de revenir au formulaire de saisies vide :

Si l'utilisateur avait une liste de simulations, celle-ci est vide. Par ailleurs, la numrotation des simulations repart 1.
Travail faire : crire la mthode [terminerSession] de la classe [Form].

11.6

Intgration de la couche web dans une architecture 3 couches JSF / EJB

L'architecture de l'application web prcdente tait la suivante :

Application web
couche [web]
1

Faces Servlet

2a
3

JSF1
JSF2
JSFn

2b

[MC]
[Link]
Modle M
Gestionnaires
d'vts

couche
[metier]
simule

Nous remplaons la couche [mtier] simule, par les couches [mtier, DAO, JPA] implmentes par des EJB au paragraphe 6.1, page
165 :

265/290

Application web
couche [web]
1

2a

Faces Servlet

JSF1
JSF2
JSFn

2b

[MC]
[Link]
Modle M
Gestionnaires
d'vts

couche
[metier, DAO, JPA]

Travail faire : raliser l'intgration des couches JSF et EJB en suivant la mthodologie du paragraphe 10, page 235.

Note : il vous faut modifier le code du bean [[Link]] :


1.
2.
3.
4.
5.
6.
7.
8.

@Named
@ApplicationScoped
public class ApplicationData implements Serializable {
// couche mtier
@EJB
private IMetierLocal metier;
...

La ligne 7 instanciait la couche [mtier] simule. Dsormais elle doit rfrencer la couche [mtier] relle. L'annotation @EJB de la
ligne 6 indique au conteneur de servlets qui va excuter la couche web, d'injecter dans le champ metier de la ligne 7, l'EJB qui
implmente l'interface locale IMetierLocal.

266/290

12 Versions 8 : Portages de l'application JSF multipages


12.1

Environnement Spring / Hibernate / Tomcat

Nous portons maintenant l'application prcdente dans un environnement Spring / Hibernate / Tomcat :

Application web
couche [web]
1

Faces Servlet

2a
3

JSF1
JSF2
JSFn

2b

[MC]
[Link]
Modle M
Gestionnaires
d'vts

couche
[mtier, DAO, JPA]

Serveur Tomcat
Nous recopions le projet Maven [mv-pam-jsf2-alone-multipages] dans le projet [mv-pam-jsf2-multipages-spring-hibernate-tomcat]
[1] :

en [2], nous supprimons les packages [exception, jpa, metier] ;


en [3], nous rcuprons ces packages en ajoutant au projet une dpendance sur le projet [mv-pam-spring-hibernate] tudi
au paragraphe 4.7, page 96 [3-6];

267/290

nous enlevons la dpendance [javaee-web-api.7.0] [7-9] qui est une dpendance pour un projet Glassfish et dont nous
n'avons pas besoin pour un projet Tomcat ;

Nous modifions le fichier [[Link]] [10] de la faon suivante :


1. <project xmlns="[Link] xmlns:xsi="[Link]
2.
xsi:schemaLocation="[Link]
3.
[Link]
4.
5.
<modelVersion>4.0.0</modelVersion>
6.
<groupId>[Link]</groupId>
7.
<artifactId>mv-pam-jsf2-multipages-spring-hibernate-tomcat</artifactId>
8.
<packaging>war</packaging>
9.
<version>1.0-SNAPSHOT</version>
10.
<name>mv-pam-jsf2-multipages-spring-hibernate-tomcat</name>
11.
<url>[Link]
12.
13.
<dependencies>
14.
15.
<!-- Spring Web -->
16.
<dependency>
17.
<groupId>[Link]</groupId>
18.
<artifactId>spring-web</artifactId>
19.
<version>[Link]</version>
20.
</dependency>
21.
22.
<!-- JSF -->
23.
<dependency>
24.
<groupId>[Link]</groupId>
25.
<artifactId>jsf-api</artifactId>
26.
<version>2.2.13</version>
27.
</dependency>
28.
<dependency>
29.
<groupId>[Link]</groupId>
30.
<artifactId>jsf-impl</artifactId>
31.
<version>2.2.13</version>
32.
</dependency>
33.
34.
<!-- JSR-330 -->
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.

268/290

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 dpendance sur le projet [mv-pam-spring-hibernate] que nous venons de crer ;
lignes 22-32 : les dpendances sur la bibliothque 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 dploye sur Tomcat ;
lignes 34-39 : l'annotation [@Inject] qui permet d'injecter un bean JSF dans un autre bean JSF ncessite la bibliothque
[[Link]] de la ligne 37 ;
lignes 16-20 : la dpendance 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 enlve, l'application de
marche plus. Je n'ai pas d'explications ;

Nous corrigeons maintenant les erreurs de syntaxe dans le code Java :

Commenons par la classe [ApplicationData] [12-15] :

ligne 12 : l'annotation [@ApplicationScoped] appartient un package [13] qui n'existe plus ;

ligne 16 : l'interface [IMetierLocal] n'existe plus [15] ;


Nous faisons voluer le code de la faon suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.

package [Link];
import
import
import
import
import
import
import

[Link];
[Link];
[Link];
[Link];
[Link];
[Link];
[Link];

@Named
@Scope("application")
public class ApplicationData implements Serializable {
// couche mtier
@Autowired
private IMetier metier;
// logger
private final boolean logsEnabled = true;
private static final Logger logger = [Link]("pam");

ligne 12 : l'annotation JSF [@ApplicationScoped] est remplace par l'annotation Spring (ligne 9) [@Scope('application')]
indique que le bean JSF est de porte [Application] ;

269/290

ligne 17 : l'interface [IMetierLocal] est remplace par l'interface [IMetier] dfinie dans le projet [mv-pam-springhibernate] ;
ligne 16 : l'annotation Spring [@Autowired] va nous permettre d'injecter ligne 17, une rfrence sur le bean de type
[IMetier] dfini dans le projet [mv-pam-spring-hibernate] ;

Nous pouvons passer maintenant la classe [Form] :

La ligne 17 errone ci-dessus est modifie comme indiqu en [18].


Nous terminons par la classe [SessionData] :

la ligne errone 14 ci-dessus est corrige comme indiqu en [21] ;

Notre projet n'a dsormais plus d'erreurs Java. Il nous reste configurer l'intgration Spring / JSF. Cela se passe essentiellement
dans le dossier [WEB-INF] :

270/290

Le fichier [[Link]] configure l'application web. Il tait jusqu' maintenant entirement configur pour JSF. Il volue dsormais de
la faon suivante :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.

<?xml version="1.0" encoding="UTF-8"?>


<web-app version="3.1" xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
<!-- listener Spring -->
<listener>
<listener-class>
[Link]
</listener-class>
</listener>
<!-- la servlet FacesServlet -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>[Link]</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- forme des URL traites -->
...
</web-app>

les lignes 9-17 restent ce qu'elles taient ;


lignes 4-8 : on ajoute un listener. Un listener est une classe charge en mme temps que la servlet qui traite les URL
demandes,
ici
la
servlet
[FacesServlet]
des
lignes
10-14.
Le
listener
[[Link]] est une classe Spring qui va exploiter par dfaut un fichier
[WEB-INF / [Link]] qui n'existe pas encore. Nous allons donc devoir l'ajouter ;

Le fichier [WEB-INF / [Link]] est exploit au dmarrage de l'application web par le listener Spring
[ContextLoaderListener] :

Son contenu sera le suivant :


1. <beans xmlns="[Link]
2.
xmlns:xsi="[Link]
3.
xmlns:context="[Link]
4.
xsi:schemaLocation="[Link]
5.
[Link]
6. [Link]
7. [Link]
8.
9.
<!-- beans JSF -->
10.
<context:component-scan base-package="[Link]" />
11.
12.
<!-- couches basses -->
13.
<import resource="classpath:[Link]" />
14.

271/290

15. </beans>

ligne 10 : on indique au listener Spring de chercher les beans JSF dans le package [[Link]]. Il va y trouver les classes
annotes par [@Scope('porte_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 [mtier] (lignes 6-9) ci-dessous :

1.
2.
3.
4.
5.
6.
7.
8.
9.

<!-- dao -->


<bean id="employeDao" class="[Link]"/>
<bean id="indemniteDao" class="[Link]"/>
<bean id="cotisationDao" class="[Link]"/>
<!-- mtier -->
<bean id="metier" class="[Link]">
<property name="employeDao" ref="employeDao"/>
<property name="cotisationDao" ref="cotisationDao"/>
</bean>

Dans la classe [ApplicationData], nous avons crit :


1.
2.
3.
4.
5.
6.
7.

@Named
@Scope("application")
public class ApplicationData implements Serializable {
// couche mtier
@Autowired
private IMetier metier;

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 honore par Spring qui va injecter en ligne 7 le seul bean implmentant
l'interface [IMetier], savoir celui dfini aux lignes 6-9 ci-dessus du fichier [[Link]] qui configure les couches
basses de l'application web.
Il reste un dernier dtail. Dans les pages xhtml, nous trouvons des annotations comme les suivantes :
1.
2.
3.
4.

<h:commandLink id="cmdFaireSimulation"
value="#{msg['[Link]']}"
action="#{[Link]}"
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 ;

Pour que ces expressions soient reconnues, il faut modifier le fichier [[Link]] de la faon suivante :
1.
2.
3.
4.
5.
6.

<?xml version="1.0" encoding="UTF-8"?>


<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="[Link]
xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]

7.
8.
<!-- le fichier des messages -->
9.
<application>
10.
<application>
11.
<!-- pour que Spring puisse grer 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 rsolues par un programme appel rsolveur La ligne 12 permet de remplacer le
rsolveur par dfaut de JSF par un rsolveur Spring ;

Voil. On peut excuter l'application web. On modifie ses proprits pour qu'elle s'excute sur le serveur Tomcat :

272/290

12.2

Environnement Spring / EclipseLink / Tomcat

Travail faire : portez le travail prcdent dans un environnement Spring / EclipseLink / Tomcat.
Notes : vous pouvez procder de la faon suivante :

273/290

12.3

dupliquer le projet [mv-pam-jsf2-multipages-spring-hibernate-tomcat] dans un projet [mv-pam-jsf2-multipages-springeclipselink-tomcat] ;


dans le nouveau projet, remplacer la dpendance [mv-pam-spring-hibernate] par celle [mv-pam-spring-eclipselink] du
paragraphe 4.12, page 137 ;

Environnement Spring / EclipseLink / Glassfish

Nous allons porter l'application web Spring / JSF du serveur Tomcat vers le serveur Glassfish. Nous rappelons que l'application
dveloppe au paragraphe 11, page 241, l'avait t dans un environnement JSF / EJB / Glassfish. Nous remplaons la technologie
EJB par une technologie Spring.

Application web
couche [web]
1

Faces Servlet

2a
3

JSF1

2b

[MC]
[Link]

couche
[mtier, DAO, JPA]

Modle M
Gestionnaires
d'vts

JSF2
JSFn

Serveur EE Glassfish
12.3.1

Etape 1

Nous allons implmenter les couches basses de l'application web [mtier, DAO, JPA] :

Application web
couche [web]
1

Faces Servlet

2a
3

JSF1
JSF2
JSFn

2b

[MC]
[Link]
Modle M
Gestionnaires
d'vts

couche
[mtier, DAO, JPA]

Serveur EE Glassfish

274/290

Nous dupliquons le projet [mv-pam-spring-eclipselink] du paragraphe , page , dans le projet [mv-pam-spring-eclipselink-glassfish] :

Ce projet va tre dploy sur le serveur Glassfish pour implmenter les couches basses de l'application web JSF / Spring /
Glassfish. A cause de cela, nous supprimons un certain nombre d'lments inutiles [2-4] :

Le fichier [[Link]] [7] qui configure la couche JPA est actuellement le suivant :
1.
2.
3.
4.
5.
6.
7.
8.

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link] xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
<class>[Link]</class>
<class>[Link]</class>
<class>[Link]</class>
</persistence-unit>
</persistence>

Ce fichier de persistence ne peut tre dploy sur le serveur EE Glassfish car pour celui-ci, le type de transaction, ligne 3, doit tre
obligatoirement JTA. Nous rcuprons le fichier [[Link]] du projet [mv-pam-ejb-metier-dao-eclipselink] (paragraphe
[Link], page 169 :
1.
2.

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link] 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"/>

275/290

12.
</properties>
13.
</persistence-unit>
14. </persistence>

ligne 3, le type de transactions est bien JTA ;

Ligne 3, nous modifions le nom de l'unit de persistance en [mv-pam-spring-glassfish-eclipselinkPU] :


<persistence-unit name="mv-pam-spring-glassfish-eclipselinkPU" transaction-type="JTA">

Le fichier de configuration Spring [spring-config-metier-dao] [8] doit lui aussi tre modifi. Il est actuellement le suivant :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.

<?xml version="1.0" encoding="UTF-8"?>


<beans xmlns="[Link]
xmlns:xsi="[Link]
xmlns:tx="[Link]
xsi:schemaLocation="[Link] [Link]
[Link] [Link]
">
<!-- dao -->
<bean id="employeDao" class="[Link]"/>
<bean id="indemniteDao" class="[Link]"/>
<bean id="cotisationDao" class="[Link]"/>
<!-- mtier -->
<bean id="metier" class="[Link]">
<property name="employeDao" ref="employeDao"/>
<property name="cotisationDao" ref="cotisationDao"/>
</bean>
<!-- configuration JPA -->
<bean id="entityManagerFactory" class="[Link]">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="[Link]">
<property name="databasePlatform" value="[Link]" />
</bean>
</property>
<property name="jpaProperties">
<props>
<!-<prop key="[Link]">drop-and-create</prop>
<prop key="[Link]">FINE</prop>-->
<!-- weaving EclipseLink -->
<prop key="[Link]">static</prop>
<prop key="[Link]">true</prop>
<prop key="[Link]">true</prop>
</props>
</property>
</bean>
<!-- la source de donnees DBCP -->
<bean id="dataSource" class="[Link]" destroy-method="close">
<property name="driverClassName" value="[Link]"/>
<property name="url" value="jdbc:mysql://localhost:3306/dbpam_hibernate"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</bean>
<!-- le gestionnaire de transactions -->
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="[Link]">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- traduction des exceptions -->
<bean class="[Link]"/>
<!-- persistence -->
<bean class="[Link]"/>
</beans>

Parce que la couche de persistance est dsormais gre 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 dfaut par le serveur Glassfish ;
Les lignes 18-43 sont remplaces par les suivantes :
1.
2.

<!-- persistence -->


<jee:jndi-lookup id="entityManagerFactory" jndi-name="persistence/mv-pam-spring-glassfish-eclipselinkPU"/>

la ligne 2 dsigne l'EntityManagerFactory qui gre la couche de persistance. Il a t associ ici l'unit de persistance [mvpam-jpa-glassfish-eclipselinkPU] qui est le nom de l'unit de persistance dans le fichier [[Link]] :

276/290

1.
2.
3.
4.
5.
6.

<?xml version="1.0" encoding="UTF-8"?>


<persistence version="2.1" xmlns="[Link] xmlns:xsi="[Link] xsi:schemaLocation="[Link]
[Link]
<persistence-unit name="mv-pam-jpa-glassfish-eclipselinkPU" transaction-type="JTA">
...
</persistence-unit>
</persistence>

Dans la configuration Spring, le gestionnaire de transactions est actuellement configur par les lignes suivantes :
1.
2.
3.
4.
5.

<!-- le gestionnaire de transactions -->


<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="[Link]">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

ligne 3, la classe [JpaTransactionManager] doit tre chang au profit d'un gestionnaire de transactions JTA, le nouveau type
de transactions dfini dans [persistence;xml] ;

Le code volue de la faon suivante :


1.
2.
3.

<!-- le gestionnaire de transactions -->


<tx:jta-transaction-manager/>
<tx:annotation-driven/>

ligne 2 : un gestionnaire de transactions JTA ;


ligne 3 : pour grer les annotations [@Transactional] dans le code Java ;

Au final, le fichier [[Link]] complet est le suivant :


1.
2.
3.
4.
5.
6.

<beans xmlns="[Link]
xmlns:xsi="[Link]
xmlns:tx="[Link]
xmlns:jee="[Link]
xsi:schemaLocation="[Link] [Link]
[Link] [Link]
[Link] [Link]
">
<!-- dao -->
<bean id="employeDao" class="[Link]"/>
<bean id="indemniteDao" class="[Link]"/>
<bean id="cotisationDao" class="[Link]"/>
<!-- mtier -->
<bean id="metier" class="[Link]">
<property name="employeDao" ref="employeDao"/>
<property name="cotisationDao" ref="cotisationDao"/>
</bean>

7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
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>

Aprs toutes ces modifications, le fichier [[Link]] peut tre allg de certaines dpendances 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>

277/290

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 dpendance EclipseLink car par dfaut le serveur
Glassfish la fournit ;

A ce stade, nous en avons fini avec le projet [mv-pam-spring-glassfish-eclipselink] qui va implmenter les couches basses de
l'application web.

12.3.2

Etape 2

Nous allons maintenant implmenter la couche web de l'application :

Application web
couche [web]
1

Faces Servlet

2a
3

JSF1
JSF2
JSFn

2b

[MC]
[Link]
Modle M
Gestionnaires
d'vts

couche
[mtier, DAO, JPA]

Serveur EE Glassfish
Nous dupliquons le projet [mv-pam-jsf2-multipages-spring-eclipselink-tomcat] dans le projet [mv-pam-jsf2-multipages-springeclipselink-glassfish] [1] :

278/290

en [2-3], nous supprimons la dpendance [mv-pam-spring-eclipselink] du projet pour la remplacer par la dpendance [mvpam-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.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.

<?xml version="1.0" encoding="UTF-8"?>


<web-app version="3.1" xmlns="[Link] xmlns:xsi="[Link]
xsi:schemaLocation="[Link] [Link]
<!-- listener Spring -->
<listener>
<listener-class>
[Link]
</listener-class>
</listener>
<!-- la servlet FacesServlet -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>[Link]</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- forme des URL traites -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<!-- pour avoir des logs sur les pages affiches, lors du dveloppement -->
<context-param>
<param-name>[Link].PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<!-- pour ignorer les commentaires des pages JSF -->
<context-param>
<param-name>[Link].FACELETS_SKIP_COMMENTS</param-name>

279/290

28.
<param-value>true</param-value>
29.
</context-param>
30.
<!-- dure 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>

On lui ajoute les lignes suivantes :


1.
2.
3.
4.
5.

<!-- persistence -->


<persistence-unit-ref>
<persistence-unit-ref-name>persistence/mv-pam-jpa-glassfish-eclipselinkPU</persistence-unit-ref-name>
<persistence-unit-name>mv-pam-jpa-glassfish-eclipselinkPU</persistence-unit-name>
</persistence-unit-ref>

Sans ces lignes, le nom JNDI [persistence] du code suivant du fichier [[Link]] n'est pas reconnu :
a)
b)

<!-- persistence -->


<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 dfinie dans le fichier [[Link]] du projet des couches basses :
<persistence-unit name="mv-pam-spring-glassfish-eclipselinkPU" transaction-type="JTA">

Le fichier [[Link]] volue de la faon suivante :


1. <project xmlns="[Link] xmlns:xsi="[Link]
2.
xsi:schemaLocation="[Link]
3.
[Link]
4.
5.
<modelVersion>4.0.0</modelVersion>
6.
<groupId>[Link]</groupId>
7.
<artifactId>mv-pam-jsf2-multipages-spring-eclipselink-glassfish</artifactId>
8.
<packaging>war</packaging>
9.
<version>1.0-SNAPSHOT</version>
10.
<name>mv-pam-jsf2-multipages-spring-eclipselink-glassfish</name>
11.
<url>[Link]
12.
13.
<dependencies>
14.
15.
<!-- Spring Web -->
16.
<dependency>
17.
<groupId>[Link]</groupId>
18.
<artifactId>spring-web</artifactId>
19.
<version>[Link]</version>
20.
</dependency>
21.
22.
<!-- JSF -->
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>

280/290

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 dpendance ainsi annote n'est pas incluse
dans l'archive war du projet. C'est ainsi parce que ces trois dpendances peuvent tre fournies par Glassfish. Il est donc inutile de les
incorporer dans l'archive du projet dploy sur Glassfish.

12.3.3

Excution

Il nous reste configurer le projet pour qu'il s'excute sur le serveur Glassfish [1-4] :

L'excution du projet donne alors le rsultat suivant :

281/290

282/290

13 Version 9 : Implmentation 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 prcdent
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> :

Une saisie errone :

283/290

Une simulation :

284/290

La liste des simulations utilise les composants <p:dataTable>, <p:commandLink> :

La page patron [[Link]] pourrait tre la suivante :


1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.

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


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[Link]
<html xmlns="[Link]
xmlns:h="[Link]
xmlns:p="[Link]
xmlns:f="[Link]
xmlns:ui="[Link]

la zone [1] de la copie d'cran ci-dessus vient remplacer la ligne 23 ci-dessus, lorsqu'elle est affiche ;

<f:view>
<h:head>
<title><h:outputText value="#{msg['[Link]']}"/></title>
<link type="text/css" rel="stylesheet" href="resources/css/[Link]" />
<link type="text/css" rel="stylesheet" href="resources/css/[Link]" />
</h:head>
<body style="background-image: url('${[Link]}/resources/images/[Link]');">
<h:form id="formulaire">
<!-- contenu -->
<p:outputPanel id="contenu">
<!-- entete -->
<ui:include src="[Link]" />
<p:spacer height="20px" />
<ui:insert name="contenu" >
Gestion des assistantes maternelles
</ui:insert>
</p:outputPanel>
</h:form>
</body>
</f:view>
</html>

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

285/290

17.
18.
19.
20.

<p:menuitem id="cmdRetourSimulateur" style="width: 200px" immediate="true"


value="#{msg['[Link]']}" actionListener="#{[Link]}"
rendered="#{[Link]}" ajax="true" update="contenu"/>
<p:menuitem id="cmdVoirSimulations" style="width: 200px" immediate="true" value="#{msg['[Link]']}"
actionListener="#{[Link]}" rendered="#{[Link]}" ajax="true" update="contenu"/>
</p:submenu>
<p:menuitem id="cmdTerminerSession" style="width: 200px" value="#{msg['[Link]']}"
actionListener="#{[Link]}" rendered="#{[Link]}" ajax="true" update="contenu"/>
</p:menubar>

21.
22.
23. </html>

lignes 10-21 : un menu ;


lignes 12, 13, 14, 17, 18, 19 : les options du menu dclenchent toutes une requte Ajax vers le serveur. Le rsultat HTML
renvoy par celui-ci est plac dans la zone du DOM (Document Object Model) d'identifiant [contenu].

Conseils :

dupliquez le projet [mv-pam-jsf2-multipages-spring-eclipselink-glassfish] puis dans les diffrentes 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 ;

286/290

Table des matires


1 ARCHITECTURE D'UNE APPLICATION JAVA EN COUCHES......................................................................................8
2 LES OUTILS DU DOCUMENT.............................................................................................................................................11
2.1 MAVEN.....................................................................................................................................................................................11
2.1.1 INTRODUCTION.......................................................................................................................................................................11
2.1.2 EXCUTION DU PROJET...........................................................................................................................................................14
2.1.3 LE SYSTME DE FICHIERS D'UN PROJET MAVEN........................................................................................................................16
2.1.4 LE DPT MAVEN LOCAL........................................................................................................................................................16
2.1.5 CHERCHER UN ARTIFACT AVEC MAVEN....................................................................................................................................17
3 JPA EN RSUM....................................................................................................................................................................22
3.1 LA PLACE DE JPA DANS UNE ARCHITECTURE EN COUCHES......................................................................................................22
3.2 JPA EXEMPLES.....................................................................................................................................................................22
3.2.1 EXEMPLE 1 - REPRSENTATION OBJET D'UNE TABLE UNIQUE.....................................................................................................22
[Link] La table [personne]...........................................................................................................................................................22
[Link] L'entit [Personne]............................................................................................................................................................23
3.2.2 CONFIGURATION DE LA COUCHE JPA.......................................................................................................................................26
3.2.3 EXEMPLE 2 : RELATION UN--PLUSIEURS.................................................................................................................................28
[Link] Le schma de la base de donnes......................................................................................................................................28
[Link] Les objets @Entity reprsentant la base de donnes........................................................................................................28
3.3 L'API DE LA COUCHE JPA......................................................................................................................................................30
3.4 LES REQUTES JPQL..............................................................................................................................................................33
3.4.1 LA TABLE [MEDECINS].......................................................................................................................................................33
3.4.2 LA TABLE [CLIENTS]...........................................................................................................................................................34
3.4.3 LA TABLE [CRENEAUX]......................................................................................................................................................34
3.4.4 LA TABLE [RV]......................................................................................................................................................................35
3.4.5 GNRATION DE LA BASE........................................................................................................................................................35
3.4.6 LA COUCHE [JPA]..................................................................................................................................................................37
3.4.7 LE PROJET NETBEANS.............................................................................................................................................................37
3.4.8 GNRATION DE LA COUCHE [JPA].........................................................................................................................................38
[Link] Cration d'une connexion Netbeans la base de donnes................................................................................................38
[Link] Cration d'une unit de persistance...................................................................................................................................39
[Link] Gnration des entits JPA................................................................................................................................................42
[Link] Les entits JPA gnres...................................................................................................................................................44
3.4.9 LE CODE D'ACCS AUX DONNES.............................................................................................................................................51
3.5 LIENS ENTRE CONTEXTE DE PERSISTANCE ET SGBD...............................................................................................................53
3.5.1 L'ARCHITECTURE DE L'APPLICATION.........................................................................................................................................53
3.5.2 LA BASE DE DONNES.............................................................................................................................................................54
3.5.3 LE PROJET MAVEN [MV-PERSONNES].......................................................................................................................................54
3.5.4 CONFIGURATION MAVEN.........................................................................................................................................................54
3.5.5 CONFIGURATION DE LA COUCHE [JPA]......................................................................................................................................55
3.5.6 L'ENTIT JPA [PERSONNE].....................................................................................................................................................55
3.5.7 CRATION DE LA TABLE [PERSONNES]................................................................................................................................56
3.5.8 TESTS DES LIENS ENTRE CONTEXTE DE PERSISTANCE ET BASE DE DONNES...............................................................................57
[Link] La mthode [test1]............................................................................................................................................................59
[Link] La mthode [test2]............................................................................................................................................................61
4 VERSION 1 : ARCHITECTURE SPRING / JPA.................................................................................................................63
4.1 LA BASE DE DONNES...............................................................................................................................................................63
4.2 MODE DE CALCUL DU SALAIRE D'UNE ASSISTANTE MATERNELLE.............................................................................................64
4.3 FONCTIONNEMENT DE L'APPLICATION CONSOLE.....................................................................................................................65
4.4 FONCTIONNEMENT DE L'APPLICATION GRAPHIQUE.................................................................................................................66
4.5 CRATION DE LA BASE DE DONNES.........................................................................................................................................66
4.6 IMPLMENTATION JPA............................................................................................................................................................68
4.6.1 COUCHE JPA / HIBERNATE.....................................................................................................................................................68
[Link] Configuration de la couche JPA........................................................................................................................................69
[Link] Les dpendances...............................................................................................................................................................72
[Link] Les entits JPA..................................................................................................................................................................73
[Link] Le code de la classe principale.........................................................................................................................................74
[Link] Tests..................................................................................................................................................................................75
4.6.2 COUCHE JPA / ECLIPSELINK...................................................................................................................................................80
4.6.3 TRAVAIL FAIRE.....................................................................................................................................................................85
4.6.4 LAZY OU EAGER ?..................................................................................................................................................................85
4.6.5 TRAVAIL FAIRE.....................................................................................................................................................................91

287/290

4.7 LE PROJET MAVEN [MV-PAM-SPRING-HIBERNATE]..................................................................................................................96


4.7.1 CONFIGURATION MAVEN.........................................................................................................................................................97
4.7.2 LA COUCHE [JPA]..................................................................................................................................................................98
4.7.3 LES INTERFACES DES COUCHES [MTIER] ET [DAO]..............................................................................................................102
4.7.4 LA CLASSE [PAMEXCEPTION]................................................................................................................................................108
4.8 LA COUCHE [DAO] DE L'APPLICATION [PAM].....................................................................................................................110
4.8.1 IMPLMENTATION..................................................................................................................................................................111
4.8.2 CONFIGURATION SPRING / JPA..............................................................................................................................................111
4.8.3 TESTS..................................................................................................................................................................................112
[Link] JUnitInitDB.....................................................................................................................................................................113
[Link] Mise en oeuvre des tests..................................................................................................................................................114
[Link] JUnitDao.........................................................................................................................................................................116
4.9 LA COUCHE [MTIER] DE L'APPLICATION [PAM]..................................................................................................................120
4.9.1 L'INTERFACE JAVA [IMETIER]................................................................................................................................................120
4.9.2 LA CLASSE [FEUILLESALAIRE]..............................................................................................................................................121
4.9.3 LA CLASSE D'IMPLMENTATION [METIER] DE LA COUCHE [METIER].........................................................................................122
4.9.4 TESTS DE LA COUCHE [METIER].............................................................................................................................................123
4.10 LA COUCHE [UI] DE L'APPLICATION [PAM] VERSION CONSOLE.......................................................................................125
4.10.1 LA CLASSE [[Link]]..........................................................................................................................................126
4.10.2 EXCUTION........................................................................................................................................................................127
4.11 LA COUCHE [UI] DE L'APPLICATION [PAM] VERSION GRAPHIQUE....................................................................................129
4.11.1 UN RAPIDE TUTORIEL..........................................................................................................................................................130
4.11.2 L'INTERFACE GRAPHIQUE [PAMJFRAME]..............................................................................................................................132
4.11.3 LES VNEMENTS DE L'INTERFACE GRAPHIQUE.....................................................................................................................133
4.11.4 INITIALISATION DE L'INTERFACE GRAPHIQUE.........................................................................................................................134
4.11.5 GESTIONNAIRES D'VNEMENTS..........................................................................................................................................136
4.11.6 EXCUTION DE L'INTERFACE GRAPHIQUE..............................................................................................................................136
4.12 IMPLMENTATION DE LA COUCHE JPA AVEC ECLIPSELINK.................................................................................................137
4.12.1 LE PROJET NETBEANS........................................................................................................................................................137
4.12.2 MISE EN OEUVRE DES TESTS...............................................................................................................................................140
[Link] JUnitDao.......................................................................................................................................................................141
4.12.3 LES AUTRES TESTS..............................................................................................................................................................141
4.12.4 TRAVAIL FAIRE.................................................................................................................................................................142
5 VERSION 2 : ARCHITECTURE OPENEJB / JPA...........................................................................................................143
5.1 INTRODUCTION AUX PRINCIPES DU PORTAGE.........................................................................................................................143
5.1.1 LES NOUVELLES ARCHITECTURES..........................................................................................................................................143
5.1.2 LES BIBLIOTHQUES DES PROJETS..........................................................................................................................................143
5.1.3 CONFIGURATION DE LA COUCHE JPA / ECLIPSELINK / OPENEJB............................................................................................143
5.1.4 IMPLMENTATION DE LA COUCHE [DAO] PAR DES EJB.........................................................................................................144
5.1.5 IMPLMENTATION DE LA COUCHE [METIER] PAR UN EJB........................................................................................................146
5.1.6 LES CLIENTS DES EJB..........................................................................................................................................................147
5.2 TRAVAIL PRATIQUE.................................................................................................................................................................148
5.2.1 CONFIGURATION INITIALE DU PROJET NETBEANS [MV-PAM-OPENEJB-ECLIPSELINK]..................................................................148
5.2.2 PORTAGE DE LA COUCHE [DAO]...........................................................................................................................................150
[Link] L'EJB [CotisationDao]....................................................................................................................................................151
[Link] Les EJB [EmployeDao] et [IndemniteDao]....................................................................................................................152
[Link] La classe [PamException]...............................................................................................................................................152
[Link] Entits srialisables.........................................................................................................................................................153
[Link] Tests de la couche [DAO]...............................................................................................................................................153
5.2.3 PORTAGE DE LA COUCHE [METIER]........................................................................................................................................158
[Link] L'EJB [Metier]................................................................................................................................................................159
[Link] Tests de la couche [metier]..............................................................................................................................................159
5.2.4 PORTAGE DE LA COUCHE [CONSOLE].....................................................................................................................................161
5.2.5 PORTAGE DE LA COUCHE [SWING]..........................................................................................................................................163
5.3 CONCLUSION..........................................................................................................................................................................164
6 VERSION 3 : PORTAGE DE L'APPLICATION PAM SUR UN SERVEUR D'APPLICATIONS GLASSFISH.......165
6.1 LA PARTIE SERVEUR DE L'APPLICATION CLIENT / SERVEUR PAM.........................................................................................165
6.1.1 L'ARCHITECTURE DE L'APPLICATION.......................................................................................................................................165
[Link] Le projet Netbeans..........................................................................................................................................................166
[Link] Configuration Maven......................................................................................................................................................167
[Link] Configuration de la couche de persistance......................................................................................................................169
[Link] Insertion des couches [JPA, DAO, metier].....................................................................................................................173
[Link] Dploiement de l'application sur le serveur Glassfish....................................................................................................173

288/290

6.1.2 CLIENT CONSOLE - VERSION 1...............................................................................................................................................175


6.1.3 CLIENT CONSOLE - VERSION 2...............................................................................................................................................180
6.1.4 LE CLIENT SWING.................................................................................................................................................................184
6.1.5 PORTAGE DES TESTS UNITAIRES.............................................................................................................................................184
7 VERSION 4 CLIENT / SERVEUR DANS UNE ARCHITECTURE DE SERVICE WEB SOAP..............................188
7.1 SERVICE WEB SOAP IMPLMENT PAR UN EJB...................................................................................................................189
7.1.1 LA PARTIE SERVEUR..............................................................................................................................................................189
7.1.2 LA PARTIE CLIENTE...............................................................................................................................................................192
[Link] Le projet Netbeans du client console..............................................................................................................................192
[Link] Le client console du service web Metier.........................................................................................................................193
[Link] Le client swing du service web Metier............................................................................................................................196
7.2 SERVICE WEB SOAP IMPLMENT PAR UNE APPLICATION WEB............................................................................................196
7.2.1 LA PARTIE SERVEUR..............................................................................................................................................................196
7.2.2 LA PARTIE CLIENTE...............................................................................................................................................................201
7.3 SERVICE WEB SOAP IMPLMENT AVEC SPRING, HIBERNATE ET TOMCAT..........................................................................201
7.3.1 LA PARTIE SERVEUR..............................................................................................................................................................202
7.3.2 LA PARTIE CLIENTE...............................................................................................................................................................207
7.4 SERVICE WEB SOAP IMPLMENT AVEC SPRING, ECLIPSELINK ET TOMCAT.......................................................................209
7.5 CONCLUSION..........................................................................................................................................................................215
8 INTRODUCTION JAVA SERVER FACES.....................................................................................................................217
9 VERSION 5 - APPLICATION PAM WEB / JSF................................................................................................................218
9.1 ARCHITECTURE DE L'APPLICATION........................................................................................................................................218
9.2 FONCTIONNEMENT DE L'APPLICATION...................................................................................................................................219
9.3 LE PROJET NETBEANS...........................................................................................................................................................221
9.3.1 LES FICHIERS DE CONFIGURATION..........................................................................................................................................226
9.3.2 LA FEUILLE DE STYLE...........................................................................................................................................................228
9.3.3 LE FICHIER DES MESSAGES....................................................................................................................................................228
9.3.4 LA PORTE DES BEANS..........................................................................................................................................................229
9.3.5 LA COUCHE [MTIER]...........................................................................................................................................................230
9.4 LE FORMULAIRE [[Link]] ET SON MODLE [[Link]]...........................................................................................231
9.4.1 TAPE 1...............................................................................................................................................................................231
9.4.2 TAPE 2...............................................................................................................................................................................232
9.4.3 TAPE 3...............................................................................................................................................................................232
9.4.4 TAPE 4...............................................................................................................................................................................233
10 VERSION 6 - INTGRATION DE LA COUCHE WEB DANS UNE ARCHITECTURE 3 COUCHES JSF / EJB. 235
10.1 ARCHITECTURE DE L'APPLICATION......................................................................................................................................235
10.2 LE PROJET NETBEANS DE LA COUCHE WEB.........................................................................................................................235
10.3 LE PROJET NETBEANS DE L'APPLICATION D'ENTREPRISE....................................................................................................237
11 VERSION 7 - APPLICATION WEB PAM MULTI-VUES / MULTI-PAGES...............................................................241
11.1 LES VUES DE L'APPLICATION................................................................................................................................................242
11.2 LE PROJET NETBEANS DE LA COUCHE [WEB].......................................................................................................................243
11.2.1 LES FICHIERS DE CONFIGURATION........................................................................................................................................244
11.2.2 LA FEUILLE DE STYLE.........................................................................................................................................................244
11.2.3 LE FICHIER DES MESSAGES..................................................................................................................................................245
11.2.4 LA COUCHE [MTIER].........................................................................................................................................................246
11.3 LES BEANS DE L'APPLICATION..............................................................................................................................................247
11.3.1 LE BEAN APPLICATIONDATA................................................................................................................................................247
11.3.2 LE BEAN SESSIONDATA......................................................................................................................................................248
11.3.3 LE BEAN FORM..................................................................................................................................................................249
11.4 LES PAGES DE L'APPLICATION..............................................................................................................................................250
11.4.1 [[Link]]................................................................................................................................................................250
11.4.2 L'ENTTE [[Link]].................................................................................................................................................251
11.5 LES CAS D'UTILISATION DE L'APPLICATION..........................................................................................................................252
11.5.1 AFFICHAGE DE LA PAGE D'ACCUEIL......................................................................................................................................252
11.5.2 L'ACTION [FAIRESIMULATION].............................................................................................................................................253
11.5.3 LA GESTION DES ERREURS...................................................................................................................................................255
11.5.4 L'ACTION [EFFACERSIMULATION].........................................................................................................................................257
11.5.5 L'ACTION [ENREGISTRERSIMULATION].................................................................................................................................258
11.5.6 L'ACTION [RETOURSIMULATEUR].........................................................................................................................................262
11.5.7 L'ACTION [VOIRSIMULATIONS]............................................................................................................................................262
11.5.8 L'ACTION [RETIRERSIMULATION].........................................................................................................................................263
11.5.9 L'ACTION [TERMINERSESSION]............................................................................................................................................265
11.6 INTGRATION DE LA COUCHE WEB DANS UNE ARCHITECTURE 3 COUCHES JSF / EJB.........................................................265

289/290

12 VERSIONS 8 : PORTAGES DE L'APPLICATION JSF MULTIPAGES......................................................................267


12.1 ENVIRONNEMENT SPRING / HIBERNATE / TOMCAT.............................................................................................................267
12.2 ENVIRONNEMENT SPRING / ECLIPSELINK / TOMCAT..........................................................................................................273
12.3 ENVIRONNEMENT SPRING / ECLIPSELINK / GLASSFISH......................................................................................................274
12.3.1 ETAPE 1.............................................................................................................................................................................274
12.3.2 ETAPE 2.............................................................................................................................................................................278
12.3.3 EXCUTION........................................................................................................................................................................281
13 VERSION 9 : IMPLMENTATION DE LA COUCHE WEB AVEC PRIMEFACES.................................................283

290/290

Vous aimerez peut-être aussi