Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Javaee 2

Télécharger au format pdf ou txt
Télécharger au format pdf ou txt
Vous êtes sur la page 1sur 257

Introduction Java EE

avec Netbeans
et le serveur d'applications Glassfish

serge.tahe at istia.univ-angers.fr
juin 2012

1/257

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 [http://tahe.developpez.com/java/primefaces]. 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 [http://www.antoniogoncalves.org] 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 disponibles pour crer des applications trois couches. Une tandem trs populaire est
Spring (http://www.springframework.org/) / Hibernate (http://www.hibernate.org/). 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
[http://www.istia.univ-angers.fr]. 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 [http://www.netbeans.org/].
Le document fait rfrence aux cours suivants :

2/257

1.
2.
3.

Persistance Java 5 par la pratique : [http://tahe.developpez.com/java/JPA] - donne les outils pour construire la couche
d'accs aux donnes avec JPA (Java Persistence API)
Introduction au langage Java [http://tahe.developpez.com/java/cours] - pour les dbutants
Introduction par l'exemple Java Server Faces, Primefaces et Primefaces mobile
[http://tahe.developpez.com/java/primefaces]

Ces supports de cours sont par la suite rfrencs [ref1] [ref2] et [ref3].
Ce document est accompagn d'un support disponible l'adresse [http://tahe.ftp-developpez.com/fichiers-archive/javaee2support.zip] contenant des scripts SQL permettant de construire les bases de donnes utilises dans le document, des fichiers
[pom.xml] parfois difficiles construire, les trois rfrences de cours ci-dessus ainsi que les squelettes de certaines des applications
du document. Il contient galement un planning donnant une estimation de temps des diffrentes tches proposes.
Serge Tah, juin 2012.

3/257

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, ...) et qui enregistre certains des rsultats fournis par la couche [2].

Il existe diffrentes possibilits pour implmenter la couche [DAO]. Examinons-en quelques-unes :

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

4/257

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, EclipseLink, 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 2.8 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) :

5/257

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 oeuvre, tester et parfois 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'application en automatisant le dploiement des EJB sur le serveur :
Eclipse, Netbeans, JDeveloper, IntelliJ IDEA. Nous utiliserons ici Netbeans 6.8 et le serveur d'application Glassfish v3.
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 :

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

6/257

2 Les outils du document


Les exemples du document ont t tests avec les outils suivants :
Netbeans de la version 6.8 la version 7.1.2. L'installation de Netbeans est dcrite dans [ref3] au paragraphe 1.3.1.
Wampserver version 2.2. L'installation de WampServer est 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 [http://maven.apache.org/index.html ]. 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], choisir la catgorie [Maven] et le type de projet [Java Application],

7/257

4
3

en [3], dsigner le dossier parent du dossier du nouveau projet,


en [4], donner un nom au projet,
en [5], le projet gnr.

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

en [1] : les diffrentes branches du projet :

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

en [3], la branche [Source Packages],

Cette branche contient les codes source des classes Java du projet. Netbeans a gnr une classe par dfaut :
1. package istia.st.mvexemple;
2.
3. /**
4. * Hello world!
5. *
6. */
7. public class App {
8.
public static void main(String[] args) {

8/257

9.
System.out.println("Hello World!");
10.
}
11. }

en [4], la branche [Test Packages] qui contient les codes source des classes de test du projet,
en [5], la bibliothque JUnit 3.8 ncessaire l'excution des tests,

Netbeans a gnr une classe par dfaut :


1. package istia.st.mvexemple;
2.
3. import junit.framework.Test;
4. import junit.framework.TestCase;
5. import junit.framework.TestSuite;
6.
7. /**
8. * Unit test for simple App.
9. */
10. public class AppTest extends TestCase {
11.
/**
12.
* Create the test case
13.
*
14.
* @param testName
15.
*
name of the test case
16.
*/
17.
public AppTest(String testName) {
18.
super(testName);
19.
}
20.
21.
/**
22.
* @return the suite of tests being tested
23.
*/
24.
public static Test suite() {
25.
return new TestSuite(AppTest.class);
26.
}
27.
28.
/**
29.
* Rigourous Test :-)
30.
*/
31.
public void testApp() {
32.
assertTrue(true);
33.
}
34. }

C'est un test JUnit 3.8. Nous utiliserons par la suite des tests JUnit 4.x.

9/257

en [6], la branche [Dependencies] est ici vide,

Cette branche affiche toute les bibliothques ncessaires au projet et gres par Maven. Toutes les bibliothques listes ici sont
automatiquement tlcharges par Maven. C'est pourquoi un projet Maven a besoin d'un accs Internet. Les bibliothques
tlcharges vont tre stockes en local. Si un autre projet a besoin d'une bibliothque dj prsente en local, celle-ci ne sera alors
pas tlcharge. Nous verrons que cette liste de bibliothques ainsi que les dpts o on peut les trouver sont dfinis dans le fichier
de configuration du projet Maven.

en [7], les bibliothques ncessaires au projet et non gres par Maven,

en [7], le fichier [pom.xml] de configuration du projet Maven. POM signifie Project Object Model. On sera amen
intervenir directement sur ce fichier.

Le fichier [pom.xml] gnr est le suivant :


1. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven4.0.0.xsd">
3.
<modelVersion>4.0.0</modelVersion>
4.
5.
<groupId>istia.st</groupId>
6.
<artifactId>mv-exemple</artifactId>
7.
<version>1.0-SNAPSHOT</version>
8.
<packaging>jar</packaging>
9.
10. <name>mv-exemple</name>
11. <url>http://maven.apache.org</url>
12.

10/257

13. <properties>
14.
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15. </properties>
16.
17. <dependencies>
18.
<dependency>
19.
<groupId>junit</groupId>
20.
<artifactId>junit</artifactId>
21.
<version>3.8.1</version>
22.
<scope>test</scope>
23.
</dependency>
24. </dependencies>
25. </project>

les lignes 5-8 dfinissent l'objet (artifact) Java qui va tre cr par le projet Maven. Ces informations proviennent de
l'assistant qui a t utilis lors de la cration du projet :

Un objet Maven est dfini par quatre proprits :

[groupId] : une information qui ressemble un nom de package. Ainsi les bibliothques du framework Spring ont
groupId=org.springframework, celles du framework JSF ont groupId=javax.faces,
[artifactId] : le nom de l'objet Maven. Dans le groupe [org.springframework] on trouve ainsi les artifactId suivants : springcontext, spring-core, spring-beans, ... Dans le groupe [javax.faces], on trouve l'artifactId JSF-api,
[version] : n de version de l'artifact Maven. Ainsi l'artifact org.springframework.spring-core a les versions suivantes : 2.5.4,
2.5.5, 2.5.6, 2.5.6.SECO1, ...
[packaging] : la forme prise par l'artifact, le plus souvent war ou jar.

Notre projet Maven va gnrer un [jar] (ligne 8) dans le groupe [istia.st] (ligne 5), nomm [mv-exemple] (ligne 6) et de version [1.0SNAPSHOT] (ligne 7). Ces quatre informations doivent dfinir de faon unique un artifact Maven.
Les lignes 17-24 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 comme ici, le packaging jar est utilis. On y ajoute 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 est ncessaire lors des test du projet. C'est le cas ici avec la bibliothque JUnit
3.8.1. Si cette bibliothque n'est pas prsente dans le dpt local du poste, elle est tlcharge.

2.1.2

Excution du projet

Nous excutons le projet :

11/257

1
2

En [1], le projet Maven est construit puis excut [1]. Les logs dans la console Netbeans sont les suivants :
1. Scanning for projects...
2. ...
3.
4. -----------------------------------------------------------------------5. Building mv-exemple 1.0-SNAPSHOT
6. -----------------------------------------------------------------------7.
8. [resources:resources]
9. [debug] execute contextualize
10. Using 'UTF-8' encoding to copy filtered resources.
11. skip non existing resourceDirectory D:\data\istia-1112\netbeans\glassfish\mv-pam\00\mvexemple\src\main\resources
12.
13. [compiler:compile]
14. Compiling 1 source file to D:\data\istia-1112\netbeans\glassfish\mv-pam\00\mvexemple\target\classes
15.
16. [exec:exec]
17. Downloading: http://repo.maven.apache.org/maven2/org/apache/commons/commonsexec/1.0.1/commons-exec-1.0.1.pom
18.
19. Downloaded: http://repo.maven.apache.org/maven2/org/apache/commons/commonsexec/1.0.1/commons-exec-1.0.1.pom (8 KB at 113.5 KB/sec)
20. Downloading: http://repo.maven.apache.org/maven2/org/apache/commons/commonsexec/1.0.1/commons-exec-1.0.1.jar
21.
22. Downloaded: http://repo.maven.apache.org/maven2/org/apache/commons/commonsexec/1.0.1/commons-exec-1.0.1.jar (49 KB at 763.6 KB/sec)
23. Hello World!
24. -----------------------------------------------------------------------25. BUILD SUCCESS
26. -----------------------------------------------------------------------27. Total time: 4.040s
28. Finished at: Thu Jun 21 10:10:40 CEST 2012
29. Final Memory: 13M/122M

Le rsultat est ligne 23. On voit que mme pour ce cas simple, Maven a tlcharg des lments (lignes 17 et 20).

2.1.3

Le systme de fichiers d'un projet Maven

12/257

2
4

1
3

2.1.4

[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 [mv-exemple-1.0-SNAPSHOT.jar].

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 :
2
3

en [1], on choisit l'option [Window / Other / Maven Repository Browser],


en [2], un onglet [Maven Repositories] s'ouvre,
en [3], il contient deux branches, une pour le dpt local, l'autre pour le dpt central. Ce dernier est gigantesque. Pour
visualiser son contenu, il faut mettre jour son index [4]. Cette mise jour dure plusieurs dizaines de minutes.

13/257

6
5

2.1.5

en [5], les bibliothques du dpt local,


en [6], on y trouve une branche [istia.st] qui correspond au [groupId] de notre projet,
en [7], on accde aux proprits du dpt local,
en [8], on a le chemin du dpt local. Il est utile de le connatre car parfois (rarement) Maven n'utilise plus la dernire
version du projet. On fait des modifications et on constate qu'elles ne sont pas prises en compte. On peut alors supprimer
manuellement la branche du dpt local correspondant notre [groupId]. Cela force Maven recrer la branche partir
de la dernire version du projet.

Chercher un artifact avec Maven

Apprenons maintenant chercher un artifact avec Maven. Partons de la liste des dpendances actuelles du fichier [pom.xml] :
1. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven4.0.0.xsd">
3.
<modelVersion>4.0.0</modelVersion>
4.
5.
<groupId>istia.st</groupId>
6.
<artifactId>mv-exemple</artifactId>
7.
<version>1.0-SNAPSHOT</version>
8.
<packaging>jar</packaging>
9.
10. <name>mv-exemple</name>
11. <url>http://maven.apache.org</url>
12.
13. <properties>
14.
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15. </properties>
16.
17. <dependencies>
18.
<dependency>
19.
<groupId>junit</groupId>
20.
<artifactId>junit</artifactId>
21.
<version>3.8.1</version>
22.
<scope>test</scope>
23.
</dependency>
24. </dependencies>
25. </project>

Les lignes 17-23 dfinissent des dpendances qu'on va modifier pour utiliser les bibliothques dans leur version la plus rcente.

14/257

Tout d'abord, nous supprimons les dpendances actuelles [1]. Le fichier [pom.xml] est alors modifi :
1. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven4.0.0.xsd">
3.
<modelVersion>4.0.0</modelVersion>
4.
5.
<groupId>istia.st</groupId>
6.
<artifactId>mv-exemple</artifactId>
7.
<version>1.0-SNAPSHOT</version>
8.
<packaging>jar</packaging>
9.
10. <name>mv-exemple</name>
11. <url>http://maven.apache.org</url>
12.
13. <properties>
14.
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15. </properties>
16.
17. <dependencies></dependencies>
18. </project>

Ligne 17, la dpendance supprime n'apparat plus dans [pom.xml]. Maintenant, recherchons-la dans les dpts Maven.

3
5
6

2
4

en [1], on ajoute une dpendance au projet,


en [2], 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 [jnit-dep]. Nous choisissons
[junit],
en [5], en procdant de la mme faon, on choisit la version la plus rcente. Le type de packaging est jar,
en [6], nous choisissons la porte test pour indiquer que la dpendance n'est ncessaire qu'aux tests.

15/257

En [6], les dpendances ajoutes apparaissent dans le projet. Le fichier [pom.xml] reflte ces changements :
1. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven4.0.0.xsd">
3.
<modelVersion>4.0.0</modelVersion>
4.
5.
<groupId>istia.st</groupId>
6.
<artifactId>mv-exemple</artifactId>
7.
<version>1.0-SNAPSHOT</version>
8.
<packaging>jar</packaging>
9.
10. <name>mv-exemple</name>
11. <url>http://maven.apache.org</url>
12.
13. <properties>
14.
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15. </properties>
16.
17. <dependencies>
18.
<dependency>
19.
<groupId>junit</groupId>
20.
<artifactId>junit</artifactId>
21.
<version>4.10</version>
22.
<scope>test</scope>
23.
<type>jar</type>
24.
</dependency>
25. </dependencies>
26. </project>

On notera que fichier [pom.xml] ne mentionne pas la dpendance [hamcrest-core-1.1] que nous voyons en [6]. Cela parce que c'est
une dpendance de JUnit 4.10 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 [http://mvnrepository.com/] :

En [1], on peut taper des mots cls. Tapons hibernate et lanons la recherche.

16/257

en [2], choisissons le [groupId] org.hibernate et l'[artifactId] hibernate-core,


en [3], choisissons la version 4.1.2-Final,
en [4], nous obtenons le code Maven coller dans le fichier [pom.xml]. Nous le faisons.

1. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
3.
<modelVersion>4.0.0</modelVersion>
4.
5.
<groupId>istia.st</groupId>
6.
<artifactId>mv-exemple</artifactId>
7.
<version>1.0-SNAPSHOT</version>
8.
<packaging>jar</packaging>
9.
10. <name>mv-exemple</name>
11. <url>http://maven.apache.org</url>
12.
13. <properties>
14.
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15. </properties>
16.
17. <dependencies>
18.
<dependency>
19.
<groupId>junit</groupId>
20.
<artifactId>junit</artifactId>
21.
<version>4.10</version>
22.
<scope>test</scope>
23.
<type>jar</type>
24.
</dependency>
25.
<dependency>
26.
<groupId>org.hibernate</groupId>
27.
<artifactId>hibernate-core</artifactId>
28.
<version>4.1.2.Final</version>

17/257

29.
</dependency>
30. </dependencies>
31. </project>

Nous sauvegardons le fichier [pom.xml]. Maven entreprend alors le tlchargement des nouvelles dpendances. Le projet volue
comme suit :

en [5], la dpendance [hibernate-core-4.1.2-Final]. Dans le dpt o il a t trouv, cet [artifactId] est lui aussi dcrit par un
fichier [pom.xml]. 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 [6] 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 [pom.xml]
entre dveloppeurs, on est assur que chaque dveloppeur utilise bien les mmes bibliothques.
Dans les exemples qui suivront, nous nous conterons de donner le fichier [pom.xml] 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.

18/257

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 : [http://tahe.developpez.com/java/JPA] - 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 31 pour crer une application
exemple.

3.1

La place de JPA dans une architecture en couches

Le lecteur est invit relire le dbut de ce document (paragraphe 1, page 4) 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 / ...]
5

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
3.2.1.1

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

19/257

PRENOM

son prnom

DATENAISSANCE

sa date de naissance

MARIE

entier 0 (non mari) ou 1 (mari)

NBENFANTS

nombre d'enfants de la personne

3.2.1.2

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 [Toplink
/ 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. @SuppressWarnings("unused")
4. @Entity
5. @Table(name="Personne")
6. public class Personne implements Serializable{
7.
8. @Id
9. @Column(name = "ID", nullable = false)
10. @GeneratedValue(strategy = GenerationType.AUTO)
11. private Integer id;
12.
13. @Column(name = "VERSION", nullable = false)
14. @Version
15. private int version;
16.
17. @Column(name = "NOM", length = 30, nullable = false, unique = true)
18. private String nom;
19.
20. @Column(name = "PRENOM", length = 30, nullable = false)
21. private String prenom;
22.
23. @Column(name = "DATENAISSANCE", nullable = false)
24. @Temporal(TemporalType.DATE)
25. private Date datenaissance;
26.
27. @Column(name = "MARIE", nullable = false)
28. private boolean marie;
29.
30. @Column(name = "NBENFANTS", nullable = false)
31. private int nbenfants;
32.
33. // constructeurs
34. public Personne() {

20/257

35. }
36.
37. public Personne(String nom, String prenom, Date datenaissance, boolean marie,
38.
int nbenfants) {
39.
setNom(nom);
40.
setPrenom(prenom);
41.
setDatenaissance(datenaissance);
42.
setMarie(marie);
43.
setNbenfants(nbenfants);
44. }
45.
46. // toString
47. public String toString() {
48. ...
49. }
50.
51. // getters and setters
52. ...
53. }

La configuration se fait l'aide d'annotations Java @Annotation. Les annotations Java sont soit 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 Toplink. 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. 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, ...)
2. la table T n'existe pas et elle va 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 :

21/257

Firebird

utilise un gnrateur de valeurs appele avant chaque insertion

SQL server

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 jouele rle d'un gnrateur de valeurs

Oracle

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 = GenerationType.AUTO indique la couche
JPA qu'elle doit utiliser cette stratgie habituelle. Cette technique a fonctionn dans tous les exemples de ce document
pour les sept SGBD utiliss.
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 :
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 enregistre. Ce sera le cas de lutilisateur U1. Sa modification est donc
accepte et on change alors la version de la personne modifie 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 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 TemporalType.DATE dsigne une date seule sans heure associe. Les autres types possibles sont TemporalType.TIME
pour coder une heure et TemporalType.TIMESTAMP 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 /

22/257

3.2.2

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.

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 [persistence.xml]
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/persistence.xml] :

A l'excution, le fichier [META-INF/persistence.xml] est cherch dans le Classpath de l'application.


Examinons la configuration de la couche JPA faite dans le fichier [persistence.xml] de notre projet :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence">
3. <persistence-unit name="JPA" transaction-type="RESOURCE_LOCAL">
4.
<!-- provider -->
5.
<provider>org.hibernate.ejb.HibernatePersistence</provider>
6.
<properties>
7.
<!-- Classes persistantes -->
8.
<property name="hibernate.archive.autodetection" value="class, hbm" />
9.
<!-- logs SQL
10.
<property name="hibernate.show_sql" value="true"/>
11.
<property name="hibernate.format_sql" value="true"/>

23/257

12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.

<property name="use_sql_comments" value="true"/>


-->
<!-- connexion JDBC -->
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/JPA" />
<property name="hibernate.connection.username" value="JPA" />
<property name="hibernate.connection.password" value="JPA" />
<!-- cration automatique du schma -->
<property name="hibernate.hbm2ddl.auto" value="create" />
<!-- Dialecte -->
<property name="hibernate.dialect" value="org.hibernate.dialect.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="hibernate.c3p0.timeout" 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 :

Programme de test
console [main]
3

Couche
[JPA/Hibernate]
4

Pool de
connexions
[c3p0] 5

Couche
[JDBC]
6

Base de
Donnes
7

le fichier [persistence.xml] 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 [persistence.xml] 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 [javax.persistence.spi.PersistenceProvider],
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, Toplink, 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 / Toplink.

24/257

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

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

Ainsi le fichier [persistence.xml] 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
3.2.3.1

Exemple 2 : relation un--plusieurs


Le schma de la base de donnes

25/257

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

3.2.3.2

Les objets @Entity reprsentant la base de donnes

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


1. package entites;
2.
3. ...
4. @Entity
5. @Table(name="jpa05_hb_article")
6. public class Article implements Serializable {
7.
8. // champs
9. @Id
10. @GeneratedValue(strategy = GenerationType.AUTO)
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)

26/257

21. // implmente par une cl trangre (categorie_id) dans Article


22. // 1 Article a ncessairement 1 Categorie (nullable=false)
23. @ManyToOne(fetch=FetchType.LAZY)
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 String.format("Article[%d,%d,%s,%d]", id, version, nom, categorie.getId());
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 rapport 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=FetchType.LAZY, ligne 23). On ne sait pas
si cette demande a un sens. On verra.

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


1. package entites;
2. ...
3. @Entity
4. @Table(name="jpa05_hb_categorie")
5. public class Categorie implements Serializable {
6.
7. // champs
8. @Id
9. @GeneratedValue(strategy = GenerationType.AUTO)
10. private Long id;
11.
12. @SuppressWarnings("unused")
13. @Version
14. private int version;
15.
16. @Column(length = 30)
17. private String nom;
18.
19. // relation inverse Categorie (one) -> Article (many) de la relation Article (many) ->
Categorie (one)
20. // cascade insertion Categorie -> insertion Articles
21. // cascade maj Categorie -> maj Articles
22. // cascade suppression Categorie -> suppression Articles
23. @OneToMany(mappedBy = "categorie", cascade = { CascadeType.ALL })
24. private Set<Article> articles = new HashSet<Article>();
25.
26. // constructeurs
27. public Categorie() {
28. }
29.

27/257

30. // getters et setters


31. ...
32. // toString
33. public String toString() {
34.
return String.format("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.
articles.add(article);
41.
// l'article change de catgorie
42.
article.setCategorie(this);
43. }
44. }

3.3

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 : cascadeType.ALL 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 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 a.equals(b). Dans la
classe Object, mre de toutes les classes, a.equals(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 csa, 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 donc 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. Nous n'aborderons pas ces cas dans ce document. Le lecteur les trouvera dans [ref1].
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].

L'API de la couche JPA

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

28/257

Interface [JPA] =
EntityManager

Client JPA
1

Base de
Donnes
4

Objets image de la BD =
Contexte de persistance

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)

met entity dans le contexte de persistance

void remove(Object entity)

enlve entity du contexte de persistance

<T> T merge(T entity)

fusionne un objet entity du client non gr par le contexte de persistance avec


l'objet entity du contexte de persistance 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.

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


Object primaryKey)
Query createQuery(String queryText)
Query createNativeQuery(String
queryText)
Query createNamedQuery(String name)

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 = Persistence.createEntityManagerFactory("nom d'une unit de
persistance");

javax.persistence.Persistence 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/persistence.xml] 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 [Persistence.createEntityManagerFactory("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.

29/257

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 = emf.createEntityManager();

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 opration SQL DELETE est faite pour le supprimer de la base.
le contexte de persistance est vid de tous ses objets mais pas ferm.

void clear()
void flush()

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 :


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

Rsumons. En mode FlushModeType.AUTO 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 FlushModeType.COMMIT, 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 :

30/257

3
1
2

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.
Iterator iterator = L.iterator();
2.
while (iterator.hasNext()) {
3.
// exploiter l'objet iterator.next() qui reprsente l'lment courant de la liste
4. ...
5. }

La liste L peut tre galement exploite avec un for :


1.
2.
3. }

3.4

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

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.

31/257

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.

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

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.

32/257

NOM : le nom du client


PRENOM : son prnom
TITRE : son titre (Melle, Mme, Mr)

3.4.3

La table [CRENEAUX]

Elle liste les crneaux horaires o les RV sont possibles :

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]

33/257

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 [dbrdvmedecins2.sql] (support du cours). Avec [WampServer], on
pourra procder comme suit :

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

7
4

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,

34/257

9
8

en [8], on importe un fichier SQL,


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

35/257

12
13

11

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,

14

en [14], le contenu de la table.

Par la suite, nous ne reviendrons plus sur cette base. Mais le lecteur est invit suivre son volution au fil des programmes surtout
lorsque a ne marche pas.

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 :

36/257

3
2
1

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


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

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.

3.4.9

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

37/257

6
3

7
4
8

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,

10

3.4.10

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 :

38/257

couche
[DAO]

Couche
[JDBC]

Couche
[JPA /
Hibernate]

SGBD

BD

Nous sommes en train de construire la couche [JPA]. La configuration de celle-ci est faite dans un fichier [persistence.xml] 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],
en [2], crer une unit de persistance,

4
7

5
8

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


en [4], choisir l'implmentation JPA Hibernate (JPA 2.0),
en [5], indiquer que les tables de la BD sont dj cres et que donc on ne les cre pas. On valide l'assistant,
en [6], le nouveau projet,
en [7], le fichier [persistence.xml] a t gnr dans le dossier [META-INF],
en [8], de nouvelles dpendances ont t ajoutes au projet Maven.

Le fichier [META-INF/persistence.xml] gnr ressemble ceci :

39/257

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


2. <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
3.
<persistence-unit name="mv-rdvmedecins-jpql-hibernatePU" transactiontype="RESOURCE_LOCAL">
4.
<provider>org.hibernate.ejb.HibernatePersistence</provider>
5.
<properties>
6.
<property name="javax.persistence.jdbc.url"
value="jdbc:mysql://localhost:3306/dbrdvmedecins2"/>
7.
<property name="javax.persistence.jdbc.password" value=""/>
8.
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
9.
<property name="javax.persistence.jdbc.user" value="root"/>
10.
<property name="hibernate.cache.provider_class"
value="org.hibernate.cache.NoCacheProvider"/>
11.
</properties>
12. </persistence-unit>
13. </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 [persistence.xml] :

14

Pour avoir des logs d'Hibernate, nous compltons le fichier [persistence.xml] de la faon suivante :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
3.
<persistence-unit name="mv-rdvmedecins-jpql-hibernatePU" transactiontype="RESOURCE_LOCAL">
4.
<provider>org.hibernate.ejb.HibernatePersistence</provider>
5.
<properties>
6.
<property name="javax.persistence.jdbc.url"
value="jdbc:mysql://localhost:3306/dbrdvmedecins2"/>
7.
<property name="javax.persistence.jdbc.password" value=""/>

40/257

8.
9.
10.

<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>


<property name="javax.persistence.jdbc.user" value="root"/>
<property name="hibernate.cache.provider_class"
value="org.hibernate.cache.NoCacheProvider"/>
11.
<property name="hibernate.show_sql" value="true"/>
12.
<property name="hibernate.format_sql" value="true"/>
13.
</properties>
14. </persistence-unit>
15. </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.

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


1. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven4.0.0.xsd">
3.
<modelVersion>4.0.0</modelVersion>
4.
5.
<groupId>istia.st</groupId>
6.
<artifactId>mv-rdvmedecins-jpql-hibernate</artifactId>
7.
<version>1.0-SNAPSHOT</version>
8.
<packaging>jar</packaging>
9.
10. <name>mv-rdvmedecins-jpql-hibernate</name>
11. <url>http://maven.apache.org</url>
12.
13. <properties>
14.
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15. </properties>
16.
17. <dependencies>
18.
<dependency>
19.
<groupId>junit</groupId>
20.
<artifactId>junit</artifactId>
21.
<version>3.8.1</version>
22.
<scope>test</scope>
23.
</dependency>
24.
<dependency>
25.
<groupId>org.hibernate</groupId>
26.
<artifactId>hibernate-entitymanager</artifactId>
27.
<version>4.1.2</version>
28.
</dependency>
29.
<dependency>
30.
<groupId>org.jboss.logging</groupId>
31.
<artifactId>jboss-logging</artifactId>
32.
<version>3.1.0.GA</version>
33.
</dependency>
34.
<dependency>
35.
<groupId>org.jboss.spec.javax.transaction</groupId>
36.
<artifactId>jboss-transaction-api_1.1_spec</artifactId>
37.
<version>1.0.0.Final</version>
38.
</dependency>
39.
<dependency>
40.
<groupId>org.hibernate</groupId>
41.
<artifactId>hibernate-core</artifactId>
42.
<version>4.1.2</version>
43.
</dependency>
44.
<dependency>
45.
<groupId>antlr</groupId>
46.
<artifactId>antlr</artifactId>

41/257

47.
<version>2.7.7</version>
48.
</dependency>
49.
<dependency>
50.
<groupId>dom4j</groupId>
51.
<artifactId>dom4j</artifactId>
52.
<version>1.6.1</version>
53.
</dependency>
54.
<dependency>
55.
<groupId>org.hibernate.javax.persistence</groupId>
56.
<artifactId>hibernate-JPA-2.0-api</artifactId>
57.
<version>1.0.1.Final</version>
58.
</dependency>
59.
<dependency>
60.
<groupId>org.javassist</groupId>
61.
<artifactId>javassist</artifactId>
62.
<version>3.15.0-GA</version>
63.
</dependency>
64.
<dependency>
65.
<groupId>org.hibernate.common</groupId>
66.
<artifactId>hibernate-commons-annotations</artifactId>
67.
<version>4.0.1.Final</version>
68.
</dependency>
69. </dependencies>
70. </project>

Les dpendances ajoutes concernent toutes l'ORM Hibernate. On ajoutera la dpendance du pilote JDBC de MySQL :
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>

Note : selon la version de Netbeans utilise, on peut obtenir des fichiers [pom.xml] diffrents.
Ce fichier contient beaucoup de redondances. La version minimale suivante peut tre utilise dans la suite :
1. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven4.0.0.xsd">
3.
<modelVersion>4.0.0</modelVersion>
4.
5.
<groupId>istia.st</groupId>
6.
<artifactId>mv-rdvmedecins-jpql-hibernate</artifactId>
7.
<version>1.0-SNAPSHOT</version>
8.
<packaging>jar</packaging>
9.
10. <name>mv-rdvmedecins-jpql-hibernate</name>
11. <url>http://maven.apache.org</url>
12.
13. <properties>
14.
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15. </properties>
16.
17. <dependencies>
18.
<dependency>
19.
<groupId>mysql</groupId>
20.
<artifactId>mysql-connector-java</artifactId>
21.
<version>5.1.6</version>
22.
</dependency>
23.
<dependency>
24.
<groupId>org.hibernate</groupId>

42/257

25.
<artifactId>hibernate-entitymanager</artifactId>
26.
<version>4.1.2</version>
27.
</dependency>
28. </dependencies>
29. </project>

3.4.11

Gnration des entits JPA

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

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

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


en [3], on slectionne toutes les tables de la base de donnes associe,

en [4], on donne un nom aux classes Java associes aux quatre tables. Ici on a enlev le pluriel des classes,
ainsi qu'un nom de paquetage [5],
en [6], JPA rassemble des lignes de tables de BD dans des collections. Nous choisissons la liste comme collection,

43/257

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

3.4.12

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. package rdvmedecins.jpa;
2.
3. ...
4. @Entity
5. @Table(name = "medecins")
6. public class Medecin implements Serializable {
7.
8. @Id
9.
@GeneratedValue(strategy = GenerationType.IDENTITY)
10. @Column(name = "ID")
11. private Long id;
12.
13. @Column(name = "TITRE")
14. private String titre;
15.
16. @Column(name = "NOM")
17. private String nom;
18.
19. @Column(name = "VERSION")
20. private int version;
21.
22. @Column(name = "PRENOM")
23. private String prenom;
24.
25. @OneToMany(cascade = CascadeType.ALL, mappedBy = "idMedecin")
26. private List<Creneau> creneauList;
27.
28. // constructeurs
29. ....
30.
31. // getters et setters
32. ....
33.
34. @Override
35. public int hashCode() {
36. ...
37. }
38.

44/257

39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49. }

@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
...
}

ligne 4, 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 5, 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 6, 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 10-11 : le champ id de la classe [Medecin] correspond au champ [ID] (ligne 10) de la table [medecins],
lignes 13-14 : le champ titre de la classe [Medecin] correspond au champ [TITRE] (ligne 13) de la table [medecins],
lignes 16-17 : le champ nom de la classe [Medecin] correspond au champ [NOM] (ligne 16) de la table [medecins],
lignes 19-20 : 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 22-23 : le champ prenom de la classe [Medecin] correspond au champ [PRENOM] de la table [medecins],
lignes 10-11 : le champ id correspond la cl primaire [ID] de la table. Les annotations des lignes 8-9 prcisent ce point,
ligne 8 : l'annotation @Id indique que le champ annot est associ la cl primaire de la table,
ligne 9 : 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 GenerationType.IDENTITY indique que la couche JPA va utiliser le mode auto_increment
de la table MySQL,
lignes 25-26 : 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 25). Le champ de la ligne 26 contiendra
tous les crneaux du mdecin. Ceci sans programmation. Pour comprendre totalement la ligne 25, il nous faut prsenter la
classe [Creneau].

Celle-ci est la suivante :


1. package rdvmedecins.jpa;
2.
3. import java.io.Serializable;
4. import java.util.List;
5. import javax.persistence.*;
6. import javax.validation.constraints.NotNull;
7.
8. @Entity
9. @Table(name = "creneaux")
10. public class Creneau implements Serializable {
11. @Id
12. @GeneratedValue(strategy = GenerationType.IDENTITY)
13. @Column(name = "ID")
14. private Long id;
15.
16. @Column(name = "MDEBUT")
17. private int mdebut;
18.
19. @Column(name = "HFIN")
20. private int hfin;
21.
22. @Column(name = "HDEBUT")
23. private int hdebut;
24.

45/257

25. @Column(name = "MFIN")


26. private int mfin;
27.
28. @Column(name = "VERSION")
29. private int version;
30.
31. @JoinColumn(name = "ID_MEDECIN", referencedColumnName = "ID")
32. @ManyToOne(optional = false)
33. private Medecin idMedecin;
34.
35. @OneToMany(cascade = CascadeType.ALL, mappedBy = "idCreneau")
36. private List<Rv> rvList;
37.
38. // constructeurs
39. ...
40. // getters et setters
41. ...
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57. }

@Override
public int hashCode() {
...
}
@Override
public boolean equals(Object object) {
...
}
@Override
public String toString() {
...
}

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 asssocis 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 32 qui
sert qualifier la cl trangre,
la ligne 31 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 33 : une rfrence sur le mdecin propritaire du crneau. On l'obtient l encore sans programmation.

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

dans l'entit [Creneau] :


1. @JoinColumn(name = "ID_MEDECIN", referencedColumnName = "ID")
2.
@ManyToOne(optional = false)
3. private Medecin idMedecin;

dans l'entit [Medecin] :


1. @OneToMany(cascade = CascadeType.ALL, mappedBy = "idMedecin")
2. 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

46/257

cette mme ligne 1 de l'entit [Medecin], l'attribut cascade=CascadeType.ALL fixe le comportement de l'entit [Medecin] vis
vis de l'entit [Creneau] :

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. package rdvmedecins.jpa;
2.
3. ...
4. @Entity
5. @Table(name = "clients")
6. public class Client implements Serializable {
7.
@Id
8.
@GeneratedValue(strategy = GenerationType.IDENTITY)
9.
@Column(name = "ID")
10. private Long id;
11.
12. @Column(name = "TITRE")
13. private String titre;
14.
15. @Column(name = "NOM")
16. private String nom;
17.
18. @Column(name = "VERSION")
19. private int version;
20.
21. @Column(name = "PRENOM")
22. private String prenom;
23.
24. @OneToMany(cascade = CascadeType.ALL, mappedBy = "idClient")
25. private List<Rv> rvList;
26.
27. // constructeurs
28. ...
29. // getters et setters
30. ...
31.
32. @Override
33. public int hashCode() {
34.
...
35. }
36.
37. @Override
38. public boolean equals(Object object) {
39.
...
40. }
41.
42. @Override
43. public String toString() {
44.
...
45. }
46.
47. }

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

47/257

L'entit [Rv] :
1. package rdvmedecins.jpa;
2.
3. ...
4. @Entity
5. @Table(name = "rv")
6. public class Rv implements Serializable {
7.
@Id
8.
@GeneratedValue(strategy = GenerationType.IDENTITY)
9.
@Column(name = "ID")
10. private Long id;
11.
12. @Column(name = "JOUR")
13. @Temporal(TemporalType.DATE)
14. private Date jour;
15.
16. @JoinColumn(name = "ID_CRENEAU", referencedColumnName = "ID")
17. @ManyToOne(optional = false)
18. private Creneau idCreneau;
19.
20. @JoinColumn(name = "ID_CLIENT", referencedColumnName = "ID")
21. @ManyToOne(optional = false)
22. private Client idClient;
23.
24. // constructeurs
25. ...
26.
27. // getters et setters
28. ...
29.
30. @Override
31. public int hashCode() {
32.
...
33. }
34.
35. @Override
36. public boolean equals(Object object) {
37.
...
38. }
39.
40. @Override
41. public String toString() {
42.
...
43. }
44.
45. }

la ligne 13 qualifie le champ jour de type Java Date. On indique que dans la table [rv], la colonne [JOUR] (ligne 12) est de
type date (sans heure),
lignes 16-18 : qualifient la relation de cl 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 des complications de programmation. On supprime l'annotation et le champ annot,

48/257

on supprime la validation @NotNull sur les cls primaires. Lorsqu'on persiste une entit JPA avec MySQL, l'entit au
dpart a une cl primaire null. Ce n'est qu'aprs persistance dans la base, que la cl primaire de l'lment persist a une
valeur.

Avec ces spcifications, les diffrentes classes deviennent les suivantes :


La classe Personne est utilise pour reprsenter les mdecins et les clients :
1. package rdvmedecins.jpa;
2.
3. import java.io.Serializable;
4. import javax.persistence.*;
5.
6. @MappedSuperclass
7. public class Personne implements Serializable {
8.
private static final long serialVersionUID = 1L;
9.
@Id
10. @GeneratedValue(strategy = GenerationType.IDENTITY)
11. @Column(name = "ID")
12. private Long id;
13.
14. @Basic(optional = false)
15. @Column(name = "TITRE")
16. private String titre;
17.
18. @Basic(optional = false)
19. @Column(name = "NOM")
20. private String nom;
21.
22. @Basic(optional = false)
23. @Column(name = "VERSION")
24. @Version
25. private int version;
26.
27. @Basic(optional = false)
28. @Column(name = "PRENOM")
29. private String prenom;
30.
31. // constructeurs
32.
33. public Personne() {
34. }
35.
36. public Personne(Long id) {
37.
this.id = id;
38. }
39.
40. public Personne(Long id, String titre, String nom, int version, String prenom) {
41.
this.id = id;
42.
this.titre = titre;
43.
this.nom = nom;
44.
this.version = version;
45.
this.prenom = prenom;
46. }
47.
48. // getters et setters
49. ...
50.
51. @Override
52. public String toString() {
53.
return String.format("[%s,%s,%s,%s,%s]", id, version, titre, prenom, nom);
54. }
55.

49/257

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. package rdvmedecins.jpa;
2.
3. import java.io.Serializable;
4. import javax.persistence.*;
5.
6. @Entity
7. @Table(name = "clients")
8. public class Client extends Personne implements Serializable {
9.
private static final long serialVersionUID = 1L;
10.
11. // constructeurs
12. public Client() {
13.
super();
14. }
15.
16. public Client(Long id) {
17.
super(id);
18. }
19.
20. public Client(Long id, String titre, String nom, int version, String prenom) {
21.
super(id, titre, nom, version, prenom);
22. }
23.
24. @Override
25. public int hashCode() {
26. ...
27. }
28.
29. @Override
30. public boolean equals(Object object) {
31. ...
32. }
33.
34. @Override
35. public String toString() {
36.
return String.format("Client[%s,%s,%s,%s]", getId(), getTitre(), getPrenom(),
getNom());
37. }
38.
39. }

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

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

package rdvmedecins.jpa;
import java.io.Serializable;
import javax.persistence.*;
@Entity
@Table(name = "medecins")
public class Medecin extends Personne implements Serializable {
private static final long serialVersionUID = 1L;

50/257

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.

// 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 String.format("Mdecin[%s,%s,%s,%s]", getId(), getTitre(), getPrenom(),
getNom());
37. }
38.
39. }

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


1. package rdvmedecins.jpa;
2.
3. import java.io.Serializable;
4. import java.util.List;
5. import javax.persistence.*;
6.
7. @Entity
8. @Table(name = "creneaux")
9. public class Creneau implements Serializable {
10.
11. private static final long serialVersionUID = 1L;
12. @Id
13. @GeneratedValue(strategy = GenerationType.IDENTITY)
14. @Basic(optional = false)
15. @Column(name = "ID")
16. private Long id;
17.
18. @Basic(optional = false)
19. @Column(name = "MDEBUT")
20. private int mdebut;
21.
22. @Basic(optional = false)
23. @Column(name = "HFIN")
24. private int hfin;
25.
26. @Basic(optional = false)
27. @NotNull
28. @Column(name = "HDEBUT")
29. private int hdebut;

51/257

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.

@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() {
...
}
@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 String.format("Creneau [%s, %s, %s:%s, %s:%s,%s]", id, version, hdebut, mdebut,
hfin, mfin, medecin);
64. }
65. }

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. package rdvmedecins.jpa;
2.
3. import java.io.Serializable;
4. import java.util.Date;
5. import javax.persistence.*;
6.
7. @Entity
8. @Table(name = "rv")
9. public class Rv implements Serializable {
10.
11. private static final long serialVersionUID = 1L;
12. @Id
13. @GeneratedValue(strategy = GenerationType.IDENTITY)
14. @Basic(optional = false)
15. @Column(name = "ID")
16. private Long id;
17.
18. @Basic(optional = false)
19. @Column(name = "JOUR")
20. @Temporal(TemporalType.DATE)

52/257

21. private Date jour;


22.
23. @JoinColumn(name = "ID_CRENEAU", referencedColumnName = "ID")
24. @ManyToOne(optional = false)
25. private Creneau creneau;
26.
27. @JoinColumn(name = "ID_CLIENT", referencedColumnName = "ID")
28. @ManyToOne(optional = false)
29. private Client client;
30.
31. // constructeurs
32. ...
33.
34. // getters et setters
35. ...
36.
37. @Override
38. public int hashCode() {
39.
...
40. }
41.
42. @Override
43. public boolean equals(Object object) {
44.
...
45. }
46.
47. @Override
48. public String toString() {
49.
return String.format("Rv[%s, %s, %s]", id, creneau, client);
50. }
51. }

3.4.13

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

Le code d'accs aux donnes

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

couche
[console]

Couche
[JPA /
Hibernate]

Couche
[JDBC]

SGBD

BD

53/257

La classe [MainJpql] est la suivante :


1. package rdvmedecins.console;
2.
3. import java.util.Scanner;
4. import javax.persistence.EntityManager;
5. import javax.persistence.EntityManagerFactory;
6. import javax.persistence.Persistence;
7.
8. public class MainJpql {
9.
10. public static void main(String[] args) {
11.
// EntityManagerFactory
12.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("mv-rdvmedecins-jpqlhibernatePU");
13.
// entityManager
14.
EntityManager em = emf.createEntityManager();
15.
// scanner clavier
16.
Scanner clavier = new Scanner(System.in);
17.
// boucle de saisie des requtes JPQL
18.
System.out.println("Requete JPQL sur la base dbrdvmedecins2 (* pour arrter) :");
19.
String requete = clavier.nextLine();
20.
while (!requete.trim().equals("*")) {
21.
try {
22.
// affichage rsultat requte
23.
for (Object o : em.createQuery(requete).getResultList()) {
24.
System.out.println(o);
25.
}
26.
} catch (Exception e) {
27.
System.out.println("L'exception suivante s'est produite : " + e);
28.
}
29.
// on vide le contexte de persistance
30.
em.clear();
31.
// nouvelle requte
32.
System.out.println("---------------------------------------------");
33.
System.out.println("Requete JPQL sur la base dbrdvmedecins2 (* pour arrter) :");
34.
requete = clavier.nextLine();
35.
}
36.
// fermeture des ressources
37.
em.close();
38.
emf.close();
39. }
40. }

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.

<persistence-unit name="mv-rdvmedecins-jpql-hibernatePU" transactiontype="RESOURCE_LOCAL">


2.
...
3. </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 *.

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

54/257

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. Requete JPQL sur la base dbrdvmedecins2 (* pour arrter) :
2. select c from Client c
3. Hibernate:
4.
select
5.
client0_.ID as ID2_,
6.
client0_.NOM as NOM2_,
7.
client0_.PRENOM as PRENOM2_,
8.
client0_.TITRE as TITRE2_,
9.
client0_.version as version2_
10.
from
11.
clients client0_
12. Client[1,Mr,Jules,MARTIN]
13. Client[2,Mme,Christine,GERMAN]
14. Client[3,Mr,Jules,JACQUARD]
15. Client[4,Melle,Brigitte,BISTROU]

3.5

ligne 2 : la requte JPQL,


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

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.

3.5.1

La classe Personne

1. package entites;
2.
3. ...
4.
5. @Entity
6. @Table(name = "jpa01_personne")
7. public class Personne {
8.
9.
@Id
10. @Column(name = "ID", nullable = false)
11. @GeneratedValue(strategy = GenerationType.AUTO)
12. private Integer id;
13.
14. @Column(name = "VERSION", nullable = false)
15. @Version
16. private int version;
17.
18. @Column(name = "NOM", length = 30, nullable = false, unique = true)
19. private String nom;
20.
21. @Column(name = "PRENOM", length = 30, nullable = false)
22. private String prenom;
23.
24. @Column(name = "DATENAISSANCE", nullable = false)
25. @Temporal(TemporalType.DATE)
26. private Date datenaissance;
27.
28. @Column(name = "MARIE", nullable = false)

55/257

29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.

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) {
40.
setNom(nom);
41.
setPrenom(prenom);
42.
setDatenaissance(datenaissance);
43.
setMarie(marie);
44.
setNbenfants(nbenfants);
45. }
46.
47. // toString
48.
49. public String toString() {
50.
return String.format("[%d,%d,%s,%s,%s,%s,%d]", getId(), getVersion(), getNom(),
getPrenom(),
51.
new SimpleDateFormat("dd/MM/yyyy").format(getDatenaissance()),
isMarie(), getNbenfants());
52. }
53.
54. // getters and setters
55. ...
56. }

3.5.2

Le programme de test

1. package tests;
2.
3. ....
4. import entites.Personne;
5.
6. @SuppressWarnings("unchecked")
7. public class Test1 {
8.
9.
// constantes
10. private final static String TABLE_NAME = "jpa01_personne"; // Contexte de persistance
11. private static EntityManagerFactory emf = Persistence.createEntityManagerFactory("JPA");
12. private static Personne p1;
13.
14. public static void main(String[] args) throws Exception {
15.
// nettoyage base
16.
log("clean");
17.
clean();
18.
19.
// dump
20.
log("dump");
21.
dump();
22.
23.
// test1
24.
log("test1");
25.
test1();
26.
27.
// test2
28.
log("test2");
29.
test2();

56/257

30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.

// fermeture EntityManagerFactory
emf.close();
}

// affichage contenu table


private static void dump() {
// contexte de persistance
EntityManager em = emf.createEntityManager();
// dbut transaction
EntityTransaction tx = em.getTransaction();
tx.begin();
// affichage personnes
for (Object p : em.createQuery("select p from Personne p order by p.nom
asc").getResultList()) {
44.
System.out.println(p);
45.
}
46.
// fin transaction
47.
tx.commit();
48.
// fin contexte
49.
em.close();
50. }
51.
52. // raz BD
53. private static void clean() {
54.
// contexte de persistance
55.
EntityManager em = emf.createEntityManager();
56.
// dbut transaction
57.
EntityTransaction tx = em.getTransaction();
58.
tx.begin();
59.
// supprimer les lments de la table PERSONNES
60.
em.createNativeQuery("delete from " + TABLE_NAME).executeUpdate();
61.
// fin transaction
62.
tx.commit();
63.
// fin contexte
64.
em.close();
65. }
66.
67. // logs
68. private static void log(String message) {
69.
System.out.println("main : ----------- " + message);
70. }
71.
72. // gestion d'objets persists
73. public static void test1() throws ParseException {
74.
// contexte de persistance
75.
EntityManager em = emf.createEntityManager();
76.
// cration personnes
77.
p1 = new Personne("Martin", "Paul", new
SimpleDateFormat("dd/MM/yy").parse("31/01/2000"), true, 2);
78.
Personne p2 = new Personne("Durant", "Sylvie", new
SimpleDateFormat("dd/MM/yy").parse("05/07/2001"), false, 0);
79.
// dbut transaction
80.
EntityTransaction tx = em.getTransaction();
81.
System.out.println("dbut transaction");
82.
tx.begin();
83.
// persistance des personnes
84.
// les logs montrent que l'opration SQL INSERT est immdiatement gnre aprs
l'opration persist
85.
// probablement pour avoir la cl primaire
86.
System.out.println(String.format("Personne p1 %s non persiste", p1));
87.
System.out.println("em.persist(p1)");
88.
em.persist(p1);

57/257

89.
90.
91.
92.
93.
94.
95.
96.
97.
98.

System.out.println(String.format("Personne p1 %s persiste", p1));


// personne p2
// INSERT est gnr ds l'opration persist
System.out.println(String.format("Personne p2 %s non persiste", p2));
System.out.println("em.persist(p2)");
em.persist(p2);
System.out.println(String.format("Personne p2 %s persiste", p2));
p2.setMarie(true);
System.out.println(String.format("Personne p2 %s modifie", p2));
// l'opration DELETE lie l'opration remove n'est faite qu' la fin de la
transaction
99.
System.out.println("em.remove(p2)");
100.
em.remove(p2);
101.
System.out.println(String.format("Personne p2 %s supprime", p2));
102.
// modification p1
103.
p1.setNom("P1");
104.
// fin transaction
105.
System.out.println("fin transaction");
106.
tx.commit();
107.
// fin contexte
108.
em.close();
109.
// on affiche la table
110.
dump();
111. }
112.
113. // gestion d'objets persists
114. public static void test2() throws ParseException {
115.
// contexte de persistance
116.
EntityManager em = emf.createEntityManager();
117.
// dbut transaction
118.
EntityTransaction tx = em.getTransaction();
119.
System.out.println("dbut transaction");
120.
tx.begin();
121.
// on modifie la personne p1 actuellement dtache
122.
System.out.println(String.format("Personne p1 %s actuelle non persiste", p1));
123.
p1.setMarie(false);
124.
System.out.println(String.format("Personne p1 %s nouvelle non persiste", p1));
125.
// on rattache la personne P1
126.
System.out.println("em.merge(p1)");
127.
Personne p1b = em.merge(p1);
128.
System.out.println(String.format("Personne p1b %s attache", p1b));
129.
// fin transaction
130.
System.out.println("fin transaction");
131.
tx.commit();
132.
// fin contexte
133.
em.close();
134. // on affiche la table
135.
dump();
136. }
137.}

3.5.3

La configuration d'Hibernate dans [persistence.xml]

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


2. <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence">
3.
<persistence-unit name="JPA" transaction-type="RESOURCE_LOCAL">
4.
<!-- provider -->
5.
<provider>org.hibernate.ejb.HibernatePersistence</provider>
6.
<properties>
7.
<!-- Classes persistantes -->
8.
<property name="hibernate.archive.autodetection" value="class, hbm" />
9.
<property name="hibernate.show_sql" value="true"/>

58/257

10. ....
11.
<!-- cration automatique du schma -->
12.
<property name="hibernate.hbm2ddl.auto" value="create" />
13. ....
14.
</properties>
15. </persistence-unit>
16. </persistence>

3.5.4

Les rsultats

1. init:
2. deps-jar:
3. Compiling 1 source file to C:\data\travail\2008-2009\netbeans\JPA\hibernate-personnesentites\build\classes
4. compile-single:
5. run-single:
6. main : ----------- clean
7. Hibernate: delete from jpa01_personne
8. main : ----------- dump
9. Hibernate: select personne0_.ID as ID0_, personne0_.DATENAISSANCE as DATENAIS2_0_,
personne0_.MARIE as MARIE0_, personne0_.NBENFANTS as NBENFANTS0_, personne0_.NOM as NOM0_,
personne0_.PRENOM as PRENOM0_, personne0_.VERSION as VERSION0_ from jpa01_personne
personne0_ order by personne0_.NOM asc
10. main : ----------- test1
11. dbut transaction
12. Personne p1 [null,0,Martin,Paul,31/01/2000,true,2] non persiste
13. em.persist(p1)
14. Hibernate: insert into jpa01_personne (DATENAISSANCE, MARIE, NBENFANTS, NOM, PRENOM,
VERSION) values (?, ?, ?, ?, ?, ?)
15. 17:57:26,312 DEBUG DateType:133 - binding '31 janvier 2000' to parameter: 1
16. 17:57:26,312 DEBUG BooleanType:133 - binding 'true' to parameter: 2
17. 17:57:26,312 DEBUG IntegerType:133 - binding '2' to parameter: 3
18. 17:57:26,312 DEBUG StringType:133 - binding 'Martin' to parameter: 4
19. 17:57:26,312 DEBUG StringType:133 - binding 'Paul' to parameter: 5
20. 17:57:26,312 DEBUG IntegerType:133 - binding '0' to parameter: 6
21. Personne p1 [1,0,Martin,Paul,31/01/2000,true,2] persiste
22. Personne p2 [null,0,Durant,Sylvie,05/07/2001,false,0] non persiste
23. em.persist(p2)
24. Hibernate: insert into jpa01_personne (DATENAISSANCE, MARIE, NBENFANTS, NOM, PRENOM,
VERSION) values (?, ?, ?, ?, ?, ?)
25. 17:57:26,328 DEBUG DateType:133 - binding '05 juillet 2001' to parameter: 1
26. 17:57:26,328 DEBUG BooleanType:133 - binding 'false' to parameter: 2
27. 17:57:26,328 DEBUG IntegerType:133 - binding '0' to parameter: 3
28. 17:57:26,328 DEBUG StringType:133 - binding 'Durant' to parameter: 4
29. 17:57:26,328 DEBUG StringType:133 - binding 'Sylvie' to parameter: 5
30. 17:57:26,328 DEBUG IntegerType:133 - binding '0' to parameter: 6
31. Personne p2 [2,0,Durant,Sylvie,05/07/2001,false,0] persiste
32. Personne p2 [2,0,Durant,Sylvie,05/07/2001,true,0] modifie
33. em.remove(p2)
34. Personne p2 [2,0,Durant,Sylvie,05/07/2001,true,0] supprime
35. fin transaction
36. Hibernate: update jpa01_personne set DATENAISSANCE=?, MARIE=?, NBENFANTS=?, NOM=?,
PRENOM=?, VERSION=? where ID=? and VERSION=?
37. 17:57:26,343 DEBUG DateType:133 - binding '31 janvier 2000' to parameter: 1
38. 17:57:26,343 DEBUG BooleanType:133 - binding 'true' to parameter: 2
39. 17:57:26,343 DEBUG IntegerType:133 - binding '2' to parameter: 3
40. 17:57:26,343 DEBUG StringType:133 - binding 'P1' to parameter: 4
41. 17:57:26,359 DEBUG StringType:133 - binding 'Paul' to parameter: 5
42. 17:57:26,359 DEBUG IntegerType:133 - binding '1' to parameter: 6
43. 17:57:26,359 DEBUG IntegerType:133 - binding '1' to parameter: 7
44. 17:57:26,359 DEBUG IntegerType:133 - binding '0' to parameter: 8
45. Hibernate: delete from jpa01_personne where ID=? and VERSION=?
46. 17:57:26,359 DEBUG IntegerType:133 - binding '2' to parameter: 1

59/257

47. 17:57:26,359 DEBUG IntegerType:133 - binding '0' to parameter: 2


48. Hibernate: select personne0_.ID as ID0_, personne0_.DATENAISSANCE as DATENAIS2_0_,
personne0_.MARIE as MARIE0_, personne0_.NBENFANTS as NBENFANTS0_, personne0_.NOM as NOM0_,
personne0_.PRENOM as PRENOM0_, personne0_.VERSION as VERSION0_ from jpa01_personne
personne0_ order by personne0_.NOM asc
49. 17:57:26,375 DEBUG IntegerType:172 - returning '1' as column: ID0_
50. 17:57:26,390 DEBUG DateType:172 - returning '31 janvier 2000' as column: DATENAIS2_0_
51. 17:57:26,390 DEBUG BooleanType:172 - returning 'true' as column: MARIE0_
52. 17:57:26,390 DEBUG IntegerType:172 - returning '2' as column: NBENFANTS0_
53. 17:57:26,390 DEBUG StringType:172 - returning 'P1' as column: NOM0_
54. 17:57:26,390 DEBUG StringType:172 - returning 'Paul' as column: PRENOM0_
55. 17:57:26,390 DEBUG IntegerType:172 - returning '1' as column: VERSION0_
56. [1,1,P1,Paul,31/01/2000,true,2]
57. main : ----------- test2
58. dbut transaction
59. Personne p1 [1,1,P1,Paul,31/01/2000,true,2] actuelle non persiste
60. Personne p1 [1,1,P1,Paul,31/01/2000,false,2] nouvelle non persiste
61. em.merge(p1)
62. Hibernate: select personne0_.ID as ID0_0_, personne0_.DATENAISSANCE as DATENAIS2_0_0_,
personne0_.MARIE as MARIE0_0_, personne0_.NBENFANTS as NBENFANTS0_0_, personne0_.NOM as
NOM0_0_, personne0_.PRENOM as PRENOM0_0_, personne0_.VERSION as VERSION0_0_ from
jpa01_personne personne0_ where personne0_.ID=?
63. 17:57:26,406 DEBUG IntegerType:133 - binding '1' to parameter: 1
64. 17:57:26,406 DEBUG DateType:172 - returning '31 janvier 2000' as column: DATENAIS2_0_0_
65. 17:57:26,406 DEBUG BooleanType:172 - returning 'true' as column: MARIE0_0_
66. 17:57:26,406 DEBUG IntegerType:172 - returning '2' as column: NBENFANTS0_0_
67. 17:57:26,406 DEBUG StringType:172 - returning 'P1' as column: NOM0_0_
68. 17:57:26,406 DEBUG StringType:172 - returning 'Paul' as column: PRENOM0_0_
69. 17:57:26,406 DEBUG IntegerType:172 - returning '1' as column: VERSION0_0_
70. Personne p1b [1,1,P1,Paul,31/01/2000,false,2] attache
71. fin transaction
72. Hibernate: update jpa01_personne set DATENAISSANCE=?, MARIE=?, NBENFANTS=?, NOM=?,
PRENOM=?, VERSION=? where ID=? and VERSION=?
73. 17:57:26,406 DEBUG DateType:133 - binding '31 janvier 2000' to parameter: 1
74. 17:57:26,406 DEBUG BooleanType:133 - binding 'false' to parameter: 2
75. 17:57:26,406 DEBUG IntegerType:133 - binding '2' to parameter: 3
76. 17:57:26,421 DEBUG StringType:133 - binding 'P1' to parameter: 4
77. 17:57:26,421 DEBUG StringType:133 - binding 'Paul' to parameter: 5
78. 17:57:26,421 DEBUG IntegerType:133 - binding '2' to parameter: 6
79. 17:57:26,421 DEBUG IntegerType:133 - binding '1' to parameter: 7
80. 17:57:26,421 DEBUG IntegerType:133 - binding '1' to parameter: 8
81. Hibernate: select personne0_.ID as ID0_, personne0_.DATENAISSANCE as DATENAIS2_0_,
personne0_.MARIE as MARIE0_, personne0_.NBENFANTS as NBENFANTS0_, personne0_.NOM as NOM0_,
personne0_.PRENOM as PRENOM0_, personne0_.VERSION as VERSION0_ from jpa01_personne
personne0_ order by personne0_.NOM asc
82. 17:57:26,453 DEBUG IntegerType:172 - returning '1' as column: ID0_
83. 17:57:26,453 DEBUG DateType:172 - returning '31 janvier 2000' as column: DATENAIS2_0_
84. 17:57:26,453 DEBUG BooleanType:172 - returning 'false' as column: MARIE0_
85. 17:57:26,453 DEBUG IntegerType:172 - returning '2' as column: NBENFANTS0_
86. 17:57:26,453 DEBUG StringType:172 - returning 'P1' as column: NOM0_
87. 17:57:26,453 DEBUG StringType:172 - returning 'Paul' as column: PRENOM0_
88. 17:57:26,453 DEBUG IntegerType:172 - returning '2' as column: VERSION0_
89. [1,2,P1,Paul,31/01/2000,false,2]
90. BUILD SUCCESSFUL (total time: 3 seconds)

Question : faites le lien entre le code Java et les rsultats affichs.

60/257

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]

Couche
[DAO]

Objets image
de la BD

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 :
cl primaire
ID
VERSION n de version augmente chaque modification de la ligne
CSGRDS pourcentage : contribution sociale gnralise + contribution au remboursement de la dette sociale
pourcentage : contribution sociale gnralise dductible
CSGD
pourcentage : scurit sociale, veuvage, vieillesse
SECU
RETRAITE pourcentage : retraite complmentaire + assurance chmage
Son contenu pourrait tre le suivant :

61/257

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]*[B
ASEHEURE])*(1+
[INDEMNITESCP]/100)

Un certain nombre de cotisations sociales Contribution sociale


gnralise et contribution au
doivent tre prleves sur ce salaire de
remboursement de la dette
base :
sociale :
[SALAIREBASE]*[CSGRDS/100]
Contribution sociale
gnralise dductible :
[SALAIREBASE]*[CSGD/100]

[SALAIREBASE]=(150*[2.1])*(1+0.
15)= 362,25
CSGRDS : 12,64
CSGD : 22,28
Scurit sociale : 34,02
Retraite : 28,55

Scurit sociale, veuvage,


vieillesse :
[SALAIREBASE]*[SECU/100]
Retraite Complmentaire + AGPF
+ Assurance Chmage :
[SALAIREBASE]*[RETRAITE/100]

62/257

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

Total des cotisations sociales :

[COTISATIONSSOCIALES]=[SALAIREB
ASE]*(CSGRDS+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
repas. A ce titre elle reoit les indemnits
suivantes :

[INDEMNITS]=[TOTALJOURS]*(ENTR
ETIENJOUR+REPASJOUR)

[INDEMNITES]=104

Au final, le salaire net payer l'assistante [SALAIREBASE]maternelle est le suivant :


[COTISATIONSSOCIALES]+

[salaire NET]=368,77

[INDEMNITS]

4.3

Fonctionnement de l'application console

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


1. dos>java -jar pam-spring-ui-metier-dao-jpa-eclipselink.jar 254104940426058 150 20
2.
3. Valeurs saisies :
4. N de scurit sociale de l'employ : 254104940426058
5. Nombre d'heures travailles : 150
6. Nombre de jours travaills : 20
7.
8. Informations Employ :
9. Nom : Jouveinal
10. Prnom : Marie
11. Adresse : 5 rue des Oiseaux
12. Ville : St Corentin
13. Code Postal : 49203
14. Indice : 2
15.
16. Informations Cotisations :
17. CSGRDS : 3.49 %
18. CSGD : 6.15 %
19. Retraite : 7.88 %
20. Scurit sociale : 9.39 %
21.
22. Informations Indemnits :
23. Salaire horaire : 2.1 euro
24. Entretien/jour : 2.1 euro
25. Repas/jour : 3.1 euro
26. Congs Pays : 15.0 %
27.
28. Informations Salaire :
29. Salaire de base : 362.25 euro
30. Cotisations sociales : 97.48 euro
31. Indemnits d'entretien : 42.0 euro
32. Indemnits de repas : 62.0 euro
33. Salaire net : 368.77 euro

On crira un programme qui recevra les informations suivantes :

63/257

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 pam-spring-ui-metier-dao-JPA-eclipselink.jar
Syntaxe : pg num_securite_sociale nb_heures_travailles nb_jours_travaills

Appel avec des donnes errones :


dos>java -jar pam-spring-ui-metier-dao-JPA-eclipselink.jar
Le nombre d'heures travailles [150x] est erron
Le nombre de jours travaills [20x] est erron

254104940426058 150x 20x

Appel avec un n de scurit sociale erron :


dos>java -jar pam-spring-ui-metier-dao-JPA-eclipselink.jar 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 :

64/257

6
1
3

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

1
2

en [2], on prend l'option [Bases de donnes],

65/257

en [3], on cre une base de donnes [dbpam_hibernate],


en [4], la base cre. On la slectionne,

5
6

en [5], on veut importer un script SQL,


en [6], on utilise le bouton [Parcourir] pour dsigner le fichier,

7
8

en [7,8], on slectionne le script SQL,


en [9], on l'excute,

10

en [10], les tables ont t cres. Leur contenu est le suivant :

66/257

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

Un programme console travaillera avec la base de donnes. Pour cela, il faut :

avoir une base de donnes,

avoir le pilote JDBC du SGBD, ici MySQL,

implmenter la couche JPA avec Hibernate,

crire le programme console.


Nous crons le projet Maven [mv-pam-jpa-hibernate] [1] :

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

la base de donnes,
le pilote JDBC du SGBD MySQL,

67/257

4.6.1.1

la couche JPA / Hibernate (entits et configuration),


le programme console de test.

La base de donnes

Crons tout d'abord la base de donnes vide. Nous lanons WampServer et utilisons l'outil PhpMyAdmin [1] :

1
2

en [2], on prend l'option [Bases de donnes],

4.6.1.2

en [3], on cre une base de donnes [dbpam_hibernate],


en [4], la base cre.

Configuration de la couche JPA

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

68/257

1
3
5
2
6

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], on peut tester la connexion,
en [7], la connexion a russi.

10

la connexion apparat en [8] et en [9],


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

13
14
15
16
11

12

69/257

en [11] on choisit la catgorie [Persistence] et en [12] l'lment [Persistence Unit],


en [13], on donne un nom cette unit de persistance,
en [14], on choisit une implmentation Hibernate,
en [15], on dsigne la connexion que nous venons de crer vers la base MySQL,
en [16], 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 [persistence.xml] :

2
1

le fichier apparat dans une nouvelle branche du projet, dans un dossier [META-INF] [1],
qui correspond au dossier [src/main/resources] du projet [2,3] .

Son contenu est le suivant :


1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
3.
<persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
4.
<provider>org.hibernate.ejb.HibernatePersistence</provider>
5.
<properties>
6.
<property name="javax.persistence.jdbc.url"
value="jdbc:mysql://localhost:3306/dbpam_hibernate"/>
7.
<property name="javax.persistence.jdbc.password" value=""/>
8.
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
9.
<property name="javax.persistence.jdbc.user" value="root"/>
10.
<property name="hibernate.cache.provider_class"
value="org.hibernate.cache.NoCacheProvider"/>
11.
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
12.
</properties>
13. </persistence-unit>
14. </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. En fait, Netbeans gnre ici une configuration
errone. La configuration doit tre la suivante :
<property name="hibernate.hbm2ddl.auto" value="create"/>

Avec l'option create, Hibernate, l'instanciation de la couche JPA, supprime puis cre les tables correspondant aux entits JPA.
L'option create-drop fait la mme chose mais la fin de vie de la couche JPA, elle supprime toutes les tables. Il existe une autre
option :
<property name="hibernate.hbm2ddl.auto" value="update"/>

70/257

Cette option cre les tables si elles n'existent pas mais elle ne les dtruit pas si elles existent dj.
Nous ajouterons trois autres proprits la configuration d'Hibernate :
1.
<property name="hibernate.show_sql" value="true"/>
2.
<property name="hibernate.format_sql" value="true"/>
3. <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. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
3.
<persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
4.
<provider>org.hibernate.ejb.HibernatePersistence</provider>
5.
<class>jpa.Cotisation</class>
6.
<class>jpa.Employe</class>
7.
<class>jpa.Indemnite</class>
8.
<properties>
9.
<property name="javax.persistence.jdbc.url"
value="jdbc:mysql://localhost:3306/dbpam_hibernate"/>
10.
<property name="javax.persistence.jdbc.password" value=""/>
11.
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
12.
<property name="javax.persistence.jdbc.user" value="root"/>
13.
<property name="hibernate.cache.provider_class"
value="org.hibernate.cache.NoCacheProvider"/>
14.
<property name="hibernate.hbm2ddl.auto" value="create"/>
15.
<property name="hibernate.show_sql" value="true"/>
16.
<property name="hibernate.format_sql" value="true"/>
17.
<property name="use_sql_comments" value="true"/>
18.
</properties>
19. </persistence-unit>
20. </persistence>

4.6.1.3

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 [persistence.xml]. L'implmentation choisie a t Hibernate. Cela a amen des
dpendances dans le projet :

71/257

Ces dpendances sont dues l'inclusion d'Hibernate dans le projet. 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 [pom.xml] de la faon suivante :
1. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
3.
<modelVersion>4.0.0</modelVersion>
4.
5.
<groupId>istia.st</groupId>
6.
<artifactId>mv-pam-jpa-hibernate</artifactId>
7.
<version>1.0-SNAPSHOT</version>
8.
<packaging>jar</packaging>
9.
10. <name>mv-pam-jpa-hibernate</name>
11. <url>http://maven.apache.org</url>
12.
13. <properties>
14.
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15. </properties>
16.
17. <dependencies>
18.
<dependency>
19.
<groupId>org.hibernate</groupId>
20.
<artifactId>hibernate-entitymanager</artifactId>
21.
<version>4.1.2</version>
22.
<type>jar</type>
23.
</dependency>
24.
<dependency>
25.
<groupId>mysql</groupId>
26.
<artifactId>mysql-connector-java</artifactId>
27.
<version>5.1.6</version>
28.
</dependency>
29. </dependencies>
30. </project>

Les lignes 24-28 ajoutent la dpendance du pilote JDBC de MySQL.

4.6.1.4

Les entits JPA

72/257

Question : Ecrire les entits [Cotisation, Indemnite, Employe].


Notes :

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.

Note : Les squelettes des entits sans les annotations sont disponibles dans le support de cours.
1. package jpa;
2.
3. public class Cotisation implements Serializable {
4.
5.
private Long id;
6.
private int version;
7.
private double csgrds;
8.
private double csgd;
9.
private double secu;
10. private double retraite;
11.
12. public Cotisation() {
13. }
14.
15. public Cotisation(double csgrds, double csgd, double secu, double retraite){
16.
setCsgrds(csgrds);
17.
setCsgd(csgd);
18.
setSecu(secu);
19.
setRetraite(retraite);
20. }
21. }
1. package jpa;
2.
3. public class Employe implements Serializable {
4.
5.
private Long id;
6.
private int version;
7.
private String SS;
8.
private String nom;
9.
private String prenom;
10. private String adresse;
11. private String ville;
12. private String codePostal;
13. private Indemnite indemnite;
14.

73/257

15.
16.
17.
18.

public Employe() {
}

public Employe(String SS, String nom, String prenom, String adresse, String ville, String
codePostal, Indemnite indemnite){
19.
setSS(SS);
20.
setNom(nom);
21.
setPrenom(prenom);
22.
setAdresse(adresse);
23.
setVille(ville);
24.
setCodePostal(codePostal);
25.
setIndemnite(indemnite);
26. }
27. }
1. package jpa;
2.
3. public class Indemnite implements Serializable {
4.
5.
private Long id;
6.
private int version;
7.
private int indice;
8.
private double baseHeure;
9.
private double entretienJour;
10. private double repasJour;
11. private double indemnitesCP;
12.
13. public Indemnite() {
14. }
15.
16. public Indemnite(int indice, double baseHeure, double entretienJour, double repasJour,
double indemnitesCP){
17.
setIndice(indice);
18.
setBaseHeure(baseHeure);
19.
setEntretienJour(entretienJour);
20.
setRepasJour(repasJour);
21.
setIndemnitesCP(indemnitesCP);
22. }
23. }

4.6.1.5

Le code de la classe principale

Nous incluons dans le projet les entits JPA dveloppes prcdemment [1] :

1
2

puis nous rajoutons [2], la classe [main.Main] suivante :

74/257

1. package main;
2.
3. import javax.persistence.EntityManager;
4. import javax.persistence.EntityManagerFactory;
5. import javax.persistence.Persistence;
6.
7. public class Main {
8.
9.
public static void main(String[] args) {
10.
// crer l'Entity Manager suffit construire la couche JPA
11.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("mv-pam-jpahibernatePU");
12.
EntityManager em=emf.createEntityManager();
13.
// libration ressources
14.
em.close();
15.
emf.close();
16. }
17. }

1.
2.
3.

4.6.1.6

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


fichier [persistence.xml] :
<persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
...
</persistence-unit>

ligne 12 : on cre l'EntityManager. Cette cration cre la couche JPA. Le fichier [persistence.xml] va tre exploit et donc 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]

Couche
[JDBC]

Implmentation
[Hibernate]

Base de
donnes

Toutes les couches ont t implmentes. On excute le projet [2].

Les rsultats console sont les suivants :

75/257

1. -----------------------------------------------------------------------2. Building mv-pam-JPA-hibernate 1.0-SNAPSHOT


3. -----------------------------------------------------------------------4.
5. --- exec-maven-plugin:1.2.1:exec (default-cli) @ mv-pam-JPA-hibernate --6. sept. 09, 2013 2:05:13 PM org.hibernate.annotations.common.Version <clinit>
7. INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
8. sept. 09, 2013 2:05:13 PM org.hibernate.Version logVersion
9. INFO: HHH000412: Hibernate Core {4.1.2}
10. sept. 09, 2013 2:05:13 PM org.hibernate.cfg.Environment <clinit>
11. INFO: HHH000206: hibernate.properties not found
12. sept. 09, 2013 2:05:13 PM org.hibernate.cfg.Environment buildBytecodeProvider
13. INFO: HHH000021: Bytecode provider name : javassist
14. sept. 09, 2013 2:05:13 PM
org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl
configure
15. INFO: HHH000402: Using Hibernate built-in connection pool (not for production use!)
16. sept. 09, 2013 2:05:13 PM
org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl
configure
17. INFO: HHH000115: Hibernate connection pool size: 20
18. sept. 09, 2013 2:05:13 PM
org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl
configure
19. INFO: HHH000006: Autocommit mode: true
20. sept. 09, 2013 2:05:13 PM
org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl
configure
21. INFO: HHH000401: using driver [com.mysql.jdbc.Driver] at URL
[jdbc:mysql://localhost:3306/dbpam_hibernate]
22. sept. 09, 2013 2:05:13 PM
org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl
configure
23. INFO: HHH000046: Connection properties: {user=root, autocommit=true, release_mode=auto}
24. sept. 09, 2013 2:05:17 PM org.hibernate.dialect.Dialect <init>
25. INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
26. sept. 09, 2013 2:05:17 PM org.hibernate.engine.jdbc.internal.LobCreatorBuilder
useContextualLobCreation
27. INFO: HHH000423: Disabling contextual LOB creation as JDBC driver reported JDBC version [3]
less than 4
28. sept. 09, 2013 2:05:18 PM
org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
29. INFO: HHH000268: Transaction strategy:
org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
30. sept. 09, 2013 2:05:18 PM org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
31. INFO: HHH000397: Using ASTQueryTranslatorFactory
32. sept. 09, 2013 2:05:18 PM org.hibernate.tool.hbm2ddl.SchemaExport execute
33. INFO: HHH000227: Running hbm2ddl schema export
34. Hibernate:
35.
alter table EMPLOYES
36.
drop
37.
foreign key FK75C8D6BC73F24A67
38. sept. 09, 2013 2:05:18 PM org.hibernate.tool.hbm2ddl.SchemaExport perform
39. ERROR: HHH000389: Unsuccessful: alter table EMPLOYES drop foreign key FK75C8D6BC73F24A67
40. sept. 09, 2013 2:05:18 PM org.hibernate.tool.hbm2ddl.SchemaExport perform
41. ERROR: Error on rename of '.\dbpam_hibernate\employes' to '.\dbpam_hibernate\#sql2-1860-a'
(errno: 152)
42. Hibernate:
43.
drop table if exists COTISATIONS
44. Hibernate:
45.
drop table if exists EMPLOYES
46. Hibernate:
47.
drop table if exists INDEMNITES

76/257

48. Hibernate:
49.
create table COTISATIONS (
50.
id bigint not null auto_increment,
51.
CSGD double precision not null,
52.
CSGRDS double precision not null,
53.
RETRAITE double precision not null,
54.
SECU double precision not null,
55.
VERSION integer not null,
56.
primary key (id)
57.
)
58. Hibernate:
59.
create table EMPLOYES (
60.
id bigint not null auto_increment,
61.
SS varchar(15) not null unique,
62.
ADRESSE varchar(50) not null,
63.
CP varchar(5) not null,
64.
NOM varchar(30) not null,
65.
PRENOM varchar(20) not null,
66.
VERSION integer not null,
67.
VILLE varchar(30) not null,
68.
INDEMNITE_ID bigint not null,
69.
primary key (id)
70.
)
71. Hibernate:
72.
create table INDEMNITES (
73.
id bigint not null auto_increment,
74.
BASE_HEURE double precision not null,
75.
ENTRETIEN_JOUR double precision not null,
76.
INDEMNITES_CP double precision not null,
77.
INDICE integer not null unique,
78.
REPAS_JOUR double precision not null,
79.
VERSION integer not null,
80.
primary key (id)
81.
)
82. Hibernate:
83.
alter table EMPLOYES
84.
add index FK75C8D6BC73F24A67 (INDEMNITE_ID),
85.
add constraint FK75C8D6BC73F24A67
86.
foreign key (INDEMNITE_ID)
87.
references INDEMNITES (id)
88. sept. 09, 2013 2:05:19 PM org.hibernate.tool.hbm2ddl.SchemaExport execute
89. INFO: HHH000230: Schema export complete
90. sept. 09, 2013 2:05:20 PM
org.hibernate.service.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
91. INFO: HHH000030: Cleaning up connection pool [jdbc:mysql://localhost:3306/dbpam_hibernate]
92. -----------------------------------------------------------------------93. BUILD SUCCESS
94. -----------------------------------------------------------------------95. Total time: 11.724s
96. Finished at: Mon Sep 09 14:05:20 CEST 2013
97. Final Memory: 5M/122M

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 35 : Hibernate essaie de supprimer la cl trangre de la table [EMPLOYES],


lignes 42-47 : suppression des trois tables,
ligne 49 : cration de la table [COTISATIONS],
ligne 59 : cration de la table [EMPLOYES],
ligne 72 : cration de la table [INDEMNITES],
ligne 83 : cration de la cl trangre de la table [EMPLOYES].

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

77/257

Les tables cres dpendent la fois de l'implmentation de la couche JPA utilise et du SGBD utilis. Ainsi une implmentation
JPA / EclipseLink avec la mme base de donnes peut gnrer des tables diffrentes. C'est ce que nous allons voir maintenant.

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 du paragraphe prcdent :


1.
2.
3.
4.
5.

crer une base MySQL [dbpam_eclipselink]. On utilisera le script [dbpam_eclipselink.sql] pour la gnrer,
crer le fichier [persistence.xml] du projet. Prendre l'implmentation JPA 2.0 EclipseLink,
ajouter dans les dpendances gnres la dpendance du pilote JDBC de MySQL,
ajouter les entits JPA et le programme console du prcdent projet. Adaptez-le le programme console pour qu'il utilise la
bonne unit de persistance,
faire les tests.

Le fichier [persistence.xml] sera le suivant :


1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
3.
<persistence-unit name="pam-jpa-eclipselinkPU" transaction-type="RESOURCE_LOCAL">
4.
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
5.
<class>jpa.Cotisation</class>
6.
<class>jpa.Employe</class>
7.
<class>jpa.Indemnite</class>
8.
<properties>
9.
<property name="eclipselink.target-database" value="MySQL"/>
10.
<property name="javax.persistence.jdbc.url"
value="jdbc:mysql://localhost:3306/dbpam_eclipselink"/>
11.
<property name="javax.persistence.jdbc.password" value=""/>
12.
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>

78/257

13.
<property name="javax.persistence.jdbc.user" value="root"/>
14.
<property name="eclipselink.logging.level" value="FINE"/>
15.
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
16.
</properties>
17. </persistence-unit>
18. </persistence>

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


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

-----------------------------------------------------------------------Building mv-pam-JPA-eclipselink 1.0-SNAPSHOT


------------------------------------------------------------------------

--- exec-maven-plugin:1.2.1:exec (default-cli) @ mv-pam-JPA-eclipselink --[EL Config]: 2013-09-09 14:10:15.181--ServerSession(303405156)-Thread(Thread[main,5,main])--The access type for the persistent class [class
JPA.Cotisation] is set to [FIELD].
7. [EL Config]: 2013-09-09 14:10:15.219--ServerSession(303405156)-Thread(Thread[main,5,main])--The access type for the persistent class [class JPA.Employe]
is set to [FIELD].
8. [EL Config]: 2013-09-09 14:10:15.229--ServerSession(303405156)-Thread(Thread[main,5,main])--The target entity (reference) class for the many to one
mapping element [field indemnite] is being defaulted to: class JPA.Indemnite.
9. [EL Config]: 2013-09-09 14:10:15.23--ServerSession(303405156)-Thread(Thread[main,5,main])--The access type for the persistent class [class JPA.Indemnite]
is set to [FIELD].
10. [EL Config]: 2013-09-09 14:10:15.231--ServerSession(303405156)-Thread(Thread[main,5,main])--The alias name for the entity class [class JPA.Cotisation] is
being defaulted to: Cotisation.
11. [EL Config]: 2013-09-09 14:10:15.257--ServerSession(303405156)-Thread(Thread[main,5,main])--The column name for element [id] is being defaulted to: ID.
12. [EL Config]: 2013-09-09 14:10:15.261--ServerSession(303405156)-Thread(Thread[main,5,main])--The alias name for the entity class [class JPA.Employe] is
being defaulted to: Employe.
13. [EL Config]: 2013-09-09 14:10:15.262--ServerSession(303405156)-Thread(Thread[main,5,main])--The column name for element [id] is being defaulted to: ID.
14. [EL Config]: 2013-09-09 14:10:15.262--ServerSession(303405156)-Thread(Thread[main,5,main])--The alias name for the entity class [class JPA.Indemnite] is
being defaulted to: Indemnite.
15. [EL Config]: 2013-09-09 14:10:15.263--ServerSession(303405156)-Thread(Thread[main,5,main])--The column name for element [id] is being defaulted to: ID.
16. [EL Config]: 2013-09-09 14:10:15.289--ServerSession(303405156)-Thread(Thread[main,5,main])--The primary key column name for the mapping element [field
indemnite] is being defaulted to: ID.
17. [EL Info]: 2013-09-09 14:10:15.824--ServerSession(303405156)--Thread(Thread[main,5,main])-EclipseLink, version: Eclipse Persistence Services - 2.3.0.v20110604-r9504
18. [EL Config]: 2013-09-09 14:10:15.834--ServerSession(303405156)--Connection(1237989929)-Thread(Thread[main,5,main])--connecting(DatabaseLogin(
19.
platform=>MySQLPlatform
20.
user name=> "root"
21.
datasource URL=> "jdbc:mysql://localhost:3306/dbpam_eclipselink"
22. ))
23. [EL Config]: 2013-09-09 14:10:16.276--ServerSession(303405156)--Connection(645036541)-Thread(Thread[main,5,main])--Connected: jdbc:mysql://localhost:3306/dbpam_eclipselink
24.
User: root@localhost
25.
Database: MySQL Version: 5.5.24-log
26.
Driver: MySQL-AB JDBC Driver Version: mysql-connector-java-5.1.6 ( Revision: $
{svn.Revision} )

79/257

27. [EL Info]: 2013-09-09 14:10:16.342--ServerSession(303405156)--Thread(Thread[main,5,main])-file:/D:/data/istia-1314/netbeans/mv-pam/05/mv-pam-JPA-eclipselink/target/classes/_pam-JPAeclipselinkPU login successful


28. [EL Fine]: 2013-09-09 14:10:16.362--ServerSession(303405156)--Connection(645036541)-Thread(Thread[main,5,main])--ALTER TABLE EMPLOYES DROP FOREIGN KEY FK_EMPLOYES_INDEMNITE_ID
29. [EL Fine]: 2013-09-09 14:10:18.209--ServerSession(303405156)--Thread(Thread[main,5,main])-SELECT 1
30. [EL Warning]: 2013-09-09 14:10:18.217--ServerSession(303405156)-Thread(Thread[main,5,main])--Exception [EclipseLink-4002] (Eclipse Persistence Services 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DatabaseException
31. Internal Exception: java.sql.SQLException: Error on rename of
'.\dbpam_eclipselink\employes' to '.\dbpam_eclipselink\#sql2-1860-b' (errno: 152)
32. Error Code: 1025
33. Call: ALTER TABLE EMPLOYES DROP FOREIGN KEY FK_EMPLOYES_INDEMNITE_ID
34. Query: DataModifyQuery(sql="ALTER TABLE EMPLOYES DROP FOREIGN KEY
FK_EMPLOYES_INDEMNITE_ID")
35. [EL Fine]: 2013-09-09 14:10:18.228--ServerSession(303405156)--Connection(645036541)-Thread(Thread[main,5,main])--DROP TABLE COTISATIONS
36. [EL Fine]: 2013-09-09 14:10:18.773--ServerSession(303405156)--Connection(645036541)-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))
37. [EL Fine]: 2013-09-09 14:10:19.856--ServerSession(303405156)--Connection(645036541)-Thread(Thread[main,5,main])--DROP TABLE EMPLOYES
38. [EL Fine]: 2013-09-09 14:10:20.351--ServerSession(303405156)--Connection(645036541)-Thread(Thread[main,5,main])--CREATE TABLE EMPLOYES (ID BIGINT NOT NULL, SS VARCHAR(15) NOT
NULL UNIQUE, ADRESSE VARCHAR(50) NOT NULL, CP VARCHAR(5) NOT NULL, NOM VARCHAR(30) NOT
NULL, PRENOM VARCHAR(20) NOT NULL, VERSION INTEGER NOT NULL, VILLE VARCHAR(30) NOT NULL,
INDEMNITE_ID BIGINT NOT NULL, PRIMARY KEY (ID))
39. [EL Fine]: 2013-09-09 14:10:21.309--ServerSession(303405156)--Connection(645036541)-Thread(Thread[main,5,main])--DROP TABLE INDEMNITES
40. [EL Fine]: 2013-09-09 14:10:21.838--ServerSession(303405156)--Connection(645036541)-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))
41. [EL Fine]: 2013-09-09 14:10:22.807--ServerSession(303405156)--Connection(645036541)-Thread(Thread[main,5,main])--ALTER TABLE EMPLOYES ADD CONSTRAINT FK_EMPLOYES_INDEMNITE_ID
FOREIGN KEY (INDEMNITE_ID) REFERENCES INDEMNITES (ID)
42. [EL Fine]: 2013-09-09 14:10:24.969--ServerSession(303405156)--Connection(645036541)-Thread(Thread[main,5,main])--CREATE TABLE SEQUENCE (SEQ_NAME VARCHAR(50) NOT NULL,
SEQ_COUNT DECIMAL(38), PRIMARY KEY (SEQ_NAME))
43. [EL Fine]: 2013-09-09 14:10:25.858--ServerSession(303405156)--Connection(645036541)-Thread(Thread[main,5,main])--DELETE FROM SEQUENCE WHERE SEQ_NAME = SEQ_GEN
44. [EL Fine]: 2013-09-09 14:10:25.863--ServerSession(303405156)--Connection(645036541)-Thread(Thread[main,5,main])--SELECT * FROM SEQUENCE WHERE SEQ_NAME = SEQ_GEN
45. [EL Fine]: 2013-09-09 14:10:25.871--ServerSession(303405156)--Connection(645036541)-Thread(Thread[main,5,main])--INSERT INTO SEQUENCE(SEQ_NAME, SEQ_COUNT) values (SEQ_GEN, 0)
46. [EL Config]: 2013-09-09 14:10:26.45--ServerSession(303405156)--Connection(645036541)-Thread(Thread[main,5,main])--disconnect
47. [EL Info]: 2013-09-09 14:10:26.451--ServerSession(303405156)--Thread(Thread[main,5,main])-file:/D:/data/istia-1314/netbeans/mv-pam/05/mv-pam-JPA-eclipselink/target/classes/_pam-JPAeclipselinkPU logout successful
48. [EL Config]: 2013-09-09 14:10:26.451--ServerSession(303405156)--Connection(1237989929)-Thread(Thread[main,5,main])--disconnect
49. -----------------------------------------------------------------------50. BUILD SUCCESS
51. -----------------------------------------------------------------------52. Total time: 12.940s
53. Finished at: Mon Sep 09 14:10:26 CEST 2013
54. Final Memory: 4M/122M

lignes 18-22 : connexion la base de donnes MySQL,


lignes 23-27 : confirmation que la connexion a russi,

80/257

lignes 28-34 : suppression de la cl trangre de la table [EMPLOYES],


ligne 35 : suppression de la table [COTISATIONS],
ligne 36 : cration de la table [COTISATIONS]. On notera avec intrt, que la cl primaire ID n'a pas l'attribut MySQL
auto_increment. Cela veut dire que ce n'est pas MySQL qui gnre les valeurs de la cl primaire,
ligne 37 : suppression de la table [EMPLOYES],
ligne 38 : cration de la table [EMPLOYES]. Sa cl primaire ID n'a pas l'attribut MySQL auto_increment,
ligne 39 : suppression de la table [INDEMNITES],
ligne 40 : cration de la table [INDEMNITES]. Sa cl primaire ID n'a pas l'attribut MySQL auto_increment,
ligne 41 : cration de la cl trangre de la table [EMPLOYES] vers la table [INDEMNITES],
ligne 42 : cration d'une table [SEQUENCE]. Elle sera utilise pour gnrer les cls primaires des trois tables prcdentes,
ligne 43 : suppression de certaines lignes de la table [SEQUENCE] si elle existait dj,
ligne 44 : recherche des lignes qui viennent d'tre supprimes. On devrait en trouver zro,
ligne 45 : insertion d'une ligne dans la table. C'est le gnrateur de cls primaires initialis ici zro,
lignes 46-48 : dconnexion de la base.

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

Donc, partir des mmes entits JPA, les implmentations JPA Hibernate et EclipseLink ne gnrent pas les mmes tables. Dans la
suite du document, lorsque l'implmentation JPA utilise est :

4.6.3

Hibernate, on utilisera la base de donnes [dbpam_hibernate],


EclipseLink, on utilisera la base de donnes [dbpam_eclipselink].

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. package jpa;

81/257

2.
3. ...
4.
5. @Entity
6. @Table(name="EMPLOYES")
7. public class Employe implements Serializable {
8.
9.
@Id
10. @GeneratedValue(strategy = GenerationType.AUTO)
11. private Long id;
12. @Version
13. @Column(name="VERSION",nullable=false)
14. private int version;
15. @Column(name="SS", nullable=false, unique=true, length=15)
16. private String SS;
17. @Column(name="NOM", nullable=false, length=30)
18. private String nom;
19. @Column(name="PRENOM", nullable=false, length=20)
20. private String prenom;
21. @Column(name="ADRESSE", nullable=false, length=50)
22. private String adresse;
23. @Column(name="VILLE", nullable=false, length=30)
24. private String ville;
25. @Column(name="CP", nullable=false, length=5)
26. private String codePostal;
27. @ManyToOne(fetch= FetchType.LAZY)
28. @JoinColumn(name="INDEMNITE_ID",nullable=false)
29. private Indemnite indemnite;
30. ...
31. }

Les lignes 27-29 dfinissent la cl trangre de la table [EMPLOYES] vers la table [INDEMNITES]. L'attribut fetch de la ligne 27
dfinit la stratgie de recherche du champ indemnite de la ligne 29. Il y a deux modes :

FetchType.LAZY : lorsqu'un employ est cherch, l'indemnit qui lui correspond n'est pas ramene. Elle le sera lorsque
le champ [Employe].indemnite sera rfrenc pour la premire fois.

FetchType.EAGER : 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 FetchType.LAZY, 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 FetchType.LAZY 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 FetchType.LAZY vite de ramener trop de donnes dont l'application n'a pas besoin tout de suite. Voyons un exemple.
Le projet [mv-pam-jpa-hibernate] est dupliqu :

82/257

3
2

en [1], on copie le projet,


en [2], on indique le dossier de la copie et en [3] son nom,
en [4], le nouveau projet porte le mme nom que l'ancien. Nous changeons cela :

3
2

en [1], on renomme le projet,

83/257

en [2], on renomme le projet et son artifactId,


en [3], le nouveau projet.

Nous modifions le programme [Main.java] de la faon suivante :


1. package main;
2.
3. import java.util.List;
4. import javax.persistence.EntityManager;
5. import javax.persistence.EntityManagerFactory;
6. import javax.persistence.Persistence;
7. import jpa.Employe;
8.
9. public class Main {
10.
11. // la requte JPQL ci-dessous ramne un employ
12. // la cl trangre [Employe].indemnite est en FetchType.LAZY
13. public static void main(String[] args) {
14.
// crer l'Entity Manager suffit construire la couche JPA
15.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("pam-jpahibernatePU");
16.
// premier essai
17.
EntityManager em = emf.createEntityManager();
18.
Employe employe = (Employe) em.createQuery("select e from Employe e where
e.nom=:nom").setParameter("nom", "Jouveinal").getSingleResult();
19.
em.close();
20.
// on affiche l'employ
21.
try {
22.
System.out.println(employe);
23.
} catch (Exception ex) {
24.
System.out.println(ex);
25.
}
26.
// deuxime essai
27.
em = emf.createEntityManager();
28.
employe = (Employe) em.createQuery("select e from Employe e left join fetch e.indemnite
where e.nom=:nom").setParameter("nom", "Jouveinal").getSingleResult();
29.
// librer les ressources
30.
em.close();
31.
// on affiche l'employ
32.
try {
33.
System.out.println(employe);
34.
} catch (Exception ex) {
35.
System.out.println(ex);
36.
}
37.
// libration ressources
38.
emf.close();
39. }
40. }

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


ligne 17 : on obtient l'EntityManager qui nous permet de dialoguer avec la couche JPA,
ligne 18 : on demande l'employ de nom Jouveinal,
ligne 19 : on ferme l'EntityManager. Cela a pour effet de fermer le contexte de persistence.
ligne 22 : on affiche l'employ reu.

La classe [Employe] est la suivante :


1.
2.
3.
4.
5.
6.

package jpa;
...
@Entity
@Table(name="EMPLOYES")

84/257

7. public class Employe implements Serializable {


8.
9.
@Id
10. @GeneratedValue(strategy = GenerationType.AUTO)
11. private Long id;
12. @Version
13. @Column(name="VERSION",nullable=false)
14. private int version;
15. @Column(name="SS", nullable=false, unique=true, length=15)
16. private String SS;
17. @Column(name="NOM", nullable=false, length=30)
18. private String nom;
19. @Column(name="PRENOM", nullable=false, length=20)
20. private String prenom;
21. @Column(name="ADRESSE", nullable=false, length=50)
22. private String adresse;
23. @Column(name="VILLE", nullable=false, length=30)
24. private String ville;
25. @Column(name="CP", nullable=false, length=5)
26. private String codePostal;
27. @ManyToOne(fetch= FetchType.LAZY)
28. @JoinColumn(name="INDEMNITE_ID",nullable=false)
29. private Indemnite indemnite;
30.
31.
32. /**
33.
* Returns a string representation of the object. This implementation constructs
34.
* that representation based on the id fields.
35.
* @return a string representation of the object.
36.
*/
37. @Override
38. public String toString() {
39.
return "jpa.Employe[id=" + getId()
40.
+ ",version="+getVersion()
41.
+",SS="+getSS()
42.
+ ",nom="+getNom()
43.
+ ",prenom="+getPrenom()
44.
+ ",adresse="+getAdresse()
45.
+",ville="+getVille()
46.
+",code postal="+getCodePostal()
47.
+",indice="+getIndemnite().getIndice()
48.
+"]";
49. }
50. ...
51. }

ligne 27 : le champ indemnite est ramen en mode LAZY,


ligne 47 : utilise le champ indemnite. Si la mthode toString est appele alors que le champ indemnite n'a pas t encore
ramen, il le sera ce moment l. Sauf si le contexte de persistance a t ferm comme dans l'exemple.

Revenons au code du [Main] :

lignes 21-25 : on devrait avoir une exception. En effet, la mthode toString va tre appele. Elle va utiliser le champ
indemnite. Celui-ci va tre cherch. Comme le contexte de persistance a t ferm, l'entit [Employe] ramene n'existe plus
d'o l'exception.
ligne 27 : on cre un nouveau EntityManager,
ligne 28 : 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 30 : on ferme l'EntityManager,
lignes 32-36 : 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 65.
Par ailleurs, le fichier [persistence.xml] doit tre modifi :

85/257

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


2. <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
3.
<persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
4.
<provider>org.hibernate.ejb.HibernatePersistence</provider>
5.
<class>jpa.Cotisation</class>
6.
<class>jpa.Employe</class>
7.
<class>jpa.Indemnite</class>
8.
<properties>
9.
<property name="javax.persistence.jdbc.url"
value="jdbc:mysql://localhost:3306/dbpam_hibernate"/>
10.
<property name="javax.persistence.jdbc.password" value=""/>
11.
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
12.
<property name="javax.persistence.jdbc.user" value="root"/>
13.
<property name="hibernate.cache.provider_class"
value="org.hibernate.cache.NoCacheProvider"/>
14.
</properties>
15. </persistence-unit>
16. </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. org.hibernate.LazyInitializationException: could not initialize proxy - no Session
2. jpa.Employe[id=31,version=0,SS=254104940426058,nom=Jouveinal,prenom=Marie,adresse=5 rue des
oiseaux,ville=St Corentin,code postal=49203,indice=2]

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.
On obtient les rsultats suivants :
1. jpa.Employe[id=453,version=1,SS=254104940426058,nom=Jouveinal,prenom=Marie,adresse=5 rue
des oiseaux,ville=St Corentin,code postal=49203,indice=2]
2. jpa.Employe[id=453,version=1,SS=254104940426058,nom=Jouveinal,prenom=Marie,adresse=5 rue
des oiseaux,ville=St Corentin,code postal=49203,indice=2]

En mode LAZY, les deux requtes ont ramen l'indemnit avec l'employ, ce qui tait inattendu. Lorsqu'on se renseigne sur le web
sur cette bizarrerie, on dcouvre que l'annotation [FetchType.LAZY] (ligne 1) :
1.
@ManyToOne(fetch= FetchType.LAZY)
2.
@JoinColumn(name="INDEMNITE_ID",nullable=false)
3. private Indemnite indemnite;

n'est pas un ordre mais un souhait. L'implmenteur JPA n'est pas oblig de le suivre. On voit donc que le code devient parfois
dpendant de l'implmentation JPA utilise. Il est possible de donner par configuration EclipseLink le comportement attendu
pour le mode LAZY.

86/257

4.6.6

Pour la suite

L'architecture de l'application construire est la suivante :

Couche
[DAO]

Couche
[metier]

Couche
[ui]

Objets image
de la BD

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Spring

Pour la suite du document, on dupliquera le projet Maven [mv-pam-jpa-hibernate] dans le projet [mv-pam-spring-hibernate] [1, 2,
3] :

puis on renomme le nouveau projet [4, 5, 6].

On changera les dpendances du nouveau projet. Le fichier [pom.xml] devient le suivant :


1. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven4.0.0.xsd">
3.
<modelVersion>4.0.0</modelVersion>
4.
5.
<groupId>istia.st</groupId>
6.
<artifactId>mv-pam-spring-hibernate</artifactId>
7.
<version>1.0-SNAPSHOT</version>
8.
<packaging>jar</packaging>
9.
10. <name>mv-pam-spring-hibernate</name>

87/257

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.

<url>http://maven.apache.org</url>
<repositories>
<repository>
<url>http://repo1.maven.org/maven2/</url>
<id>swing-layout</id>
<layout>default</layout>
<name>Repository for library Library[swing-layout]</name>
</repository>
</repositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.1.2</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>mysql</groupId>

88/257

74.
<artifactId>mysql-connector-java</artifactId>
75.
<version>5.1.6</version>
76.
</dependency>
77.
<dependency>
78.
<groupId>org.swinglabs</groupId>
79.
<artifactId>swing-layout</artifactId>
80.
<version>1.0.3</version>
81.
</dependency>
82. </dependencies>
83. </project>

lignes 25-31 : la dpendance pour les tests JUnit,


lignes 32-41 : les dpendances pour le pool de connexions Apache DBCP,
lignes 42-65 : les dpendances pour le framework Spring,
lignes 67-71 : les dpendances pour l'implmentation JPA / Hibernate,
lignes 72-76 : la dpendance pour le pilote JDBC de MySQL,
lignes 77-81 : la dpendance pour l'interface Swing. Celle-ci est automatiquement ajoute par Netbeans lorsqu'on ajoute
une interface Swing au projet.

Par ailleurs, on gnrera les deux bases MySQL :

[dbpam_hibernate] partir du script [dbpam_hibernate.sql],

[dbpam_eclipselink] partir du script [dbpam_eclipselink.sql],

4.7

Les interfaces des couches [metier] et [DAO]

Note : le prochain travail pratique est au paragraphe 4.9, page 97. D'ici l, il faut lire le cours.
Revenons l'architecture de l'application :

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

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. dos>java -jar pam-spring-ui-metier-dao-jpa-eclipselink.jar 254104940426058 150 20
2.
3. Valeurs saisies :
4. N de scurit sociale de l'employ : 254104940426058
5. Nombre d'heures travailles : 150
6. Nombre de jours travaills : 20
7.
8. Informations Employ :
9. Nom : Jouveinal
10. ...
11.
12. Informations Cotisations :
13. CSGRDS : 3.49 %
14. ...
15.
16. Informations Indemnits :

89/257

17. ...
18.
19. Informations Salaire :
20. Salaire de base : 362.25 euro
21. Cotisations sociales : 97.48 euro
22. Indemnits d'entretien : 42.0 euro
23. Indemnits de repas : 62.0 euro
24. 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
:

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

Un certain nombre d'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. package metier;
2.
3. public interface IMetier {
4.
// obtenir la feuille de salaire
5.
public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int
nbJoursTravaills );
6. }

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. La classe
[FeuilleSalaire] pourrait tre la suivante :

1. package metier;
2.
3. import jpa.Cotisation;
4. import jpa.Employe;
5. import jpa.Indemnite;
6.
7. public class FeuilleSalaire {
8.
// champs privs
9.
private Employe employe;
10. private Cotisation cotisation;
11. private ElementsSalaire elementsSalaire;
12.
13. ...
14. }

90/257

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]

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

2
1

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.
3.
4.
5.
6.
7.
8.

package metier;

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

import java.util.List;
import jpa.Employe;

public interface IMetier {


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

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. package dao;
2.
3. import java.util.List;
4. import jpa.Cotisation;
5.
6. public interface ICotisationDao {
7.
// crer une nouvelle cotisation
8.
public Cotisation create(Cotisation cotisation);
9.
// modifier une cotisation existante
10. public Cotisation edit(Cotisation cotisation);
11.
// supprimer une cotisation existante
12. public void destroy(Cotisation cotisation);
13.
// chercher une cotisation particulire
14. public Cotisation find(Long id);
15.
// obtenir tous les objets Cotisation
16. public List<Cotisation> findAll();
17.
18. }

91/257

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

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 de plus 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.
// crer une nouvelle cotisation
2. 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 :

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.

92/257

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.
// crer une nouvelle cotisation
2. 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
:
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 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.
// crer une nouvelle cotisation
2. 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. package dao;
2.
3. import java.util.List;
4. import jpa.Cotisation;
5.
6. public interface ICotisationDao {
7.
// crer une nouvelle cotisation
8.
public Cotisation create(Cotisation cotisation);
9.
// modifier une cotisation existante
10. public Cotisation edit(Cotisation cotisation);
11.
// supprimer une cotisation existante
12. public void destroy(Cotisation cotisation);
13.
// chercher une cotisation particulire
14. public Cotisation find(Long id);

93/257

15.
// obtenir tous les objets Cotisation
16. public List<Cotisation> findAll();
17.
18. }

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.

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. package dao;
2.
3. import java.util.List;
4. import jpa.Indemnite;
5.
6. public interface IIndemniteDao {
7.
// crer une entit Indemnite
8.
public Indemnite create(Indemnite indemnite);
9.
// modifier une entit Indemnite
10. public Indemnite edit(Indemnite indemnite);
11.
// supprimer une entit Indemnite
12. public void destroy(Indemnite indemnite);
13.
// rechercher une entit Indemnite via son identifiant
14. public Indemnite find(Long id);
15.
// obtenir toutes les entits Indemnite
16. public List<Indemnite> findAll();
17.
18. }

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. package dao;
2.
3. import java.util.List;
4. import jpa.Employe;
5.
6. public interface IEmployeDao {
7.
// crer une nouvelle entit Employe
8.
public Employe create(Employe employe);
9.
// modifier une entit Employe existante
10. public Employe edit(Employe employe);
11.
// supprimer une entit Employe

94/257

12.
13.
14.
15.
16.
17.
18.
19. }

4.8

public void destroy(Employe employe);


// chercher une entit Employe via son identifiant id
public Employe find(Long id);
// chercher une entit Employe via son n SS
public Employe find(String SS);
// obtenir toutes les entits Employe
public List<Employe> findAll();

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 16 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 8 : la mthode [create] cre une nouvelle entit [Employe]
ligne 10 : la mthode [edit] modifie une entit [Employe] existante
ligne 12 : la mthode [destroy] supprime une entit [Employe] existante
ligne 14 : la mthode [find] permet de retrouver une entit [Employe] existante via son identifiant id
ligne 16 : 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 18 : la mthode [findAll] rend dans une liste toutes les entits [Employe] existantes. Nous avons vu que cette
mthode tait ncessaire l'application graphique.

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.

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 :

95/257

Son code est le suivant :


1. package exception;
2.
3. @SuppressWarnings("serial")
4. public class PamException extends RuntimeException {
5.
6.
// code d'erreur
7.
private int code;
8.
9.
public PamException(int code) {
10.
super();
11.
this.code = code;
12. }
13.
14. public PamException(String message, int code) {
15.
super(message);
16.
this.code = code;
17. }
18.
19. public PamException(Throwable cause, int code) {
20.
super(cause);
21.
this.code = code;
22. }
23.
24. public PamException(String message, Throwable cause, int code) {
25.
super(message, cause);
26.
this.code = code;
27. }
28.
29. // getter et setter
30.
31. public int getCode() {
32.
return code;
33. }
34.
35. public void setCode(int code) {
36.
this.code = code;
37. }
38.
39. }

ligne 4 : [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 7. Les trois constructeurs des
lignes 14, 19 et 24 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 :

96/257

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

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

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

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

4.9.1

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Spring

Implmentation

Lectures conseilles : paragraphe 3.1.3 de [ref1]


Question : 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.
Les classes d'implmentation feront partie du paquetage [dao] :

Note : les classes du paquetage [dao] auront la forme suivante :


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

package dao;
...
import org.springframework.transaction.annotation.Transactional;
@Transactional
public class CotisationDao implements ICotisationDao {

97/257

9.
@PersistenceContext
10. private EntityManager em;
11.
12. // constructeur
13. public CotisationDao() {
14. }
15.
16. // crer une cotisation
17. public Cotisation create(Cotisation cotisation) {
18.
try{
19.
em.persist(cotisation);
20.
return cotisation;
21.
}catch (Throwable th){
22.
throw new PamException(th,11);
23.
}
24. }
25. ....
26.
27. }

4.9.2

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 28, 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 dans un type [PamException] (ligne 22).

Configuration

Lectures conseilles : paragraphe 3.1.5 de [ref1]


L'intgration DAO / JPA est configure par le fichier Spring [spring-config-dao.xml] et le fichier JPA [persistence.xml] :

Question : 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 [dbpam_hibernate.sql]. 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 : suivre le paragraphe 3.1.5 de [ref1]

98/257

4.9.3

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

4.9.4

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Spring

InitDB

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.

99/257

2
1

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. package dao;
2.
3. ...
4.
5. public class JUnitInitDB {
6.
7.
static private IEmployeDao employeDao = null;
8.
static private ICotisationDao cotisationDao = null;
9.
static private IIndemniteDao indemniteDao = null;
10.
11. @BeforeClass
12. public static void init(){
13.
// configuration de l'application
14.
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-dao.xml");
15.
// couches DAO
16.
employeDao = (IEmployeDao) ctx.getBean("employeDao");
17.
cotisationDao = (ICotisationDao) ctx.getBean("cotisationDao");
18.
indemniteDao = (IIndemniteDao) ctx.getBean("indemniteDao");
19. }
20.
21. @Test
22. public void initDB(){
23.
// on remplit la base
24. ...
25.
// on affiche le contenu de la base
26. ...
27. }
28.
29. @Before()
30. public void clean(){
31.
// on vide la base
32. ...
33. }
34. }

100/257

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 12) ce qui entrane que les champs qu'elle utilise doivent tre galement statiques
(lignes 7-9).
la mthode [clean] est excute avant chaque test (annotation @Before). Elle vide la base de donnes.
la mthode [initDB] est un test (annotation @Test). C'est le seul. Un test doit contenir des instructions d'assertion
Assert.assertCondition. 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.

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

4.9.5

Mise en oeuvre des tests

Nous sommes dsormais prts pour excuter [InitDB]. Nous dcrivons la procdure avec le SGBD MySQL5 :

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

4
6

le projet est construit [4]

101/257

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 Assert.assertCondition 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. ------------- Standard Output --------------2. Employs ---------------------3. jpa.Employe[id=5,version=0,SS=254104940426058,nom=Jouveinal,prenom=Marie,adresse=5 rue des
oiseaux,ville=St Corentin,code postal=49203,indice=2]
4. jpa.Employe[id=6,version=0,SS=260124402111742,nom=Laverti,prenom=Justine,adresse=La
brlerie,ville=St Marcel,code postal=49014,indice=1]
5. Indemnits ---------------------6. jpa.Indemnite[id=5,version=0,indice=1,base heure=1.93,entretien jour2.0,repas
jour=3.0,indemnits CP=12.0]
7. jpa.Indemnite[id=6,version=0,indice=2,base heure=2.1,entretien jour2.1,repas
jour=3.1,indemnits CP=15.0]
8. Cotisations ---------------------9. jpa.Cotisation[id=3,version=0,csgrds=3.49,csgd=6.15,secu=9.39,retraite=7.88]
10. ------------- ---------------- ---------------

Les tables [EMPLOYES, INDEMNITES, COTISATIONS] ont t remplies. On peut le vrifier avec une connexion Netbeans la
base [dbpam_hibernate].
2

4.9.6

en [1], 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 sera le suivant :

102/257

1. package dao;
2.
3. import exception.PamException;
4. ...
5.
6. public class JUnitDao {
7.
8. // couches DAO
9.
static private IEmployeDao employeDao;
10. static private IIndemniteDao indemniteDao;
11. static private ICotisationDao cotisationDao;
12.
13. @BeforeClass
14. public static void init() {
15.
// log
16.
log("init");
17.
// configuration de l'application
18.
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-dao.xml");
19.
// couches DAO
20.
employeDao = (IEmployeDao) ctx.getBean("employeDao");
21.
indemniteDao = (IIndemniteDao) ctx.getBean("indemniteDao");
22.
cotisationDao = (ICotisationDao) ctx.getBean("cotisationDao");
23. }
24.
25. @AfterClass
26. public static void terminate() {
27. }
28.
29. @Before()
30. public void clean() {
31. ...
32. }
33.
34. // logs
35. private static void log(String message) {
36.
System.out.println("----------- " + message);
37. }
38.
39. // tests
40. @Test
41. public void test01() {
42.
log("test01");
43.
// liste des cotisations
44.
List<Cotisation> cotisations = cotisationDao.findAll();
45.
int nbCotisations = cotisations.size();
46.
// on ajoute une cotisation
47.
Cotisation cotisation = cotisationDao.create(new Cotisation(3.49, 6.15, 9.39, 7.88));
48.
// on la demande
49.
cotisation = cotisationDao.find(cotisation.getId());
50.
// vrification
51.
Assert.assertNotNull(cotisation);
52.
Assert.assertEquals(3.49, cotisation.getCsgrds(), 1e-6);
53.
Assert.assertEquals(6.15, cotisation.getCsgd(), 1e-6);
54.
Assert.assertEquals(9.39, cotisation.getSecu(), 1e-6);
55.
Assert.assertEquals(7.88, cotisation.getRetraite(), 1e-6);
56.
// on la modifie
57.
cotisation.setCsgrds(-1);
58.
cotisation.setCsgd(-1);
59.
cotisation.setRetraite(-1);
60.
cotisation.setSecu(-1);
61.
Cotisation cotisation2 = cotisationDao.edit(cotisation);
62.
// vrifications

103/257

63.
Assert.assertEquals(cotisation.getVersion() + 1, cotisation2.getVersion());
64.
Assert.assertEquals(-1, cotisation2.getCsgrds(), 1e-6);
65.
Assert.assertEquals(-1, cotisation2.getCsgd(), 1e-6);
66.
Assert.assertEquals(-1, cotisation2.getRetraite(), 1e-6);
67.
Assert.assertEquals(-1, cotisation2.getSecu(), 1e-6);
68.
// on demande l'lment modifi
69.
Cotisation cotisation3 = cotisationDao.find(cotisation2.getId());
70.
// vrifications
71.
Assert.assertEquals(cotisation3.getVersion(), cotisation2.getVersion());
72.
Assert.assertEquals(-1, cotisation3.getCsgrds(), 1e-6);
73.
Assert.assertEquals(-1, cotisation3.getCsgd(), 1e-6);
74.
Assert.assertEquals(-1, cotisation3.getRetraite(), 1e-6);
75.
Assert.assertEquals(-1, cotisation3.getSecu(), 1e-6);
76.
// on supprime l'lment
77.
cotisationDao.destroy(cotisation3);
78.
// vrifications
79.
Cotisation cotisation4 = cotisationDao.find(cotisation3.getId());
80.
Assert.assertNull(cotisation4);
81.
cotisations = cotisationDao.findAll();
82.
Assert.assertEquals(nbCotisations, cotisations.size());
83. }
84.
85.
86. @Test
87. public void test02(){
88.
log("test02");
89.
// on demande la liste des indemnits
90. ...
91.
// on ajoute une Indemnite indemnite
92. ..
93.
// on va chercher indemnite en base on rcupre indemnite1
94. ..
95.
// on vrifie que indemnite1 = indemnite
96. ...
97.
// on modifie l'indemnit obtenue et on persiste la modification en BD. On obtient
indemnite2
98. ...
99.
// on vrifie la version de indemnite2
100.
...
101.
// on va chercher indemnite2 en base on obtient indemnite3
102.
...
103.
// on vrifie que indemnite3 = indemnite2
104.
...
105.
// on supprime en base l'image de indemnite3
106.
...
107.
// on va chercher indemnite3 en base
108.
...
109.
// on vrifie qu'on a obtenu une rfrence null
110.
...
111.
}
112.
113.
@Test
114.
public void test03(){
115.
log("test03");
116.
// on rpte un test analogue aux prcdents pour Employe
117.
...
118.
}
119.
120.
@Test
121.
public void test04(){
122.
log("test04");
123.
// on teste la mthode [IEmployeDao].find(String SS)
124.
// d'abord avec un employ existant

104/257

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.

// 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(){
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
// on le vrifie
...
}

// getters et setters
...

105/257

Dans la classe de tests prcdente, la base est vide avant chaque test.
Question : 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 = cotisationDao.findAll();
6.
int nbCotisations = cotisations.size();
7.
// on ajoute une cotisation
8.
Cotisation cotisation = cotisationDao.create(new Cotisation(3.49, 6.15, 9.39, 7.88));
9.
// on la demande
10.
cotisation = cotisationDao.find(cotisation.getId());
11.
// vrification
12.
Assert.assertNotNull(cotisation);
13.
Assert.assertEquals(0, cotisation.getCsgrds(), 1e-6);
14.
Assert.assertEquals(6.15, cotisation.getCsgd(), 1e-6);
15.
Assert.assertEquals(9.39, cotisation.getSecu(), 1e-6);
16.
Assert.assertEquals(7.88, cotisation.getRetraite(), 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 :

106/257

1
2

4.10

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 [metier] de l'application [PAM]

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

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

4.10.1

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Spring

L'interface Java [IMetier]

Celle-ci a t dcrite au paragraphe ??, page 91. Nous la rappelons ci-dessous :


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

package metier;
import java.util.List;
import jpa.Employe;

public interface IMetier {


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

L'implmentation de la couche [metier] sera faite dans un paquetage [metier] :

107/257

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 90. Nous revenons dessus maintenant.

4.10.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. package metier;
2.
3. import jpa.Cotisation;
4. import jpa.Employe;
5. import jpa.Indemnite;
6.
7. public class FeuilleSalaire implements Serializable{
8.
// champs privs
9.
private Employe employe;
10. private Cotisation cotisation;
11. private ElementsSalaire elementsSalaire;
12.
13. // constructeurs
14. public FeuilleSalaire() {
15.
16. }
17.
18. public FeuilleSalaire(Employe employe, Cotisation cotisation, ElementsSalaire
elementsSalaire) {
19.
setEmploye(employe);
20.
setCotisation(cotisation);
21.
setElementsSalaire(elementsSalaire);
22. }
23.
24. // toString
25. public String toString() {
26.
return "[" + employe + "," + cotisation + "," + elementsSalaire + "]";
27. }
28.
29. // accesseurs
30. ...
31. }

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 29 et au-del : les accesseurs publics aux champs privs de la classe

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 java.io.Serializable;
4.
5. public class ElementsSalaire implements Serializable{
6.
7.
// champs privs
8.
private double salaireBase;
9.
private double cotisationsSociales;
10. private double indemnitesEntretien;

108/257

11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.

private double indemnitesRepas;


private double salaireNet;
// constructeurs
public ElementsSalaire() {
}
public ElementsSalaire(double salaireBase, double cotisationsSociales,
double indemnitesEntretien, double indemnitesRepas,
double salaireNet) {
setSalaireBase(salaireBase);
setCotisationsSociales(cotisationsSociales);
setIndemnitesEntretien(indemnitesEntretien);
setIndemnitesRepas(indemnitesRepas);
setSalaireNet(salaireNet);
}

// toString
@Override
public String toString() {
return "[salaire base=" + salaireBase + ",cotisations sociales=" + cotisationsSociales
+ ",indemnits d'entretien="
33.
+ indemnitesEntretien + ",indemnits de repas=" + indemnitesRepas + ",salaire net="
34.
+ salaireNet + "]";
35. }
36.
37. // getters et setters
38. ....
39. }

4.10.3

ligne 3 : la classe implmente l'interface Serializable parce qu'elle est un composant de la classe FeuilleSalaire qui doit tre
srialisable.
ligne 6 : le salaire de base
ligne 7 : les cotisations sociales payes sur ce salaire de base
ligne 8 : les indemnits journalires d'entretien de l'enfant
ligne 9 : les indemnits journalires de repas de l'enfant
ligne 10 : le salaire net payer l'assistante maternelle
lignes 12-27 : les constructeurs de la classe
lignes 30-35 : mthode [toString] identifiant un objet [ElementsSalaire] particulier
lignes 38 et au-del : les accesseurs publics aux champs privs de la classe

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

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


1. package metier;
2.
3. ...
4.
5. @Transactional
6. public class Metier implements IMetier {
7.
8.
// rfrences sur la couche [DAO]
9.
private ICotisationDao cotisationDao = null;
10. private IEmployeDao employeDao=null;
11.
12.
13. // obtenir la feuille de salaire
14. public FeuilleSalaire calculerFeuilleSalaire(String SS,
15.
double nbHeuresTravailles, int nbJoursTravaills) {
16. ...

109/257

17. }
18.
19. // liste des employs
20.
public List<Employe> findAllEmployes() {
21.
...
22. }
23.
24. // getters et setters
25. ...
26. }

ligne 5 : 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 9-10 : les rfrences sur les couches [DAO] des entits [Cotisation, Employe, Indemnite] ;
lignes 14-17 : la mthode [calculerFeuilleSalaire] ;
lignes 20-22 : la mthode [findAllEmployes] ;
ligne 24 et au-del : les accesseurs publics des champs privs de la classe.

Question : crire le code de la mthode [findAllEmployes].


Question : crire le code de la mthode [calculerFeuilleSalaire].
On notera les points suivants :

le mode de calcul du salaire a t expliqu au paragraphe 4.2, page 62.

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

Tests de la couche [metier]

Nous crons deux programmes de test :


1

2
3
Les classes de tests [3] sont crs dans un paquetage [metier] [2] de la branche [Test Packages] [1] du projet.
La classe [JUnitMetier_1] pourrait tre la suivante :
1. package metier;
2.
3. ...
4.
5. public class JUnitMetier_1 {
6.
7. // couches dao
8.
static private IMetier metier;
9.
10. @BeforeClass
11. public static void init(){
12.
// log
13.
log("init");
14.
// configuration de l'application
15.
// instanciation couche [metier]

110/257

16.

ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-metierdao.xml");


17.
metier = (IMetier) ctx.getBean("metier");
18.
// couches dao
19.
IEmployeDao employeDao=(IEmployeDao) ctx.getBean("employeDao");
20.
ICotisationDao cotisationDao=(ICotisationDao) ctx.getBean("cotisationDao");
21.
IIndemniteDao indemniteDao=(IIndemniteDao) ctx.getBean("indemniteDao");
22.
// on vide la base
23.
for(Employe employe:employeDao.findAll()){
24.
employeDao.destroy(employe);
25.
}
26.
for(Cotisation cotisation:cotisationDao.findAll()){
27.
cotisationDao.destroy(cotisation);
28.
}
29.
for(Indemnite indemnite : indemniteDao.findAll()){
30.
indemniteDao.destroy(indemnite);
31.
}
32.
// on la remplit
33.
Indemnite indemnite1=new Indemnite(1,1.93,2,3,12);
34.
Indemnite indemnite2=new Indemnite(2,2.1,2.1,3.1,15);
35.
indemniteDao.create(indemnite1);
36.
indemniteDao.create(indemnite2);
37.
employeDao.create(new Employe("254104940426058","Jouveinal","Marie","5 rue des
oiseaux","St Corentin","49203",indemnite2));
38.
employeDao.create(new Employe("260124402111742","Laverti","Justine","La brlerie","St
Marcel","49014",indemnite1));
39.
cotisationDao.create(new Cotisation(3.49,6.15,9.39,7.88));
40. }
41.
42. @AfterClass
43. public static void terminate() {
44. }
45.
46. // logs
47. static private void log(String message) {
48.
System.out.println("----------- " + message);
49. }
50.
51. // test
52. @Test
53. public void test01(){
54.
// calcul de feuilles de salaire
55.
System.out.println(metier.calculerFeuilleSalaire("260124402111742",30, 5));
56.
System.out.println(metier.calculerFeuilleSalaire("254104940426058",150, 20));
57.
try {
58.
System.out.println(metier.calculerFeuilleSalaire("xx", 150, 20));
59.
} catch (PamException ex) {
60.
System.err.println(String.format("PamException[Code=%d, message=%s]",ex.getCode(),
ex.getMessage()));
61.
}
62. }
63. }

Il n'y a pas d'assertion Assert.assertCondition 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.
2.
3.
4.

Testsuite: metier.JUnitMetier_1
----------- init
....
[jpa.Employe[id=22,version=0,SS=260124402111742,nom=Laverti,prenom=Justine,adresse=La
brlerie,ville=St Marcel,code
postal=49014,indice=1],JPA.Cotisation[id=6,version=0,csgrds=3.49,csgd=6.15,secu=9.39,retrai
te=7.88],JPA.Indemnite[id=29,version=0,indice=1,base heure=1.93,entretien jour2.0,repas

111/257

jour=3.0,indemnits CP=12.0],[salaire base=64.85,cotisations sociales=17.45,indemnits


d'entretien=10.0,indemnits de repas=15.0,salaire net=72.4]]
5. [jpa.Employe[id=21,version=0,SS=254104940426058,nom=Jouveinal,prenom=Marie,adresse=5 rue
des oiseaux,ville=St Corentin,code
postal=49203,indice=2],JPA.Cotisation[id=6,version=0,csgrds=3.49,csgd=6.15,secu=9.39,retrai
te=7.88],jpa.Indemnite[id=30,version=0,indice=2,base heure=2.1,entretien jour2.1,repas
jour=3.1,indemnits CP=15.0],[salaire base=362.25,cotisations sociales=97.48,indemnits
d'entretien=42.0,indemnits de repas=62.0,salaire net=368.77]]
6. PamException[Code=101, message=L'employ de n[xx] est introuvable]
7. Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 3,234 sec

ligne 4 : la feuille de salaire de Justine Laverti


ligne 5 : la feuille de salaire de Marie Jouveinal
ligne 6 : l'exception due au fait que l'employ de n SS 'xx' n'existe pas.

Question : la ligne 17 de [JUnitMetier_1] utilise le bean Spring nomm metier. Donner la dfinition de ce bean dans le fichier
[spring-config-metier-dao.xml]. 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. package metier;
2.
3. ...
4. public class JUnitMetier_2 {
5.
6. // couche mtier
7.
static private IMetier metier;
8.
9.
@BeforeClass
10. public static void init(){
11. ...
12. }
13.
14. // logs
15. static private void log(String message) {
16.
System.out.println("----------- " + message);
17. }
18.
19. // test
20. @Test
21. public void test01(){
22. ...
23. }
24. }

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

112/257

4.11

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

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

Couche
[ui]

Couche
[metier]

Objets image
de la BD

Couche
[DAO]

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

La classe [ui.console.Main]

Nous nous intressons tout d'abord l'application console implmente par la classe [ui.console.Main] ci-dessus. Son
fonctionnement a t dcrit au paragraphe 4.3, page 63. Le squelette de la classe [Main] pourrait tre le suivant :
1. package ui.console;
2.
3. import exception.PamException;
4. import metier.FeuilleSalaire;
5. import metier.IMetier;
6.
7. import java.util.ArrayList;
8. import org.springframework.context.ApplicationContext;
9. import org.springframework.context.support.ClassPathXmlApplicationContext;
10.
11. public class Main {
12.
13. /**
14.
* @param args
15.
*/
16. public static void main(String[] args) {
17.
// donnes locales
18.
final String syntaxe = "pg num_securite_sociale nb_heures_travailles
nb_jours_travaills";
19.
// on vrifie le nombre de paramtres
20. ...
21.
// liste des erreurs
22.
ArrayList erreurs = new ArrayList();
23.
// le second paramtre doit tre un nombre rel >0
24. ...
25.
// erreur ?
26.
if (...) {
27.
erreurs.add("Le nombre d'heures travailles [" + args[1]

113/257

28.
+ "] est erron");
29.
}
30.
// le troisime paramtre doit tre un nombre entier >0
31. ...
32.
// erreur ?
33.
if (...) {
34.
erreurs.add("Le nombre de jours travaills [" + args[2]
35.
+ "] est erron");
36.
}
37.
// des erreurs ?
38.
if (erreurs.size() != 0) {
39.
for (int i = 0; i < erreurs.size(); i++) {
40.
System.err.println(erreurs.get(i));
41.
}
42.
return;
43.
}
44.
// c'est bon - on peut demander la feuille de salaire
45.
FeuilleSalaire feuilleSalaire = null;
46.
try {
47.
// instanciation couche [metier]
48.
...
49.
// calcul de la feuille de salaire
50.
...
51.
} catch (PamException ex) {
52.
System.err.println("L'erreur suivante s'est produite : "+ ex.getMessage());
53.
return;
54.
} catch (Exception ex) {
55.
System.err.println("L'erreur suivante s'est produite : "+ ex.toString());
56.
return;
57.
}
58.
59.
// affichage dtaill
60.
String output = "Valeurs saisies :\n";
61.
output += ajouteInfo("N de scurit sociale de l'employ", args[0]);
62.
output += ajouteInfo("Nombre d'heures travailles", args[1]);
63.
output += ajouteInfo("Nombre de jours travaills", args[2]);
64.
output += ajouteInfo("\nInformations Employ", "");
65.
output += ajouteInfo("Nom", feuilleSalaire.getEmploye().getNom());
66.
output += ajouteInfo("Prnom", feuilleSalaire.getEmploye().getPrenom());
67.
output += ajouteInfo("Adresse", feuilleSalaire.getEmploye().getAdresse());
68.
output += ajouteInfo("Ville", feuilleSalaire.getEmploye().getVille());
69.
output += ajouteInfo("Code Postal", feuilleSalaire.getEmploye().getCodePostal());
70.
output += ajouteInfo("Indice", ""+
feuilleSalaire.getEmploye().getIndemnite().getIndice());
71.
output += ajouteInfo("\nInformations Cotisations", "");
72.
output += ajouteInfo("CSGRDS", ""+ feuilleSalaire.getCotisation().getCsgrds() + " %");
73.
output += ajouteInfo("CSGD", ""+ feuilleSalaire.getCotisation().getCsgd() + " %");
74.
output += ajouteInfo("Retraite", ""+ feuilleSalaire.getCotisation().getRetraite() + "
%");
75.
output += ajouteInfo("Scurit sociale", ""+ feuilleSalaire.getCotisation().getSecu() +
" %");
76.
output += ajouteInfo("\nInformations Indemnits", "");
77.
output += ajouteInfo("Salaire horaire", ""+
feuilleSalaire.getEmploye().getIndemnite().getBaseHeure() + " euro");
78.
output += ajouteInfo("Entretien/jour", ""+
feuilleSalaire.getEmploye().getIndemnite().getEntretienJour() + " euro");
79.
output += ajouteInfo("Repas/jour", ""+
feuilleSalaire.getEmploye().getIndemnite().getRepasJour() + " euro");
80.
output += ajouteInfo("Congs Pays", ""+
feuilleSalaire.getEmploye().getIndemnite().getIndemnitesCP()+ " %");
81.
output += ajouteInfo("\nInformations Salaire", "");
82.
output += ajouteInfo("Salaire de base", ""+
feuilleSalaire.getElementsSalaire().getSalaireBase()+ " euro");

114/257

83.

output += ajouteInfo("Cotisations sociales", ""+


feuilleSalaire.getElementsSalaire().getCotisationsSociales()+ " euro");
84.
output += ajouteInfo("Indemnits d'entretien", ""+
feuilleSalaire.getElementsSalaire().getIndemnitesEntretien()+ " euro");
85.
output += ajouteInfo("Indemnits de repas", ""+
feuilleSalaire.getElementsSalaire().getIndemnitesRepas()+ " euro");
86.
output += ajouteInfo("Salaire net", ""+
feuilleSalaire.getElementsSalaire().getSalaireNet() + " euro");
87.
88.
System.out.println(output);
89. }
90.
91. static String ajouteInfo(String message, String valeur) {
92.
return message + " : " + valeur + "\n";
93. }
94. }

Question : complter le code ci-dessus.

4.11.2

Excution

Pour excuter la classe [ui.console.Main], on procdera de la faon suivante :

3
5
6
2

115/257

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,
slectionner la classe [4],
la classe apparat en [5]. 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 [6],
ceci fait, on peut excuter le projet [7]. La configuration prcdente fait que c'est la classe [ui.console.Main] qui va tre
excute.

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

4.12

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

116/257

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


en[2] : l'interface graphique

4.12.1

Un rapide tutoriel

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

1
3
2

[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

117/257

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


[6] : on place le formulaire dans un paquetage
[8] : le formulaire est ajout l'arborescence du projet
[9] : le formulaire est accessible selon deux perspectives : [Design] [9] 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

12

10

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

118/257

15

14
13

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

119/257

17

18

16

19

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]

20

21

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

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


[http://www.netbeans.org/kb/trails/matisse.html].

4.12.2

L'interface graphique [PamJFrame]

On construira l'interface graphique suivante :

120/257

1
2

en [1], l'interface graphique


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

JLabel1

JPanel1

JPanel2

121/257

JPanel3

JPanel4

JPanel5

Travail pratique : construire l'interface graphique prcdente en s'aidant du tutoriel


[http://www.netbeans.org/kb/trails/matisse.html].

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

122/257

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


1.
2.
3. }

private void jButtonSalaireActionPerformed(java.awt.event.ActionEvent 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.
jButtonSalaire.setText("Salaire");
2.
jButtonSalaire.addActionListener(new java.awt.event.ActionListener() {
3.
public void actionPerformed(java.awt.event.ActionEvent evt) {
4.
jButtonSalaireActionPerformed(evt);
5.
}
6. });

Ce sont les lignes 2-5 qui indiquent que le clic (evt de type ActionPerformed) sur le bouton [jButtonSalaire] (ligne 2) doit tre 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(javax.swing.event.CaretEvent evt) {
...
}

Le code Java qui associe la mthode prcdente l'vnement [caretUpdate] sur le champ de saisie [jTextFieldHT] est lui aussi
gnr :
1.
jTextFieldHT.addCaretListener(new javax.swing.event.CaretListener() {
2.
public void caretUpdate(javax.swing.event.CaretEvent evt) {
3.
jTextFieldHTCaretUpdate(evt);
4.
}
5. });

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

Initialisation de l'interface graphique

123/257

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("spring-config-metierdao.xml");
3. IMetier metier = (IMetier) ctx.getBean("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. package ui.swing;
2.
3. ...
4. public class PamJFrame extends javax.swing.JFrame {
5.
6.
/** Creates new form PamJFrame */
7.
public PamJFrame() {
8.
initComponents();
9.
}
10.
11. /** This method is called from within the constructor to
12.
* initialize the form.
13.
* WARNING: Do NOT modify this code. The content of this method is
14.
* always regenerated by the Form Editor.
15.
*/
16. // <editor-fold defaultstate="collapsed" desc=" Generated Code ">
17. private void initComponents() {
18. ...
19. }// </editor-fold>
20.
21. private void jTextFieldHTCaretUpdate(javax.swing.event.CaretEvent evt) {
22. ...
23. }
24.
25. private void jButtonSalaireActionPerformed(java.awt.event.ActionEvent evt) {
26. ...
27. }
28.
29. public static void main(String args[]) {
30.
java.awt.EventQueue.invokeLater(new Runnable() {
31.
public void run() {
32.
new PamJFrame().setVisible(true);
33.
}
34.
});
35. }
36.
37. // Variables declaration - do not modify
38. private javax.swing.JButton jButtonSalaire;
39. ...
40. // End of variables declaration

124/257

41.
42. }

lignes 29-35 : la mthode statique [main] qui lance l'application


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

Pour ajouter au code prcdent nos propres initialisations, nous pouvons procder comme suit :
1. /** Creates new form PamJFrame */
2.
public PamJFrame() {
3.
initComponents();
4.
doMyInit();
5.
}
6.
7. ...
8.
9.
// variables d'instance
10. private IMetier metier=null;
11. private List<Employe> employes=null;
12. private String[] employesCombo=null;
13. private double heuresTravailles=0;
14.
15. // initialisations propritaires
16. public void doMyInit(){
17.
// init contexte
18.
try{
19.
// instanciation couche [metier]
20. ...
21. // liste des employs
22. ...
23.
}catch (PamException ex){
24. // le message de l'exception est plac dans [jTextAreaStatus]
25. ...
26. // retour
27.
return;
28.
}
29.
// bouton salaire inhib
30. ...
31.
// jScrollPane1 cach
32. ...
33.
// spinner jours travaills
34.
jSpinnerJT.setModel(new SpinnerNumberModel(0,0,31,1));
35.
// combobox employs
36.
employesCombo=new String[employes.size()];
37.
int i=0;
38.
for(Employe employe : employes){
39.
employesCombo[i++]=employe.getPrenom()+" "+employe.getNom();
40.
}
41.
jComboBoxEmployes.setModel(new DefaultComboBoxModel(employesCombo));
42. }

ligne 4 : on appelle une mthode propritaire pour faire nos propres initialisations. Celles-ci sont dfinies par le code des
lignes 10-42

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

125/257

4.12.5

Gestionnaires d'vnements

Question : 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.
Question : crire la mthode [jButtonSalaireActionPerformed] qui doit afficher la feuille de salaire de l'employ slectionn dans
[jComboBoxEmployes].

4.12.6

Excution de l'interface graphique

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

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

Le projet doit tre complet avec ses fichiers de configuration (persistence.xml, spring-config-metier-dao.xml) et la classe de
l'interface graphique. On lancera Le SGBD cible avant d'excuter le projet.

4.13

Implmentation de la couche JPA avec EclipseLink

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

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Objets image
de la BD

4.13.1

Interface
[JPA]

Implmentation
[EclipseLink]

Couche
[JDBC]

Spring

Le projet Netbeans

Le nouveau projet Netbeans est obtenu par recopie du projet prcdent :

126/257

4
3
2
5

en [1] : aprs un clic droit sur le projet Hibernate, choisir Copy


l'aide du bouton [2], choisir le dossier parent du nouveau projet. Le nom du dossier apparat en [3].
en [4], donner un nom au nouveau projet
en [5], le nom du dossier du projet

127/257

1
4
5

3
2

en [1], le nouveau projet a t cr. Il porte le mme nom que l'original,


en [2] et [3], on le renomme [mv-pam-spring-eclipselink].

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

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

Commenons par ce dernier point. Le fichier [pom.xml] pour le nouveau projet sera celui-ci :
1. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
3.
<modelVersion>4.0.0</modelVersion>
4.
5.
<groupId>istia.st</groupId>
6.
<artifactId>mv-pam-spring-eclipselink</artifactId>
7.
<version>1.0-SNAPSHOT</version>
8.
<packaging>jar</packaging>
9.
10. <name>mv-pam-spring-eclipselink</name>
11. <url>http://maven.apache.org</url>
12. <repositories>
13.
<repository>
14.
<url>http://repo1.maven.org/maven2/</url>
15.
<id>swing-layout</id>
16.
<layout>default</layout>
17.
<name>Repository for library Library[swing-layout]</name>
18.
</repository>
19.
<repository>

128/257

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.

<url>http://download.eclipse.org/rt/eclipselink/maven.repo/</url>
<id>eclipselink</id>
<layout>default</layout>
<name>Repository for library Library[eclipselink]</name>
</repository>
</repositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.0.3</version>
</dependency>

129/257

83.
<dependency>
84.
<groupId>mysql</groupId>
85.
<artifactId>mysql-connector-java</artifactId>
86.
<version>5.1.6</version>
87.
</dependency>
88.
<dependency>
89.
<groupId>org.swinglabs</groupId>
90.
<artifactId>swing-layout</artifactId>
91.
<version>1.0.3</version>
92.
</dependency>
93. </dependencies>
94. </project>

lignes 73-82 : les dpendances pour l'implmentation JPA EclipseLink,


lignes 19-24 : le dpt Maven pour EclipseLink.

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 [spring-config-metier-dao.xml] on a :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3.
xmlns:tx="http://www.springframework.org/schema/tx"
4.
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/springtx-2.0.xsd">
5.
6.
<!-- couches applicatives -->
7.
<!-- DAO -->
8.
<bean id="employeDao" class="dao.EmployeDao" />
9.
<bean id="indemniteDao" class="dao.IndemniteDao" />
10. <bean id="cotisationDao" class="dao.CotisationDao" />
11. <!-- mtier -->
12. <bean id="metier" class="metier.Metier">
13.
<property name="employeDao" ref="employeDao"/>
14.
<property name="indemniteDao" ref="indemniteDao"/>
15.
<property name="cotisationDao" ref="cotisationDao"/>
16. </bean>
17.
18. <!-- configuration JPA -->
19. <bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
20.
<property name="dataSource" ref="dataSource" />
21.
<property name="jpaVendorAdapter">
22.
<bean class="org.springframework.orm.JPA.vendor.HibernateJpaVendorAdapter">
23.
<!-24.
<property name="showSql" value="true" />
25.
-->
26.
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect"
/>
27.
<property name="generateDdl" value="true" />
28.
<!-29.
<property name="generateDdl" value="true" />
30.
-->
31.
</bean>
32.
</property>
33.
<property name="loadTimeWeaver">
34.
<bean
class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
35.
</property>
36. </bean>
37.

130/257

38.
39.

<!-- la source de donnees DBCP -->


<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroymethod="close">
40.
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
41.
<property name="url" value="jdbc:mysql://localhost:3306/dbpam_hibernate" />
42.
<property name="username" value="root" />
43. <!-44.
<property name="password" value="" />
45. -->
46. </bean>
47. ....
48. </beans>

Les lignes 19-36 configurent la couche JPA. L'implmentation JPA utilise est Hibernate (ligne 22). Par ailleurs, la base de donnes
cible est [dbpam_hibernate] (ligne 41).
Pour passer une implmentation JPA / EclipseLink, les lignes 19-35 ci-dessus sont remplaces par les lignes ci-dessous :
1.
2.

<!-- configuration JPA -->


<bean id="entityManagerFactory"
class="org.springframework.orm.JPA.LocalContainerEntityManagerFactoryBean">
3.
<property name="dataSource" ref="dataSource" />
4.
<property name="jpaVendorAdapter">
5.
<bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
6.
<!-7.
<property name="showSql" value="true" />
8.
-->
9.
<property name="databasePlatform"
value="org.eclipse.persistence.platform.database.MySQLPlatform" />
10.
<!-11.
<property name="generateDdl" value="true" />
12.
-->
13.
</bean>
14.
</property>
15.
<property name="loadTimeWeaver">
16.
<bean
class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
17.
</property>
18. </bean>

ligne 5 : l'implmentation JPA utilise est EclipseLink


ligne 9 : 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. Ici, la proprit est en
commentaires.
ligne 7 : pour visualiser sur la console les ordres SQL mis par la couche JPA. Ici, la proprit est en commentaires.

Par ailleurs, la base de donnes cible devient [dbpam_eclipselink] (ligne 4 ci-dessous) :


1. <!-- la source de donnees DBCP -->
2.
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroymethod="close">
3.
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
4.
<property name="url" value="jdbc:mysql://localhost:3306/dbpam_eclipselink" />
5.
<property name="username" value="root" />
6. <!-7.
<property name="password" value="" />
8. -->
9.
</bean>

4.13.2

Mise en oeuvre des tests

131/257

Avant de tester l'application entire, il est bon de vrifier si les tests JUnit passent avec la nouvelle implmentation JPA. Avant de les
faire, on commencera par supprimer les tables de la base de donnes. Pour cela, dans l'onglet [Runtime] de Netbeans, si besoin est,
on crera une connexion sur la base dbpam_eclipselink / MySQL5. Une fois connect la base dbpam_eclipselink / MySQL5, on
pourra procder la suppression des tables comme montr ci-dessous :

[1] : avant la suppression


[2] : aprs la suppression

Ceci fait, on peut excuter le premier test sur la couche [DAO] : InitDB qui remplit la base. Pour que les tables dtruites
prcdemment soient recres par l'application, il faut s'assurer que dans la configuration JPA / EclipseLink de Spring la ligne :
<property name="generateDdl" value="true" />

existe et n'est pas mise en commentaires.


Nous construisons le projet (Build) puis nous excutons le test [JUnitInitDB] :

en [1], le test InitDB est excut.


en [2], il choue. L'exception est lance par Spring et non par un test qui aurait chou.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class
path resource [spring-config-DAO.xml]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Must start with
Java agent to use InstrumentationLoadTimeWeaver. See Spring documentation.

132/257

Spring indique qu'il y a un problme de configuration. Le message n'est pas clair. La raison de l'exception a t explique
au paragraphe 3.1.9 de [ref1]. Pour que la configuration Spring / EclipseLink fonctionne, la JVM qui excute l'application
doit tre lance avec un paramtre particulier, un agent Java. La forme de ce paramtre est la suivante :
-javaagent:C:\...\spring-agent.jar

[spring-agent.jar] est l'agent Java dont a besoin la JVM pour grer la configuration Spring / EclipseLink.
Lorsqu'on excute un projet, il est possible de passer des arguments la JVM :

2
3

en [1], on accde aux proprits du projet ;


en [2], les proprits du Run ;
en [3], on passe le paramtre -javaagent la JVM. On trouvera l'archive [spring-agent] dans le support du cours et on
adaptera la ligne 9 au chemin rel de cette archive. S'il y a des espaces dans ce chemin, entourez-le d'apostrophes comme
dans '-javaagent:mon chemin'.

La configuration de Netbeans pour passer l'agent Java fonctionne bien pour le programme principal mais pas pour les tests (les
programmes dans la branche Tests). Pour ceux-ci, on ajoute le plugin [maven-surefire] dans le fichier [pom.xml] :
1. <build>
2.
<plugins>
3.
<plugin>
4.
<groupId>org.apache.maven.plugins</groupId>
5.
<artifactId>maven-surefire-plugin</artifactId>
6.
<version>2.12.2</version>
7.
<configuration>
8.
<forkmode>pertest</forkmode>
9.
<argLine>-javaagent:D:/Temp/12-09-10/spring-agent-2.5.6.jar</argLine>
10.
</configuration>
11.
</plugin>
12.
</plugins>
13. </build>

133/257

Le plugin [maven-surefire] est utilis pour les tests. Il permet de produire des rapports. Ici, nous ne l'utilisons que pour passer
l'agent Java la JVM (ligne 9). On trouvera l'archive [spring-agent] dans le support du cours et on adaptera la ligne 9 au chemin rel
de cette archive. S'il y a des espaces dans ce chemin, entourez-le d'apostrophes comme dans 'mon chemin'.
Note : on trouvera le [pom.xml] complet dans le support de cours.

4.13.3

InitDB

Maintenant, nous sommes prts pour tester de nouveau [InitDB]. Cette fois-ci les rsultats obtenus sont les suivants :

en [1], le test a t russi


en [2], dans l'onglet [Services], on rafrachit la connexion qu'a Netbeans avec la base [dbpam_eclipselink]
en [3], quatre tables ont t cres

5
6

4.13.4

en [5], on visualise le contenu de la table [employes]


en [6], le rsultat.

JUnitDao

Note : la classe [JUnitDao] est disponible dans le support de cours.


L'excution de la classe de tests [JUnitDao] peut chouer, mme si avec l'implmentation JPA / Hibernate, elle avait russi. Pour
comprendre pourquoi, analysons un exemple.
La mthode teste est la mthode IndemniteDao.create suivante :
1. package dao;
2.
3. ...
4. @Transactional(propagation=Propagation.REQUIRED)
5. public class IndemniteDao implements IIndemniteDao{
6.
7.
@PersistenceContext
8.
private EntityManager em;
9.
10. // constructeur
11. public IndemniteDao() {
12. }
13.
14. // crer une indemnit

134/257

15. public Indemnite create(Indemnite indemnite) {


16.
try{
17.
em.persist(indemnite);
18.
}catch(Throwable th){
19.
throw new PamException(th,31);
20.
}
21.
return indemnite;
22. }
23.
24. ...
25. }

lignes 15-22 : la mthode teste

La mthode de test est la suivante :


1. package dao;
2.
3. ...
4.
5. public class JUnitDao {
6.
7. // couches DAO
8.
static private IEmployeDao employeDao;
9.
static private IIndemniteDao indemniteDao;
10. static private ICotisationDao cotisationDao;
11.
12. @BeforeClass
13. public static void init() {
14.
// log
15.
log("init");
16.
// configuration de l'application
17.
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-DAO.xml");
18.
// couches DAO
19.
employeDao = (IEmployeDao) ctx.getBean("employeDao");
20.
indemniteDao = (IIndemniteDao) ctx.getBean("indemniteDao");
21.
cotisationDao = (ICotisationDao) ctx.getBean("cotisationDao");
22. }
23.
24. @Before()
25. public void clean() {
26.
// on vide la base
27.
for (Employe employe : employeDao.findAll()) {
28.
employeDao.destroy(employe);
29.
}
30.
for (Cotisation cotisation : cotisationDao.findAll()) {
31.
cotisationDao.destroy(cotisation);
32.
}
33.
for (Indemnite indemnite : indemniteDao.findAll()) {
34.
indemniteDao.destroy(indemnite);
35.
}
36. }
37.
38. // logs
39. private static void log(String message) {
40.
System.out.println("----------- " + message);
41. }
42.
43. // tests
44. .
45. @Test
46. public void test05() {
47.
log("test05");

135/257

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

// on cre deux indemnits avec le mme indice


// enfreint la contrainte d'unicit de l'indice
boolean erreur = true;
Indemnite indemnite1 = null;
Indemnite indemnite2 = null;
Throwable th = null;
try {
indemnite1 = indemniteDao.create(new Indemnite(1, 1.93, 2, 3, 12));
indemnite2 = indemniteDao.create(new Indemnite(1, 1.93, 2, 3, 12));
erreur = false;
} catch (PamException ex) {
th = ex;
// vrifications
Assert.assertEquals(31, ex.getCode());
} catch (Throwable th1) {
th = th1;
}
// vrifications
Assert.assertTrue(erreur);
// chane des exceptions
System.out.println("Chane des exceptions --------------------------------------");
System.out.println(th.getClass().getName());
while (th.getCause() != null) {
th = th.getCause();
System.out.println(th.getClass().getName());
}
// la 1re indemnit a du tre persiste
Indemnite indemnite = indemniteDao.find(indemnite1.getId());
// vrification
Assert.assertNotNull(indemnite);
Assert.assertEquals(1, indemnite.getIndice());
Assert.assertEquals(1.93, indemnite.getBaseHeure(), 1e-6);
Assert.assertEquals(2, indemnite.getEntretienJour(), 1e-6);
Assert.assertEquals(3, indemnite.getRepasJour(), 1e-6);
Assert.assertEquals(12, indemnite.getIndemnitesCP(), 1e-6);
// la seconde indemnit n'a pas du tre persiste
List<Indemnite> indemnites = indemniteDao.findAll();
int nbIndemnites = indemnites.size();
Assert.assertEquals(nbIndemnites, 1);

Question : expliquer ce que fait le test test05 et indiquer les rsultats attendus.
Les rsultats obtenus avec une couche JPA / Hibernate sont les suivants :
1. ----------- test05
2. 4 juin 2010 16:45:43 org.hibernate.util.JDBCExceptionReporter logExceptions
3. ATTENTION: SQL Error: 1062, SQLState: 23000
4. 4 juin 2010 16:45:43 org.hibernate.util.JDBCExceptionReporter logExceptions
5. GRAVE: Duplicate entry '1' for key 2
6. Chane des exceptions -------------------------------------7. exception.PamException
8. javax.persistence.EntityExistsException
9. org.hibernate.exception.ConstraintViolationException
10. com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException

Le test passe, c.a.d. que les assertions sont vrifies et il n'y a pas d'exception qui sort de la mthode de test.
Question : expliquer ce qui s'est pass.

136/257

Les rsultats obtenus avec une couche JPA / EclipseLink sont les suivants :
1. ----------- test05
2. [EL Warning]: 2010-06-04 16:48:26.421--UnitOfWork(749304)--Exception [EclipseLink-4002]
(Eclipse Persistence Services - 2.0.0.v20091127-r5931):
org.eclipse.persistence.exceptions.DatabaseException
3. Internal Exception: com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException:
Duplicate entry '1' for key 2
4. Error Code: 1062
5. Call: INSERT INTO INDEMNITES (ID, ENTRETIEN_JOUR, REPAS_JOUR, INDICE, INDEMNITES_CP,
BASE_HEURE, VERSION) VALUES (?, ?, ?, ?, ?, ?, ?)
6.
bind => [108, 2.0, 3.0, 1, 12.0, 1.93, 1]
7. Query: InsertObjectQuery(JPA.Indemnite[id=108,version=1,indice=1,base heure=1.93,entretien
jour2.0,repas jour=3.0,indemnits CP=12.0])
8. Chane des exceptions -------------------------------------9. org.springframework.transaction.TransactionSystemException
10. javax.persistence.RollbackException
11. org.eclipse.persistence.exceptions.DatabaseException
12. com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException

Comme prcdemment avec Hibernate, le test passe, c.a.d. que les assertions sont vrifies et il n'y a pas d'exception qui sort de la
mthode de test.
Question : expliquer ce qui s'est pass.
Question : de ces deux exemples, que peut-on conclure de l'interchangeabilit des implmentations JPA ? Est-elle totale ici ?

4.13.5

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. 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 exceptions prs : l'exemple
prcdent montre en effet que les exceptions lances par les couches [DAO] peuvent diffrer. Ainsi dans le cas d'utilisation du test,
Spring / JPA / Hibernate lance une exception de type [PamException], une exception propre l'application [pam] alors que
Spring / JPA / EclipseLink lui, lance une exception de type [TransactionSystemException], une exception du framework Spring. Si
dans le cas d'usage du test, la couche [ui] attend une exception de type [PamException] parce qu'elle a t construite avec Hibernate,
elle ne fonctionnera plus lorsqu'on passera EclipseLink.

4.13.6

Travail faire

Travail pratique : refaire les tests des applications console et swing avec diffrents SGBD : Postgres, Oracle XE, SQL Server.

137/257

5 Version 2 : Architecture OpenEJB / JPA


5.1

Introduction aux principes du portage

Note : le cours est lire jusqu'au paragraphe 5.2, page 143 o commence le travail pratique.
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 143 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]

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

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

138/257

5.1.3

Configuration de la couche JPA / EclipseLink / OpenEJB


le fichier [META-INF/persistence.xml] configurant la couche JPA devient le suivant :

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


2. <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
3.
<persistence-unit name="pam-openejb-ui-metier-dao-jpa-eclipselinkPU" transactiontype="JTA">
4.
<!-- entits JPA -->
5.
<class>jpa.Cotisation</class>
6.
<class>jpa.Employe</class>
7.
<class>jpa.Indemnite</class>
8.
<!-- le fournisseur JPA est EclipseLink -->
9.
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
10.
<!-- proprits provider -->
11.
<properties>
12.
<property name="eclipselink.ddl-generation" value="create-tables"/>
13.
</properties>
14. </persistence-unit>
15. </persistence>

ligne 3 : les transactions dans un conteneur EJB sont de type JTA (Java Transaction API). Elles taient de type
RESOURCE_LOCAL avec Spring.
ligne 9 : l'implmentation JPA utilise est EclipseLink
lignes 5-7 : les entits gres par la couche JPA
lignes 11-13 : 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/openejb.conf] suivant :

1. <?xml version="1.0"?>
2. <openejb>
3.
<Resource id="Default JDBC Database">
4.
JdbcDriver com.mysql.jdbc.Driver
5.
JdbcUrl jdbc:mysql://localhost:3306/dbpam_eclipselink
6.
UserName root
7.
</Resource>
8. </openejb>

5.1.4

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 une base MySQL [dbpam_eclipselink]

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.

package dao;
import java.util.List;
import jpa.Cotisation;
public interface ICotisationDao {
// crer une nouvelle cotisation
Cotisation create(Cotisation cotisation);
// modifier une cotisation existante

139/257

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

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 :
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 javax.ejb.Local;
@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 javax.ejb.Remote;
@Remote
public interface ICotisationDaoRemote extends ICotisationDao{
}

La couche [DAO] est implmente par un EJB implmentant les deux interfaces (ce n'est pas obligatoire) :
1. @Stateless()
2. @TransactionAttribute(TransactionAttributeType.REQUIRED)
3. public class CotisationDao implements ICotisationDaoLocal, ICotisationDaoRemote {
4.
5.
@PersistenceContext
6.
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.

140/257

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]

Couche d'accs aux


donnes [DAO]

Rseau
tcp /ip

Donnes

JVM 2

Ci-dessus, les couches [metier] et [DAO] changent des objets par valeur (srialisation 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.

package metier;
import java.util.List;
import jpa.Employe;

public interface IMetier {


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

On cre une interface locale et une interface distante partir de l'interface prcdente :
1.
2.
3.
4.
5.
6.
7.

package metier;

1.
2.
3.
4.
5.
6.

package metier;

import javax.ejb.Local;
@Local
public interface IMetierLocal extends IMetier{
}

import javax.ejb.Remote;
@Remote
public interface IMetierRemote extends IMetier{

141/257

7. }

L'EJB de la couche [metier] implmente ces deux interfaces :


1. @Stateless()
2. @TransactionAttribute(TransactionAttributeType.REQUIRED)
3. public class Metier implements IMetierLocal, IMetierRemote {
4.
5.
// rfrence sur les couches [DAO] locales
6.
@EJB
7.
private ICotisationDaoLocal cotisationDao = null;
8.
@EJB
9.
private IEmployeDaoLocal employeDao = null;
10. @EJB
11. private IIndemniteDaoLocal indemniteDao = 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-11 : on refait la mme chose pour les interfaces locales des EJB [EmployeDao] et [IndemniteDao].
Au final, lorsque l'EJB [Metier] sera instanci, les champs des lignes 7, 9 et 11 seront initialiss avec des rfrences sur les
interfaces locales des trois 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

5.1.6

Couche
mtier
[metier]

Rseau
tcp /ip

Couche d'accs
aux donnes
[DAO]

Donnes

JVM 2

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

142/257

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

// on configure le conteneur Open EJB embarqu


Properties properties = new Properties();
properties.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory");
// initialisation du contexte JNDI avec les proprits prcdentes
InitialContext initialContext = new InitialContext(properties);
// instanciation couches DAO
employeDao = (IEmployeDaoLocal) initialContext.lookup("EmployeDaoLocal");
cotisationDao = (ICotisationDaoLocal) initialContext.lookup("CotisationDaoLocal");
indemniteDao = (IIndemniteDaoLocal) initialContext.lookup("IndemniteDaoLocal");

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

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 IemployeDaoLocal employeDaoLocal ;

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

// on configure le conteneur Open EJB embarqu


Properties properties = new Properties();
properties.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory");
4.
// initialisation du contexte JNDI du conteneur EJB
5.
InitialContext initialContext = new InitialContext(properties);
6.
7.
// instanciation couche mtier distante
8. metier = (IMetierRemote) initialContext.lookup("MetierRemote");

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

143/257

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

Mise en place de la base de donnes [dbpam_eclipselink]

Si elle n'existe pas, crez la base MySQL [dbpam_eclipselink]. Si elle existe, supprimez toutes ses tables. Crez une connexion
Netbeans vers cette base comme il a t dcrit page 144.

5.2.2

Configuration initiale du projet Netbeans


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 [openejb.conf] [4-8] suivant :

1. <?xml version="1.0"?>
2. <openejb>
3.
<Resource id="Default JDBC Database">
4.
JdbcDriver com.mysql.jdbc.Driver
5.
JdbcUrl jdbc:mysql://localhost:3306/dbpam_eclipselink
6.
UserName root
7.
</Resource>
8. </openejb>

144/257

9
8

10

crer le dossier [src / main/ resources/ META-INF] [9]


y mettre le fichier [persistence.xml] [10] suivant :

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


2. <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
3.
<persistence-unit name="dbpam_eclipselinkPU" transaction-type="JTA">
4.
<!-- le fournisseur JPA est EclipseLink -->
5.
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
6.
<!-- entits Jpa -->
7.
<class>jpa.Cotisation</class>
8.
<class>jpa.Employe</class>
9.
<class>jpa.Indemnite</class>
10.
<!-- proprits provider EclipseLink -->
11.
<properties>
12.
<property name="eclipselink.logging.level" value="FINE"/>
13.
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
14.
</properties>
15. </persistence-unit>
16. </persistence>

ligne 12 : on demande des logs fins EclipseLink,


ligne 13 : les tables seront cres l'instanciation de la couche JPA,
ajouter les bibliothques OpenEJB, EclipseLink ainsi que le pilote JDBC de MySQL au fichier [pom.xml] du projet :

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

145/257

2.

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
3.
<modelVersion>4.0.0</modelVersion>
4.
5.
<groupId>istia.st</groupId>
6.
<artifactId>mv-pam-openejb-eclipselink</artifactId>
7.
<version>1.0-SNAPSHOT</version>
8.
<packaging>jar</packaging>
9.
10. <name>mv-pam-openejb-eclipselink</name>
11. <url>http://maven.apache.org</url>
12.
13. <properties>
14.
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15. </properties>
16.
17. <dependencies>
18.
<dependency>
19.
<groupId>org.apache.openejb</groupId>
20.
<artifactId>openejb-core</artifactId>
21.
<version>4.0.0</version>
22.
</dependency>
23.
<dependency>
24.
<groupId>junit</groupId>
25.
<artifactId>junit</artifactId>
26.
<version>4.10</version>
27.
<scope>test</scope>
28.
<type>jar</type>
29.
</dependency>
30.
<dependency>
31.
<groupId>org.eclipse.persistence</groupId>
32.
<artifactId>eclipselink</artifactId>
33.
<version>2.3.0</version>
34.
</dependency>
35.
<dependency>
36.
<groupId>org.eclipse.persistence</groupId>
37.
<artifactId>javax.persistence</artifactId>
38.
<version>2.0.3</version>
39.
</dependency>
40.
<dependency>
41.
<groupId>mysql</groupId>
42.
<artifactId>mysql-connector-java</artifactId>
43.
<version>5.1.6</version>
44.
</dependency>
45.
<dependency>
46.
<groupId>org.swinglabs</groupId>
47.
<artifactId>swing-layout</artifactId>
48.
<version>1.0.3</version>
49.
</dependency>
50. </dependencies>
51.
52. <repositories>
53.
<repository>
54.
<url>http://download.eclipse.org/rt/eclipselink/maven.repo/</url>
55.
<id>eclipselink</id>
56.
<layout>default</layout>
57.
<name>Repository for library Library[eclipselink]</name>
58.
</repository>
59. </repositories>
60.
61. </project>

lignes 18-22 : la dpendance OpenEJB,

146/257

5.2.3

lignes 30-39 : les dpendances EclipseLink,


lignes 41-44 : la dpendance du pilote JDBC de MySQL

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]

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.

5.2.3.1

L'EJB [CotisationDao]

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 javax.ejb.Local;
@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 javax.ejb.Remote;
@Remote
public interface ICotisationDaoRemote extends ICotisationDao{
}

Puis nous modifions la classe [CotisationDao] pour en faire un EJB :


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

...
import javax.persistence.PersistenceContext;
import jpa.Cotisation;
@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class CotisationDao implements ICotisationDaoLocal, ICotisationDaoRemote {

147/257

8.
9.
@PersistenceContext
10. private EntityManager em;
11. ...

L'import que faisait cette classe sur le framework Spring disparat. Faire un [Clean and Build] du projet :

1
2

En [1], il n'y a plus d'erreurs sur la classe [CotisationDao].

5.2.3.2

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

5.2.3.3

La classe [PamException]

La classe [PamException] reste ce qu'elle tait un dtail prs :


1. package exception;
2.
3. import javax.ejb.ApplicationException;
4.
5. @ApplicationException(rollback=true)
6. public class PamException extends RuntimeException implements Serializable{
7.
8.
// code d'erreur
9.
private int code;
10. ...

La ligne 5 est ajoute. Pour avoir les bons import, faire [clic droit+Fix imports].
Pour comprendre l'annotation de la ligne 5, 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

148/257

utilisateur

Couche
interface
utilisateur [ui]

Couche
mtier
[metier]

Couche d'accs aux


donnes [DAO]

Proxy
EJB

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], ici appele [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.

5.2.3.4

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

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. public [Classe] extends ... implements Serializable{
2. ..
3. }

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.

5.2.3.5

Test 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] :

149/257

3
2

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. public class JUnitInitDBLocal {
2.
3.
static private IEmployeDaoLocal employeDao = null;
4.
static private ICotisationDaoLocal cotisationDao = null;
5.
static private IIndemniteDaoLocal 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.
properties.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory");
12.
// initialisation du contexte JNDI avec les proprits prcdentes
13.
InitialContext initialContext = new InitialContext(properties);
14.
// instanciation couches DAO locales
15.
employeDao = (IEmployeDaoLocal) initialContext.lookup("EmployeDaoLocal");
16.
cotisationDao = (ICotisationDaoLocal) initialContext.lookup("CotisationDaoLocal");
17.
indemniteDao = (IIndemniteDaoLocal) initialContext.lookup("IndemniteDaoLocal");
18. }
1.
2. ...

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

150/257

On construit le projet (Build), on lance le serveur MySQL si besoin est, on excute le test JUnitInitDBLocal. On rappelle que le
fichier [persistence.xml] a t configur pour recrer les tables chaque excution. Avant l'excution du test, il est prfrable de
supprimer les ventuelles tables de la base MySQL [dbpam_eclipselink].

4
2

en [1], dans l'onglet [Services], on supprime les tables de la connexion Netbeans tablie au paragraphe 5.2.1.
en [2], la base [dbpam_eclipselink] n'a plus de tables
en [3], le projet est construit
en [4], le test JUnitInitDBLocal est excut

en [5], le test a t russi


en [6], on rafrachit la connexion Netbeans
en [7], on voit les 4 tables cres par la couche JPA. Le test avait pour but de les remplir. On visualise le contenu de l'une
d'entre-elles

en [8], le contenu de la table [EMPLOYES]

Le conteneur OpenEJB a affich des logs dans la console :


1. Infos - PersistenceUnit(name=dbpam_eclipselinkPU,
provider=org.eclipse.persistence.JPA.PersistenceProvider) - provider time 396ms
2. Infos - Jndi(name=CotisationDaoLocal) --> Ejb(deployment-id=CotisationDao)
3. Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/CotisationDao!
dao.ICotisationDaoLocal) --> Ejb(deployment-id=CotisationDao)
4. Infos - Jndi(name=CotisationDaoRemote) --> Ejb(deployment-id=CotisationDao)
5. Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/CotisationDao!
dao.ICotisationDaoRemote) --> Ejb(deployment-id=CotisationDao)

151/257

6. Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/CotisationDao) -->


Ejb(deployment-id=CotisationDao)
7. Infos - Jndi(name=EmployeDaoLocal) --> Ejb(deployment-id=EmployeDao)
8. Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/EmployeDao!
dao.IEmployeDaoLocal) --> Ejb(deployment-id=EmployeDao)
9. Infos - Jndi(name=EmployeDaoRemote) --> Ejb(deployment-id=EmployeDao)
10. Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/EmployeDao!
dao.IEmployeDaoRemote) --> Ejb(deployment-id=EmployeDao)
11. Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/EmployeDao) -->
Ejb(deployment-id=EmployeDao)
12. Infos - Jndi(name=IndemniteDaoLocal) --> Ejb(deployment-id=IndemniteDao)
13. Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/IndemniteDao!
dao.IIndemniteDaoLocal) --> Ejb(deployment-id=IndemniteDao)
14. Infos - Jndi(name=IndemniteDaoRemote) --> Ejb(deployment-id=IndemniteDao)
15. Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/IndemniteDao!
dao.IIndemniteDaoRemote) --> Ejb(deployment-id=IndemniteDao)
16. Infos - Jndi(name=global/classpath.ear/mv-pam-openejb-eclipselink/IndemniteDao) -->
Ejb(deployment-id=IndemniteDao)
17. Infos - existing thread singleton service in SystemInstance()
org.apache.openejb.cdi.ThreadSingletonServiceImpl@624a240d
18. Infos - OpenWebBeans Container is starting...
19. Infos - Adding OpenWebBeansPlugin : [CdiPlugin]
20. Infos - All injection points were validated successfully.
21. Infos - OpenWebBeans Container has started, it took [70] ms.
22. Infos - Created Ejb(deployment-id=IndemniteDao, ejb-name=IndemniteDao, container=Default
Stateless Container)
23. Infos - Created Ejb(deployment-id=EmployeDao, ejb-name=EmployeDao, container=Default
Stateless Container)
24. Infos - Created Ejb(deployment-id=CotisationDao, ejb-name=CotisationDao, container=Default
Stateless Container)
25. Infos - Started Ejb(deployment-id=IndemniteDao, ejb-name=IndemniteDao, container=Default
Stateless Container)
26. Infos - Started Ejb(deployment-id=EmployeDao, ejb-name=EmployeDao, container=Default
Stateless Container)
27. Infos - Started Ejb(deployment-id=CotisationDao, ejb-name=CotisationDao, container=Default
Stateless Container)
28. Infos - Deployed Application(path=D:\data\istia-1112\netbeans\glassfish\mv-pam\tmp\mv-pamopenejb-eclipselink\classpath.ear)

lignes 2-3 : les deux noms JNDI de l'EJB [CotisationDaoLocal],


lignes 4-5 : les deux noms JNDI de l'EJB [CotisationDaoRemote],
lignes 7-8 :les deux noms JNDI de l'EJB [EmployeDaoLocal],
lignes 9-10 :les deux noms JNDI de l'EJB [EmployeDaoRemote],
lignes 12-13 :les deux noms JNDI de l'EJB [IndemniteDaoLocal],
lignes 14-15 :les deux noms JNDI de l'EJB [EmployeDaoRemote].

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 :

152/257

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.
properties.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory");
12.
// initialisation du contexte JNDI avec les proprits prcdentes
13.
InitialContext initialContext = new InitialContext(properties);
14.
// instanciation couches DAO distantes
15.
employeDao = (IEmployeDaoRemote) initialContext.lookup("EmployeDaoRemote");
16.
cotisationDao = (ICotisationDaoRemote) initialContext.lookup("CotisationDaoRemote");
17.
indemniteDao = (IIndemniteDaoRemote) initialContext.lookup("IndemniteDaoRemote");
18. }

Ceci fait, la nouvelle classe de test peut tre excute. Auparavant, avec la connexion Netbeans [dbpam_eclipselink], supprimez les
tables de la base [dbpam_eclipselink].

Avec la connexion Netbeans [dbpam_eclipselink], vrifiez que la base a t remplie.

5.2.4

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

2
3

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.

5.2.4.1

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. package metier;

153/257

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

import javax.ejb.Local;
@Local
public interface IMetierLocal extends IMetier{
}
package metier;
import javax.ejb.Remote;
@Remote
public interface IMetierRemote extends IMetier{
}

Ceci fait, en [3] nous modifions la classe [Metier] afin qu'elle devienne un EJB :
1. @Stateless
2. @TransactionAttribute(TransactionAttributeType.REQUIRED)
3. public class Metier implements IMetierLocal, IMetierRemote {
4.
5.
// rfrences sur la couche [DAO] locale
6.
@EJB
7.
private ICotisationDaoLocal cotisationDao = null;
8.
@EJB
9.
private IEmployeDaoLocal employeDao = null;
10. @EJB
11. private IIndemniteDaoLocal indemniteDao = null;
12.
13. // obtenir la feuille de salaire
14. public FeuilleSalaire calculerFeuilleSalaire(String SS,
15.
double nbHeuresTravailles, int nbJoursTravaills) {
16.
// on rcupre les informations lies l'employ
17. ...

5.2.4.2

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-11 : le mme mcanisme est utilis pour les deux autres EJB de la couche [DAO].

Test 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] :

154/257

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 locale
4.
static private IMetierLocal metier;
5.
6.
@BeforeClass
7.
public static void init() throws NamingException {
8.
// on configure le conteneur Open EJB embarqu
9.
Properties properties = new Properties();
10.
properties.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory");
11.
// initialisation du contexte JNDI avec les proprits prcdentes
12.
InitialContext initialContext = new InitialContext(properties);
13.
14.
// instanciation couches DAO locales
15.
IEmployeDaoLocal employeDao = (IEmployeDaoLocal)
initialContext.lookup("EmployeDaoLocal");
16.
ICotisationDaoLocal cotisationDao = (ICotisationDaoLocal)
initialContext.lookup("CotisationDaoLocal");
17.
IIndemniteDaoLocal indemniteDao = (IIndemniteDaoLocal)
initialContext.lookup("IndemniteDaoLocal");
18.
// instanciation couche mtier locale
19.
metier = (IMetierLocal) initialContext.lookup("MetierLocal");
20.
21.
// on vide la base
22. ...
23. }

ligne 4 : une rfrence sur l'interface locale de l'EJB [Metier]


lignes 8-12 : configuration du conteneur OpenEJB identique celle faite dans le test de la couche [DAO]
lignes 15-19 : on demande au contexte JNDI de la ligne 12, des rfrences sur les 3 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.

Note : avant d'excuter ce test, il faut modifier le fichier [persistence.xml] pour que les tables ne soient plus dtruites et recres au
moment de l'instanciation de la couche [JPA] (lignes 13-15 ci-dessous) :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

155/257

3.
<persistence-unit name="dbpam_eclipselinkPU" transaction-type="JTA">
4.
<!-- le fournisseur JPA est EclipseLink -->
5.
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
6.
<!-- entits Jpa -->
7.
<class>jpa.Cotisation</class>
8.
<class>jpa.Employe</class>
9.
<class>jpa.Indemnite</class>
10.
<!-- proprits provider EclipseLink -->
11.
<properties>
12.
<property name="eclipselink.logging.level" value="FINE"/>
13.
<!-14.
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
15.
-->
16.
</properties>
17. </persistence-unit>
18. </persistence>

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. public class JUnitMetierRemote {
2.
3.
// couche mtier distante
4.
static private IMetierRemote metier;
5.
6.
@BeforeClass
7.
public static void init() throws NamingException {
8.
// on configure le conteneur Open EJB embarqu
9.
Properties properties = new Properties();
10.
properties.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory");
11.
// initialisation du contexte JNDI avec les proprits prcdentes
12.
InitialContext initialContext = new InitialContext(properties);
13.
14.
// instanciation couches DAO distantes
15.
IEmployeDaoRemote employeDao = (IEmployeDaoRemote)
initialContext.lookup("EmployeDaoRemote");
16.
ICotisationDaoRemote cotisationDao = (ICotisationDaoRemote)
initialContext.lookup("CotisationDaoRemote");
17.
IIndemniteDaoRemote indemniteDao = (IIndemniteDaoRemote)
initialContext.lookup("IndemniteDaoRemote");
18.
// instanciation couche mtier distante
19.
metier = (IMetierRemote) initialContext.lookup("MetierRemote");
20.
21.
// on vide la base
22.
for(Employe employe:employeDao.findAll()){
23.
employeDao.destroy(employe);
24.
}
25.
for(Cotisation cotisation:cotisationDao.findAll()){
26.
cotisationDao.destroy(cotisation);
27.
}

156/257

28.
29.
30.
31.
32.
33.
34.
35.
36.

for(Indemnite indemnite : indemniteDao.findAll()){


indemniteDao.destroy(indemnite);
}
// on la remplit
Indemnite indemnite1=new Indemnite(1,1.93,2,3,12);
Indemnite indemnite2=new Indemnite(2,2.1,2.1,3.1,15);
indemnite1=indemniteDao.create(indemnite1);
indemnite2=indemniteDao.create(indemnite2);
employeDao.create(new Employe("254104940426058","Jouveinal","Marie","5 rue des
oiseaux","St Corentin","49203",indemnite2));
37.
employeDao.create(new Employe("260124402111742","Laverti","Justine","La brlerie","St
Marcel","49014",indemnite1));
38.
cotisationDao.create(new Cotisation(3.49,6.15,9.39,7.88));
39. }
40. }

lignes 4 et 19 : on utilise l'interface distante de l'EJB [Metier].


lignes 15-17 : on utilise les interfaces distantes de la couche [DAO]
lignes 34-35 : parce qu'avec les interfaces distantes, les objets changs entre le client et le serveur le sont par valeur, il faut
rcuprer le rsultat rendu par la mthode create(Indemnite i). Ce n'tait pas obligatoire avec les interfaces locales o l les
objets sont passs par rfrence.

Ceci fait, le projet peut tre construit et le test [JUnitMetierRemote] excut :

5.2.5

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

2
3
1

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

public static void main(String[] args) {

// donnes locales
final String syntaxe = "pg num_securite_sociale nb_heures_travailles
nb_jours_travaills";
4. ...
5.
// des erreurs ?
6.
if (erreurs.size() != 0) {

157/257

7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.

for (int i = 0; i < erreurs.size(); i++) {


System.err.println(erreurs.get(i));
}
return;

}
// c'est bon - on peut demander la feuille de salaire la couche [mtier]
IMetierLocal metier = null;
FeuilleSalaire feuilleSalaire = null;
try {
// on configure le conteneur Open EJB embarqu
Properties properties = new Properties();
properties.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory");
19.
// initialisation du contexte JNDI avec les proprits prcdentes
20.
InitialContext initialContext = new InitialContext(properties);
21.
// instanciation couche mtier locale
22.
metier = (IMetierLocal) initialContext.lookup("MetierLocal");
23.
// calcul de la feuille de salaire
24.
feuilleSalaire = metier.calculerFeuilleSalaire(args[0], nbHeuresTravailles,
nbJoursTravaills);
25.
} catch (PamException ex) {
26.
System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
27.
return;
28.
} catch (Exception ex) {
29.
System.err.println("L'erreur suivante s'est produite : " + ex.toString());
30.
return;
31.
}
32.
// affichage dtaill
33.
String output = "Valeurs saisies :\n";
34.
output += ajouteInfo("N de scurit sociale de l'employ", args[0]);
35. ....

Les modifications se situent dans les lignes 13-25. C'est la faon d'avoir une rfrence sur la couche [metier] qui change (lignes 1722). 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] :

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/persistence.xml] :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
3.
<persistence-unit name="dbpam_eclipselinkPU" transaction-type="JTA">
4.
<!-- le fournisseur JPA est EclipseLink -->
5.
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
6.
<!-- entits Jpa -->

158/257

7.
<class>jpa.Cotisation</class>
8.
<class>jpa.Employe</class>
9.
<class>jpa.Indemnite</class>
10.
<!-- proprits provider EclipseLink -->
11.
<properties>
12.
<property name="eclipselink.logging.level" value="FINE"/>
13.
<!-14.
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
15.
-->
16.
</properties>
17. </persistence-unit>
18. </persistence>

La ligne 14 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. Ceci fait, on peut excuter le programme.
Si tout va bien, on obtient un affichage console analogue au suivant :
1. .......
2. INFO - Created EJB(deployment-id=IndemniteDao, ejb-name=IndemniteDao, container=Default
Stateless Container)
3. INFO - Deployed Application(path=classpath.ear)
4. [EL Info]: 2009-09-30 15:09:21.109--ServerSession(16658781)--EclipseLink, version: Eclipse
Persistence Services - 1.1.2.v20090612-r4475
5. [EL Info]: 2009-09-30 15:09:21.937--ServerSession(16658781)--file:/C:/temp/09-09-28/pamconsole-metier-dao-openejb-eclipselink-0910/build/classes/-JPA login successful
6. Valeurs saisies :
7. N de scurit sociale de l'employ : 254104940426058
8. Nombre d'heures travailles : 150
9. Nombre de jours travaills : 20
10.
11. Informations Employ :
12. Nom : Jouveinal
13. Prnom : Marie
14. Adresse : 5 rue des oiseaux
15. Ville : St Corentin
16. Code Postal : 49203
17. Indice : 2
18.
19. Informations Cotisations :
20. CSGRDS : 3.49 %
21. CSGD : 6.15 %
22. Retraite : 7.88 %
23. Scurit sociale : 9.39 %
24.
25. Informations Indemnits :
26. Salaire horaire : 2.1 euro
27. Entretien/jour : 2.1 euro
28. Repas/jour : 3.1 euro
29. Congs Pays : 15.0 %
30.
31. Informations Salaire :
32. Salaire de base : 362.25 euro
33. Cotisations sociales : 97.48 euro
34. Indemnits d'entretien : 42.0 euro
35. Indemnits de repas : 62.0 euro
36. Salaire net : 368.77 euro
37.
38. BUILD SUCCESSFUL (total time: 4 seconds)

Nous avons utilis ici l'interface locale de la couche [metier]. Nous utilisons maintenant son interface distante dans une seconde
classe console :

159/257

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. // c'est bon - on peut demander la feuille de salaire la couche [metier]
2.
IMetierRemote metier = null;
3.
FeuilleSalaire feuilleSalaire = null;
4.
try {
5.
// on configure le conteneur Open EJB embarqu
6. ...
7.
// instanciation couche mtier distante
8.
metier = (IMetierRemote) initialContext.lookup("MetierRemote");
9.
// calcul de la feuille de salaire
10.
feuilleSalaire = metier.calculerFeuilleSalaire(args[0], nbHeuresTravailles,
nbJoursTravaills);
11.
} catch (PamException ex) {
12. ...
13.
} catch (Exception ex) {
14. ...
15.
}

Les modifications sont faites aux lignes 2 et 8. Le projet est configur [2] pour excuter la classe [MainRemote]. Son excution
donne les mmes rsultats que prcdemment.

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

160/257

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.

161/257

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 v3

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

162/257

Conteneur
Ejb3

Client
Java SE

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.

6.1.1.1

Le projet Netbeans

Commenons par crer un nouveau projet Netbeans :


1
3
2

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.

5
4b
4a

avec le bouton [4a], choisir le dossier parent du dossier du projet ou taper son nom directement en [4b].
en [5], donner un nom au projet
en [6], choisir le serveur d'application sur lequel il sera excut. Celui choisi ici est l'un de ceux visibles dans l'onglet
[Runtime / Servers], ici Glassfish v3.
en [7], choisir la version de Java EE.

1
3

en [1], le nouveau projet. Il diffre d'un projet Java classique par quelques points :

une branche [Other Sources] [2] est automatiquement cre. Elle contiendra notamment le fichier
[persistence.xml] qui configure la couche JPA,

si on construit le projet (Build), on voit apparatre [3] une dpendance [javaee-api-6.0]. Elle est de type provided car
elle est fournie l'excution par le conteneur EJB de Glassfish.

163/257

6.1.1.2

Configuration de la couche de persistance

Par configuration de la couche de persistance, nous entendons l'criture du fichier [persistence.xml] 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]

Couche
[metier]

Couche
[DAO]

Couche
[JPA /
Eclipselink]

Couche
[JDBC]

SGBD

BD

Java EE - serveur Glassfish

Java SE

On pourra procder comme suit. Tout d'abord, dans l'onglet [Runtime / Databases], on crera [1] une connexion sur la base
MySQL5 [dbpam_eclipselink] :

Ceci fait, on peut passer la cration de la ressource JDBC utilise par le module EJB :

2
4
1

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

164/257

7
8
5

6
9

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 [persistence.xml] 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_eclipselink.
en [9], un rcapitulatif des proprits du pool de connexions - on ne touche rien

11

10

en [10], on peut prciser plusieurs des proprits du pool de connexions - on laisse les valeurs par dfaut
en [11], l'issue de l'assistant de cration d'une ressource JDBC pour le module EJB, un fichier [glassfish-resources.xml] a
t cr dans la branche [Other Sources]. Le contenu de ce fichier est le suivant :

165/257

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


2. <!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource
Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
3. <resources>
4.
<jdbc-resource enabled="true" jndi-name="jdbc/dbpam_eclipselink" object-type="user" poolname="dbpamEclipselinkConnectionPool">
5.
<description/>
6.
</jdbc-resource>
7.
<jdbc-connection-pool allow-non-component-callers="false" associate-with-thread="false"
connection-creation-retry-attempts="0" connection-creation-retry-interval-in-seconds="10"
connection-leak-reclaim="false" connection-leak-timeout-in-seconds="0" connectionvalidation-method="auto-commit" datasourceclassname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" 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" max-connection-usage-count="0" max-pool-size="32" max-wait-timein-millis="60000" name="dbpamEclipselinkConnectionPool" non-transactionalconnections="false" pool-resize-quantity="2" res-type="javax.sql.DataSource" statementtimeout-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_eclipselink"/>
9.
<property name="User" value="root"/>
10.
<property name="Password" value=""/>
11. </jdbc-connection-pool>
12. </resources>

Le fichier [glassfish-resources.xml] 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 [persistence.xml] qui va configurer la couche JPA du module EJB :

4
1
3

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 [Persistence]
en [4], on veut crer une unit de persistance

5
10

6
8

7
9

en [5], donner un nom l'unit de persistance

166/257

en [6], 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 [7], choisir la source de donnes JDBC [jdbc/dbpam_eclipselink] qui vient d'tre cre.
en [8], indiquer que les transactions sont gres par le conteneur EJB
en [9], 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_eclipselink] dj cre.
la fin de l'assistant, un fichier [persistence.xml] a t cr [10]. Son contenu est le suivant :

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


2. <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
3.
<persistence-unit name="mv-pam-ejb-metier-dao-eclipselinkPU" transaction-type="JTA">
4.
<jta-data-source>jdbc/dbpam_eclipselink</jta-data-source>
5.
<exclude-unlisted-classes>false</exclude-unlisted-classes>
6.
<properties/>
7.
</persistence-unit>
8. </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 5 : le nom JNDI de la source de donnes utilise par la couche de persistance : jdbc/dbpam_eclipselink
ligne 6 : 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 v3 utilise par
dfaut EclipseLink.

6.1.1.3

Insertion des couches [JPA, DAO, metier]

Maintenant que le fichier [persistence.xml] a t dfini, nous pouvons passer l'insertion dans le projet des couches [metier, dao,
JPA] de l'application d'entreprise [pam] :

Couche
[ui]

Couche
[metier]

Couche
[DAO]

Couche
[JPA /
EclipseLink]

Couche
[JDBC]

SGBD

BD

Java EE - serveur Glassfish

Java SE

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 :

6.1.1.4

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]

Configuration du serveur Glassfish

Il nous reste configurer le serveur Glassfish sur deux points :

167/257

la couche JPA est implmente par EclipseLink. Il faut nous assurer que le serveur Glassfish a les bibliothques de cette
implmentation JPA.
la source de donnes est une base MySQL. Il faut nous assurer que le serveur Glassfish a le pilote JDBC de ce SGBD.

On peut dcouvrir l'absence de ces bibliothques lors du dploiement du module EJB. Voici une faon de procder parmi d'autres
pour ajouter des bibliothques manquantes au serveur Glassfish :

en [1], visualiser les proprits du serveur Glassfish


en [2], noter le dossier des domaines du serveur. Nous le notons par la suite <domains>
dans le dossier <domains>\domain1\lib, mettre les bibliothques manquantes. Dans l'exemple, les bibliothques
d'Hibernate (lib / hibernate-tools) et le pilote JDBC de MySQL (lib / divers) ont t rajouts. Par dfaut, le serveur
Glassfish a les bibliothques d'EclipseLink. On ne rajoutera donc que le pilote JDBC de MySQL.

6.1.1.5

en [1], dans l'onglet [Services], nous lanons le serveur Glassfish v3


en [2], il est actif

Dploiement du module EJB

Nous dployons maintenant le module EJB sur le serveur Glassfish :

168/257

en [1], le module EJB est dploy


en [2], l'arborescence du serveur Glassfish est rafrachie
en [3], aprs dploiement, le module EJB apparat dans la branche [Applications] du serveur Glassfish
en [4], la ressource JDBC [jdbc / dbpam_eclipselink] a t cre sur le serveur Glassfish. On rappelle que nous l'avions
dfinie page 164.

Lors du dploiement, le serveur Glassfish logue dans la console des informations intressantes :
1. dao.
2. ....
3. INFO: Portable JNDI names for EJB IndemniteDao : [java:global/pam-serveur-metier-dao-JPAeclipselink/IndemniteDao!DAO.IIndemniteDaoLocal, java:global/pam-serveur-metier-dao-JPAeclipselink/IndemniteDao!DAO.IIndemniteDaoRemote]
4. INFO: Glassfish-specific (Non-portable) JNDI names for EJB IndemniteDao :
[DAO.IIndemniteDaoRemote#DAO.IIndemniteDaoRemote, dao.IIndemniteDaoRemote]
5. ...
6. INFO: Portable JNDI names for EJB CotisationDao : [java:global/pam-serveur-metier-dao-JPAeclipselink/CotisationDao!dao.ICotisationDaoLocal, java:global/pam-serveur-metier-dao-JPAeclipselink/CotisationDao!dao.ICotisationDaoRemote]
7. INFO: Glassfish-specific (Non-portable) JNDI names for EJB CotisationDao :
[DAO.ICotisationDaoRemote, dao.ICotisationDaoRemote#DAO.ICotisationDaoRemote]
8. INFO: Portable JNDI names for EJB Metier : [java:global/pam-serveur-metier-dao-JPAeclipselink/Metier!metier.IMetierRemote, java:global/pam-serveur-metier-dao-JPAeclipselink/Metier!metier.IMetierLocal]
9. INFO: Glassfish-specific (Non-portable) JNDI names for EJB Metier :
[metier.IMetierRemote#metier.IMetierRemote, metier.IMetierRemote]
10. ...
11. INFO: Portable JNDI names for EJB EmployeDao : [java:global/pam-serveur-metier-dao-JPAeclipselink/EmployeDao!dao.IEmployeDaoLocal, java:global/pam-serveur-metier-dao-JPAeclipselink/EmployeDao!dao.IEmployeDaoRemote]
12. INFO: Glassfish-specific (Non-portable) JNDI names for EJB EmployeDao :
[DAO.IEmployeDaoRemote#dao.IEmployeDaoRemote, DAO.IEmployeDaoRemote]
13. INFO: pam-serveur-metier-dao-JPA-eclipselink was successfully deployed in 12 891
milliseconds.

On notera aux lignes

3, 6, 8 et 11 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.

4, 7, 9, 12 : les noms JNDI des EJB dploys sous une forme spcifique Glassfish v3.
Ces noms seront utiles l'application console que nous allons crire pour utiliser le module EJB dploy.

6.2

Client console - version 1

169/257

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]

Couche
[JDBC]

Couche
[JPA /
Toplink]

SGBD

BD

Java EE - serveur Glassfish

Java SE

Nous crons un nouveau projet Maven de type [Java Application] nomm [mv-pam-client-ejb-metier-dao-eclipselink] :

2
3

en [1], le projet du client ;


en [2], les dpendances du projet ;
en [3], le projet a une dpendance sur le projet prcdent.

Pour construire la dpendance [3], on procde de la faon suivante :

4
5
6

en [4], on ajoute une dpendance ;


en [5], on slectionne l'onglet [Open Projects] ;
en [6], on dsigne le projet prcdent. Il faut que celui-ci soit charg dans Netbeans pour apparatre.

Le fichier [pom.xml] est le suivant :


1. <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2.
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven4.0.0.xsd">
3.
<modelVersion>4.0.0</modelVersion>
4.
5.
<groupId>istia.st</groupId>
6.
<artifactId>mv-pam-client-ejb-metier-dao-eclipselink</artifactId>
7.
<version>1.0-SNAPSHOT</version>

170/257

8.
<packaging>jar</packaging>
9.
10. <name>mv-pam-client-ejb-metier-dao-eclipselink</name>
11. <url>http://maven.apache.org</url>
12. <repositories>
13.
<repository>
14.
<url>http://download.eclipse.org/rt/eclipselink/maven.repo/</url>
15.
<id>eclipselink</id>
16.
<layout>default</layout>
17.
<name>Repository for library Library[eclipselink]</name>
18.
</repository>
19.
<repository>
20.
<url>http://repo1.maven.org/maven2/</url>
21.
<id>swing-layout</id>
22.
<layout>default</layout>
23.
<name>Repository for library Library[swing-layout]</name>
24.
</repository>
25. </repositories>
26. <properties>
27.
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
28. </properties>
29.
30. <dependencies>
31.
<dependency>
32.
<groupId>org.glassfish.appclient</groupId>
33.
<artifactId>gf-client</artifactId>
34.
<version>3.1.1</version>
35.
</dependency>
36.
<dependency>
37.
<groupId>${project.groupId}</groupId>
38.
<artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
39.
<version>${project.version}</version>
40.
<type>ejb</type>
41.
</dependency>
42.
<dependency>
43.
<groupId>org.swinglabs</groupId>
44.
<artifactId>swing-layout</artifactId>
45.
<version>1.0.3</version>
46.
</dependency>
47. </dependencies>
48. </project>

lignes 31-35 : la dpendance sur la bibliothque [gf-client] qui permet un client Glassfish de communiquer avec un
serveur distant,
lignes 36-41 : 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 la classe [MainRemote] :

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 :

171/257

1. // c'est bon - on peut demander la feuille de salaire


2.
FeuilleSalaire feuilleSalaire = null;
3.
IMetierRemote metier = null;
4.
try {
5.
// contexte JNDI du serveur Glassfish
6.
InitialContext initialContext = new InitialContext();
7.
// instanciation couche mtier
8.
metier = (IMetierRemote) initialContext.lookup("java:global/istia.st_mv-pam-ejbmetier-dao-eclipselink_ejb_1.0-SNAPSHOT/Metier!metier.IMetierRemote");
9.
// calcul de la feuille de salaire
10.
feuilleSalaire = metier.calculerFeuilleSalaire(args[0], nbHeuresTravailles,
nbJoursTravaills);
11.
} catch (PamException ex) {
12.
System.err.println("L'erreur suivante s'est produite : "
13.
+ ex.getMessage());
14.
return;
15.
} catch (Exception ex) {
16.
System.err.println("L'erreur suivante s'est produite : "
17.
+ ex.toString());
18.
return;
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: EJB5181:Portable JNDI names for EJB Metier: [java:global/istia.st_mv-pam-ejb-metierdao-eclipselink_ejb_1.0-SNAPSHOT/Metier!metier.IMetierLocal, java:global/istia.st_mv-pamejb-metier-dao-eclipselink_ejb_1.0-SNAPSHOT/Metier!metier.IMetierRemote]


2. Infos: EJB5182:Glassfish-specific (Non-portable) JNDI names for EJB Metier:
[metier.IMetierRemote#metier.IMetierRemote, metier.IMetierRemote]

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 9, nous utilisons le nom JNDI portable.

le reste du code ne change pas

En [1], on configure le projet pour qu'il excute la classe [MainRemote] avec des arguments. Si tout va bien, l'excution du projet
donne le rsultat suivant :
1. run:
2. Valeurs saisies :
3. N de scurit sociale de l'employ : 254104940426058
4. Nombre d'heures travailles : 150
5. Nombre de jours travaills : 20
6.
7. Informations Employ :
8. Nom : Jouveinal
9. Prnom : Marie
10. Adresse : 5 rue des oiseaux
11. Ville : St Corentin

172/257

12. Code Postal : 49203


13. Indice : 2
14.
15. Informations Cotisations :
16. CSGRDS : 3.49 %
17. CSGD : 6.15 %
18. Retraite : 7.88 %
19. Scurit sociale : 9.39 %
20.
21. Informations Indemnits :
22. Salaire horaire : 2.1 euro
23. Entretien/jour : 2.1 euro
24. Repas/jour : 3.1 euro
25. Congs Pays : 15.0 %
26.
27. Informations Salaire :
28. Salaire de base : 362.25 euro
29. Cotisations sociales : 97.48 euro
30. Indemnits d'entretien : 42.0 euro
31. Indemnits de repas : 62.0 euro
32. Salaire net : 368.77 euro
33.
34. BUILD SUCCESSFUL (total time: 2 seconds)

Si dans les proprits, on met un n de scurit sociale incorrect, on obtient le rsultat suivant :
1. run:
2. L'erreur suivante s'est produite : L'employ de n[254104940426058x] est introuvable
3. BUILD SUCCESSFUL (total time: 2 seconds)

Note : si vous rencontrez une exception de type [unmarshalling / marshalling exception] cela signifie que le serveur a voulu envoyer
au client un objet qui n'a pas pu tre 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.3

Client console - version 2

Dans les versions prcdentes, l'environnement JNDI du serveur Glassfish tait configur partir d'un fichier [jndi.properties]
trouv quelque part dans les archives du projet. Son contenu par dfaut est le suivant :
1.
2.
3.
4.
5.
6.
7.
8.

# accs JNDI Sun Application Server


java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
java.naming.factory.url.pkgs=com.sun.enterprise.naming
# Required to add a javax.naming.spi.StateFactory for CosNaming that
# supports dynamic RMI-IIOP.
java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl
org.omg.CORBA.ORBInitialHost=localhost
org.omg.CORBA.ORBInitialPort=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 [jndi.properties] 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.

173/257

2
1

en [1], le nouveau projet


en [2], le fichier [spring-config-client.xml] de configuration de Spring. Son contenu est le suivant :

Le fichier de configuration de Spring est le suivant :


1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3.
xmlns:tx="http://www.springframework.org/schema/tx"
4.
xmlns:jee="http://www.springframework.org/schema/jee"
5.
xsi:schemaLocation="
6.
http://www.springframework.org/schema/beans
7.
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
8.
http://www.springframework.org/schema/tx
9.
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
10.
http://www.springframework.org/schema/jee
11.
http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">
12.
13. <!-- mtier -->
14. <jee:jndi-lookup id="metier" jndi-name="java:global/istia.st_mv-pam-ejb-metier-daoeclipselink_ejb_1.0-SNAPSHOT/Metier!metier.IMetierRemote">
15.
<jee:environment>
16.
java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
17.
java.naming.factory.url.pkgs=com.sun.enterprise.naming
18.
java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl
19.
org.omg.CORBA.ORBInitialHost=localhost
20.
org.omg.CORBA.ORBInitialPort=3700
21.
</jee:environment>
22. </jee:jndi-lookup>
23. </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, 10 et 11.

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 [jndi.properties] devient le contenu de la balise <jee:environment> (ligne 15) qui sert dfinir les
paramtres de connexion au service JNDI.

La classe principale [MainRemote] volue de la faon suivante :


1. ...
2.
// c'est bon - on peut demander la feuille de salaire
3.
FeuilleSalaire feuilleSalaire = null;
4.
IMetierRemote metier=null;
5.
try {
6.
// instanciation couche [metier]
7.
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-configclient.xml");
8.
metier = (IMetierRemote) ctx.getBean("metier");
9.
// calcul de la feuille de salaire

174/257

10.

feuilleSalaire = metier.calculerFeuilleSalaire(args[0], nbHeuresTravailles,


nbJoursTravaills);
11.
} catch (PamException ex) {
12.
System.err.println("L'erreur suivante s'est produite : "
13.
+ ex.getMessage());
14.
return;
15.
} catch (Exception ex) {
16.
System.err.println("L'erreur suivante s'est produite : "
17.
+ ex.toString());
18.
return;
19.
}
20.
...

Lignes 7-8, la rfrence de type [IMetierRemote] sur la couche [metier] est demande Spring. 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 [spring-config-client.xml] changerait. On retrouverait
alors une configuration analogue l'architecture Spring / JPA tudie au paragraphe 4.11.
Le lecteur est invit tester cette nouvelle version.

6.4

Le client Swing

Nous construisons maintenant le client swing de notre application client / serveur EJB.

Le fichier [pom.xml] doit avoir la dpendance ncessaire aux applications swing :


1.
<dependency>
2.
<groupId>org.swinglabs</groupId>
3.
<artifactId>swing-layout</artifactId>
4.
<version>1.0.3</version>
5. </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.

175/257

Couche
[ui]
swing

Jvm1 - Java SE

Couche
[metier]

Couche
[DAO]

Couche
[JPA /
EclipseLink]

Couche
[JDBC]

Jvm2 Java EE - serveur Glassfish v3

Travail pratique : en suivant l'exemple du client console [ui.console.MainRemote] du projet, modifier la faon utilise par la
mthode [doMyInit] (cf page 125) de la classe [PamJFrame] pour acqurir une rfrence sur la couche [metier] qui est maintenant
distante.

176/257

7 Version 4 client / serveur dans une architecture de service web


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

177/257

7.1
7.1.1

Service web 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]

2
1

Java SE

S
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. package metier;
2.
3. ...
4. @WebService
5. @Stateless()
6. @TransactionAttribute(TransactionAttributeType.REQUIRED)
7. public class Metier implements IMetierLocal,IMetierRemote {
8.
9.
// rfrences sur les couches [DAO]
10. @EJB
11. private ICotisationDaoLocal cotisationDao = null;
12. @EJB
13. private IEmployeDaoLocal employeDao=null;
14. @EJB
15. private IIndemniteDaoLocal indemniteDao=null;
16.
17.
18. // obtenir la feuille de salaire
19. @WebMethod
20. public FeuilleSalaire calculerFeuilleSalaire(String SS,
21. ...
22. }
23.
24. // liste des employs
25. @WebMethod
26.
public List<Employe> findAllEmployes() {
27. ...
28. }
29. // important - pas de getters et setters pour les EJB qui deviennent des services web

178/257

30. }

ligne 4, l'annotation @WebService fait de la classe [Metier] un service web. Un service web expose des mthodes ses
clients. Celles-ci doivent tre annotes par l'attribut @WebMethod.
lignes 19 et 25 : les deux mthodes de la classe [Metier] deviennent des mthodes du service web.
ligne 29 : 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.

L'ajout de ces annotations est dtect par Netbeans qui fait alors voluer la nature du projet :

1
3
2

En [1], une arborescence [Web Services] est apparue dans le projet. On y trouve le service web Metier et ses deux mthodes.
L'application serveur peut tre dploye [2]. Le serveur MySQL soit tre lanc et sa base [dbpam_eclipselink] exister et tre remplie.
Il peut tre ncessaire auparavant de supprimer [3] 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.
1

En [1], nous voyons notre application serveur dploye sur le serveur Glassfish. Une fois le service web dploy, il peut tre test :
2

en [1], dans le projet courant, nous testons le service web [Metier]


le service web est accessible via diffrentes URL. L'URL [2] permet de tester le service web

179/257

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.
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). Nous y retrouvons bien les deux
employs avec leurs indemnits. Le lecteur est invit tester de la mme faon la mthode [4] en lui passant les trois paramtres
qu'elle attend.

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.

180/257

7.1.2

La partie cliente

Couche
[ui]

C
1

S
2

Couche
[metier]

Couche
[DAO]

Couche
[JPA /
EclipseLink]

Couche
[JDBC]

SGBD

BD

Java EE - serveur Glassfish

Java SE
7.1.2.1

RMI

Le projet Netbeans du client console

Nous crons maintenant un projet Java de type [Java Application] pour la partie client de l'application. Il n'a pas t possible (juin
2012) de crer un projet Maven pour ce client. Une erreur survient, semble connue mais reste non rsolue.

Une fois le projet cr, nous indiquons qu'il sera client du service web que nous venons de dployer sur le serveur Glassfish :

2
3

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 [4], nous allons dsigner le projet Netbeans du service web


dans la fentre [5], 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 [6], nous dsignons le service web auquel on veut se connecter.

181/257

7
10

8
11

en [7], 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]

Couche
[DAO]

Couche
[JPA /
Toplink]

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 [8].
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 [10] 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] [10] 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
[CalculerFeuilleSalaire.java] et [CalculerFeuilleSalaireResponse.java], 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 super.getPort(new QName("http://metier/", "MetierPort"), Metier.class);

La mthode getMetierPort de la ligne 2 permet d'obtenir une rfrence sur le service web Metier distant.

182/257

7.1.2.2

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] qui tait un client d'un serveur EJB, dans le nouveau projet.

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 [3], le morceau de code o la couche [metier] est instancie [3]. Elle l'est avec du code JNDI pour obtenir une rfrence
sur un EJB distant.

Nous faisons voluer le code de la faon suivante :

le code JNDI 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 [4], il nous reste obtenir une rfrence sur la service web distant [Metier] afin de pouvoir appeler sa mthode
[calculerFeuilleSalaire].
en [5], 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.

183/257

ligne 112, on voit que [calculerFeuilleSalaire] est une mthode de la classe [client.Metier] (ligne 111). Maintenant que nous
savons comment obtenir la couche [metier], le code prcdent peut tre rcrit de la faon suivante :

1. ...
2. // c'est bon - on peut demander la feuille de salaire
3.
FeuilleSalaire feuilleSalaire = null;
4.
Metier metier = null;
5.
try {
6.
// instanciation couche [metier]
7.
metier = new MetierService().getMetierPort();
8.
// calcul de la feuille de salaire
9.
feuilleSalaire = metier.calculerFeuilleSalaire(args[0], nbHeuresTravailles,
nbJoursTravaills);
10.
} catch (Throwable th) {
11.
// chane des exceptions
12.
System.out.println("Chane des exceptions --------------------------------------");
13.
System.out.println(th.getClass().getName() + ":" + th.getMessage());
14.
while (th.getCause() != null) {
15.
th = th.getCause();
16.
System.out.println(th.getClass().getName() + ":" + th.getMessage());
17.
}
18.
System.exit(1);
19.
}
20.
// affichage rapide
21. ...

La ligne 7 rcupre une rfrence sur le service web Metier. Ceci fait, le code de la classe ne change pas, si ce n'est quand mme
qu'en ligne 10, ce n'est pas l'exception de type [Exception] qui est gre mais le type plus gnral Throwable, la classe parent de la
classe Exception. S'il y a exception, nous affichons toutes les causes imbriques de celle-ci jusqu' la cause originelle.
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 client

excuter le client

Les rsultats dans la console sont les suivants :


1. ...
2. Valeurs saisies :
3. N de scurit sociale de l'employ : 254104940426058
4. Nombre d'heures travailles : 150
5. Nombre de jours travaills : 20
6.
7. Informations Employ :
8. Nom : Jouveinal
9. Prnom : Marie
10. Adresse : 5 rue des oiseaux
11. ...

184/257

Avec la configuration suivante :

on obtient les rsultats suivants :


1.
2.
3.
4.

Chane des exceptions -------------------------------------javax.xml.ws.soap.SOAPFaultException:L'employ de n[xx] est introuvable


com.sun.xml.internal.ws.developer.ServerSideException:L'employ de n[xx] est introuvable
Java Result: 1

On notera qu'alors que le service web [Metier] envoie une exception de type [PamException] l'exception reue par le client est de
type [SOAPFaultException]. Mme dans la chane des exceptions, on ne voit pas apparatre le type [PamException].

7.1.3

Le client swing du service web Metier

Travail faire : porter le client swing du projet [mv-pam-client-ejb-metier-dao-JPA-eclipselink] dans le nouveau projet afin que lui
aussi soit client du service web dploy sur le serveur Glassfish.

7.2

Service web 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 :

1
2

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]

185/257

en [4], nous choisissons la version Java EE 6


en [6], 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.

4
5
2

en [1], on ajoute un projet aux dpendances du projet web,


en [2], on slectionne le projet [mv-pam-ejb-metier-dao-eclipselink],
en [4], la porte de la dpendance est provided, c'est dire qu'elle sera fournie par l'environnement d'excution,
en [5], 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 tagues @WebMethod

Nous crons une classe [PamWsEjbMetier] dans un package [pam.ws] :

186/257

La classe [PamWsEjbMetier] est la suivante :


1. package pam.ws;
2.
3. import java.util.List;
4. import javax.ejb.EJB;
5. import javax.jws.WebMethod;
6. import javax.jws.WebService;
7. import jpa.Employe;
8. import metier.FeuilleSalaire;
9. import metier.IMetier;
10. import metier.IMetierLocal;
11.
12. @WebService
13. public class PamWsEjbMetier implements IMetier{
14.
15. @EJB
16. private IMetierLocal metier;
17.
18. @WebMethod
19. public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int
nbJoursTravaills) {
20.
return metier.calculerFeuilleSalaire(SS, nbHeuresTravailles, nbJoursTravaills);
21. }
22.
23. @WebMethod
24. public List<Employe> findAllEmployes() {
25.
return metier.findAllEmployes();
26. }
27.
28. }

lignes 7-10 : la classe importe des classes du module EJB [pam-serveurws-metier-dao-JPA-eclipselink] dont le projet
Maven a t ajout aux dpendances du projet.
ligne 12 : la classe est un service web
ligne 13 : elle implmente l'interface IMetier dfinie dans le module EJB
lignes 18-19 : la mthode calculerFeuilleSalaire est expose comme mthode du service web
lignes 23-24 : la mthode findAllEmployes est expose comme mthode du service web
lignes 15-16 : l'interface locale de l'EJB [Metier] est injecte dans le champ de la ligne 16. Nous utilisons l'interface locale
car l'application web et le module EJB s'excutent dans la mme JVM.
lignes 20 et 25 : 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.

Dans Netbeans, l'application web est reconnue comme exposant un service web :

2
3

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

187/257

Ceci fait, nous crons un nouveau projet [3].

en [4], nous choisissons un projet de type [Enterprise Application].


en [5], nous donnons un nom au projet

6
7
8

en [6], nous configurons le projet. La version de Java EE sera Java EE 6. 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.
en [7], 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 [8], nous ajoutons des dpendances au projet d'entreprise

188/257

10

en [9], on ajoute le projet web de type war,


en [10], on ajoute le projet EJB de type ejb,

11

en [11], 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 [11] :

11
13

12

Le serveur MySQL doit tre lanc et la base [dbpam_eclipselink] disponible et remplie. Ceci fait, l'application d'entreprise peut tre
dploye [12]. En [13], on peut voir qu'elle a bien t dploye sur le serveur Glassfish.
Nous pouvons tester le service web qui vient d'tre dploy :

189/257

1
2

7.2.2

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.

La partie cliente

Travail faire : en suivant la dmarche dcrite au paragraphe 7.1.2.1, page 181, construire un client console du service web
prcdent.

7.3

Service web implment avec Spring 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.11, page 113 :

190/257

7.3.1

La partie serveur

Nous crons une application Maven de type web nomme [mv-pam-ws-spring-tomcat] [1]:

Nous modifions le fichier [pom.xml] pour y inclure les dpendances [2] suivantes :
1.
<dependencies>
2.
<dependency>
3.
<groupId>${project.groupId}</groupId>
4.
<artifactId>mv-pam-spring-hibernate</artifactId>
5.
<version>${project.version}</version>
6.
</dependency>
7.
<!-- Apache CXF dependencies -->
8.
<dependency>
9.
<groupId>org.apache.cxf</groupId>
10.
<artifactId>cxf-rt-frontend-jaxws</artifactId>
11.
<version>2.2.12</version>
12.
</dependency>
13.
<dependency>
14.
<groupId>org.apache.cxf</groupId>
15.
<artifactId>cxf-rt-transports-http</artifactId>
16.
<version>2.2.12</version>
17.
</dependency>
18. </dependencies>

lignes 3-7 : la dpendance sur le projet [mv-pam-spring-hibernate],


lignes 8-17 : les dpendances sur le framework Apache CXF [http://cxf.apache.org/]. Celui-ci facilite la cration de
services web.

Ce fichier [pom.xml] amne de nombreuses dpendances [2].


Revenons l'architecture de l'application :

191/257

Couche
[web]

Couche
[metier]

Couche
[DAO]

Interface
[JPA]

Implmentation
[Hibernate]

Couche
[JDBC]

Spring

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 / web.xml] de la faon suivante :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
3.
<display-name>mv-pam-ws-spring-tomcat</display-name>
4.
<listener>
5.
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
6.
</listener>
7. <!-- Configuration de CXF -->
8.
<servlet>
9.
<servlet-name>CXFServlet</servlet-name>
10.
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
11.
<load-on-startup>1</load-on-startup>
12. </servlet>
13. <servlet-mapping>
14.
<servlet-name>CXFServlet</servlet-name>
15.
<url-pattern>/ws/*</url-pattern>
16. </servlet-mapping>
17. <session-config>
18.
<session-timeout>
19.
30
20.
</session-timeout>
21. </session-config>
22. <welcome-file-list>
23.
<welcome-file>index.jsp</welcome-file>
24. </welcome-file-list>
25. </web-app>

le framework CXF a une dpendance sur Spring. Lignes 4-6 : un listener est dclar. La classe correspondante va tre
charge en mme temps que l'application web. Elle va exploiter le fichier de configuration de Spring [WEB-INF /
applicationContext.xml] :

lignes 8-12 : la servlet CXF qui va grer les appels au service web que nous allons crer,
lignes 13-16 : 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 :

192/257

L'interface [IWsMetier] sera la suivante :


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

package pam.ws;

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 javax.jws.WebService;
import metier.IMetier;
@WebService
public interface IWsMetier extends IMetier{
}

La classe d'implmentation de cette interface est la suivante :


1. package pam.ws;
2.
3. import java.util.List;
4. import javax.jws.WebMethod;
5. import javax.jws.WebService;
6. import jpa.Employe;
7. import metier.FeuilleSalaire;
8. import metier.IMetier;
9.
10. @WebService
11. public class PamWsMetier implements IWsMetier {
12.
13. // couche mtier
14. private IMetier metier;
15.
16. // constructeur
17. public PamWsMetier(){
18.
19. }
20.
21. @WebMethod
22. public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillees, int
nbJoursTravailles) {
23.
return metier.calculerFeuilleSalaire(SS, nbHeuresTravaillees, nbJoursTravailles);
24. }
25.
26. @WebMethod
27. public List<Employe> findAllEmployes() {
28.
return metier.findAllEmployes();
29. }
30.
31. // getters et setters
32.
33. public void setMetier(IMetier metier) {
34.
this.metier = metier;
35. }
36.

193/257

37. }

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 21, 26 : l'annotation @WebMethod fait d'une mthode, une mthode expose par le service web,
lignes 23, 28 : les mthodes sont implmentes l'aide de la couche [mtier].

Il nous reste dfinir le contenu du fichier de configuration de Spring [applicationContext.xml] :

Son contenu est le suivant :


1. <?xml version="1.0" encoding="UTF-8"?>
2. <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3.
xmlns:tx="http://www.springframework.org/schema/tx"
4.
xmlns:jaxws="http://cxf.apache.org/jaxws"
5.
xsi:schemaLocation="http://www.springframework.org/schema/beans
6.
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
7.
http://www.springframework.org/schema/tx
8.
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
9.
http://cxf.apache.org/jaxws
10.
http://cxf.apache.org/schemas/jaxws.xsd">
11.
12. <!-- Apache CXF -->
13. <import resource="classpath:META-INF/cxf/cxf.xml" />
14. <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
15. <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
16.
17. <!-- couches basses -->
18. <import resource="classpath:spring-config-metier-dao.xml" />
19.
20. <!-- service web -->
21. <bean id="wsMetier" class="pam.ws.PamWsMetier">
22.
<property name="metier" ref="metier"/>
23. </bean>
24. <jaxws:endpoint id="wsmetier"
25.
implementor="#wsMetier"
26.
address="/metier">
27. </jaxws:endpoint>
28.
29. </beans>

lignes 13-15 : on importe des fichiers de configuration Apache CXF. Ceux-ci sont cherchs dans le Classpath du projet
(attribut classpath:),
lignes 4, 9, 10 : des espaces de noms spcifiques Apache CXF sont dclars,
ligne 18 : on importe le fichier de configuration Spring du projet [mv-pam-spring-hibernate],
lignes 21-23 : dfinissent le bean du service web avec sa dpendance sur la couche [mtier] (ligne 22),
lignes 24-27 : dfinissent le service web lui-mme,

ligne 25 : le bean Spring implmentant le service web est celui dfini ligne 21 ;

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 web.xml), cette URL devient /ws/metier.

194/257

Notre projet est prt tre excut. Nous l'excutons (Run) et demandons l'URL [http://localhost:8080/mv-pam-ws-springtomcat/ws] dans un navigateur :

La page liste tous les services web dploys. Ici, il n'y en a qu'un. Nous suivons le lien WSDL :
2

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 7.1.2.1, page 181, construire un client console du service web
prcdent.
Note : pour indiquer l'URL du fichier WSDL du service web, on procdera comme suit :

195/257

On mettra en [3], l'URL note prcdemment en [2].

196/257

8 Introduction Java Server Faces


On lira le document Introduction Java Server Faces, Primefaces et Primefaces
[http://tahe.developpez.com/java/primefaces]. On suivra le tutoriel sur Java Server Faces (JSF).

mobile

l'URL

197/257

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
[metier]

Couche
EJB3
[DAO]

Objets image
de la BD

Implmentation
[EclipseLink]

Interface
[JPA]

Couche
[JDBC]

Serveur Glassfish v3

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
[metier, 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 162 et dont l'architecture tait la suivante :
client
Couche
[ui]

Jvm1 - Java SE

serveur
Couche
[DAO]

Couche
[metier]

Couche
[JPA /
EclipseLink]

Couche
[JDBC]

Jvm2 Java EE - serveur Glassfish

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

Client
Java SE

Conteneur Ejb3

Jpa / EclipseLink

Donnes

serveur Java EE

Dans l'architecture de la nouvelle application :

198/257

Jpa 3
Conteneur EJB3
[metier, DAO] 2 EclipseLink

Conteneur web
[web / jsf]

Navigateur

HTTP

SGBD

serveur Java EE

seule la couche [web / JSF] 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

Navigateur
4b

JSF1
JSF2
JSFn

2b

2a

Faces Servlet
3

Gestionnaire
d'vts

Modles

couche
[metier, 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 [javax.faces.webapp.FacesServlet]. On trouve celle-ci dans la bibliothque [JSF-api.jar].

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 [faces-config.xml]. On y trouve la liste des vues et les rgles de transition de l'une l'autre. A partir de la version JSF
2, 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.

199/257

9.2

Fonctionnement de l'application

Lorsque l'application est demande la premire fois, on obtient la page suivante :


A

On remplit alors le formulaire puis on demande le salaire :


B

On obtient le rsultat suivant :

200/257

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 :

201/257

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

Application web
couche [web]
1

Faces Servlet

Navigateur
4b

JSF1
JSF2
JSFn

2b

2a
3

Gestionnaire
d'vts

Modles

couche
[metier, 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 le projet Maven suivant :

1
4

3
6

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

202/257

Nous passons en revue certains de ces lments.

9.3.1

Les fichiers de configuration

Le fichier [web.xml] est celui gnr par dfaut par Netbeans avec de plus la configuration d'une page d'exception :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
3.
<context-param>
4.
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
5.
<param-value>client</param-value>
6.
</context-param>
7.
<context-param>
8.
<param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
9.
<param-value>true</param-value>
10. </context-param>
11. <context-param>
12.
<param-name>javax.faces.PROJECT_STAGE</param-name>
13.
<param-value>Development</param-value>
14. </context-param>
15. <servlet>
16.
<servlet-name>Faces Servlet</servlet-name>
17.
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
18.
<load-on-startup>1</load-on-startup>
19. </servlet>
20. <servlet-mapping>
21.
<servlet-name>Faces Servlet</servlet-name>
22.
<url-pattern>/faces/*</url-pattern>
23. </servlet-mapping>
24. <session-config>
25.
<session-timeout>
26.
30
27.
</session-timeout>
28. </session-config>
29. <welcome-file-list>
30.
<welcome-file>faces/index.xhtml</welcome-file>
31. </welcome-file-list>
32. <error-page>
33.
<error-code>500</error-code>
34.
<location>/faces/exception.xhtml</location>
35. </error-page>
36. <error-page>
37.
<exception-type>java.lang.Exception</exception-type>
38.
<location>/faces/exception.xhtml</location>
39. </error-page>
40. </web-app>

ligne 30 : [index.html] est la page d'accueil de l'application


lignes 32-39 : configuration de la page d'exception

La page [exception.html] est tire de [ref3]. Son code est le suivant :


1. <?xml version='1.0' encoding='UTF-8' ?>
2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3.
4. <html xmlns="http://www.w3.org/1999/xhtml"
5.
xmlns:h="http://java.sun.com/JSF/html"
6.
xmlns:f="http://java.sun.com/JSF/core">

203/257

7.
8.
9.
10.
11.
12.

<f:view locale="#{changeLocale.locale}">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('$
{request.contextPath}/resources/images/standard.jpg');">
13.
<h:form id="formulaire">
14.
<h3><h:outputText value="#{msg['exception.header']}"/></h3>
15.
<h:panelGrid columnClasses="col1,col2" columns="2" border="1">
16.
<h:outputText value="#{msg['exception.httpCode']}"/>
17.
<h:outputText value="#{requestScope['javax.servlet.error.status_code']}"/>
18.
<h:outputText value="#{msg['exception.message']}"/>
19.
<h:outputText value="#{requestScope['javax.servlet.error.exception']}"/>
20.
<h:outputText value="#{msg['exception.requestUri']}"/>
21.
<h:outputText value="#{requestScope['javax.servlet.error.request_uri']}"/>
22.
<h:outputText value="#{msg['exception.servletName']}"/>
23.
<h:outputText value="#{requestScope['javax.servlet.error.servlet_name']}"/>
24.
</h:panelGrid>
25.
</h:form>
26.
</h:body>
27. </f:view>
28. </html>

Toute exception non explicitement gre par le code de l'application web provoquera l'affichage d'une page analogue celle cidessous :

Le fichier [faces-config.xml] sera le suivant :


1. <?xml version="1.0" encoding="UTF-8"?>
2. <!-- =========== FULL CONFIGURATION FILE ================================== -->
3. <faces-config version="2.0"
4.
xmlns="http://java.sun.com/xml/ns/javaee"
5.
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6.
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
7.
8.
<application>
9.
<resource-bundle>
10.
<base-name>
11.
messages
12.
</base-name>
13.
<var>msg</var>
14.
</resource-bundle>
15.
<message-bundle>messages</message-bundle>
16. </application>
17. </faces-config>

On notera les points suivants :

lignes 9-14 : le fichier [messages.properties] sera utilis pour l'internationalisation des pages. Il sera accessible dans les
pages XHTML via la cl msg.

204/257

9.3.2

ligne 15 : dfinit le fichier [messages.properties] 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. Cette
possibilit n'est pas utilise ici.

La feuille de style

Le fichier [styles.css] est le suivant :


1. .libelle{
2.
background-color: #ccffff;
3.
font-family: 'Times New Roman',Times,serif;
4.
font-size: 14px;
5.
font-weight: bold
6. }
7. body{
8.
background-color: #ffccff
9. }
10.
11. .error{
12.
color: #ff3333
13. }
14.
15. .info{
16.
background-color: #99cc00
17. }
18.
19. .titreInfos{
20.
background-color: #ffcc00
21. }

Voici des exemples de code JSF utilisant ces styles :


<h:outputText value="#{msg['form.infos.employ']}"
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. form.titre=Feuille de salaire
2. form.comboEmployes.libell\u00e9=Employ\u00e9
3. form.heuresTravaill\u00e9es.libell\u00e9=Heures travaill\u00e9es
4. form.joursTravaill\u00e9s.libell\u00e9=Jours travaill\u00e9s
5. form.heuresTravaill\u00e9es.required=Indiquez le nombre d'heures travaill\u00e9es
6. form.heuresTravaill\u00e9es.validation=Donn\u00e9e incorrecte
7. form.joursTravaill\u00e9s.required=Indiquez le nombre de jours travaill\u00e9s
8. form.joursTravaill\u00e9s.validation=Donn\u00e9e incorrecte
9. form.btnSalaire.libell\u00e9=Salaire
10. form.btnRaz.libell\u00e9=Raz
11. exception.header=L'exception suivante s'est produite
12. exception.httpCode=Code HTTP de l'erreur

205/257

13. exception.message=Message de l'exception


14. exception.requestUri=Url demand\u00e9e lors de l'erreur
15. exception.servletName=Nom de la servlet demand\u00e9e lorsque l'erreur s'est produite
16. form.infos.employ\u00e9=Informations Employ\u00e9
17. form.employe.nom=Nom
18. form.employe.pr\u00e9nom=Pr\u00e9nom
19. form.employe.adresse=Adresse
20. form.employe.ville=Ville
21. form.employe.codePostal=Code postal
22. form.employe.indice=Indice
23. form.infos.cotisations=Informations Cotisations sociales
24. form.cotisations.csgrds=CSGRDS
25. form.cotisations.csgd=CSGD
26. form.cotisations.retraite=Retraite
27. form.cotisations.secu=S\u00e9curit\u00e9 sociale
28. form.infos.indemnites=Informations Indemnit\u00e9s
29. form.indemnites.salaireHoraire=Salaire horaire
30. form.indemnites.entretienJour=Entretien / Jour
31. form.indemnites.repasJour=Repas / Jour
32. form.indemnites.cong\u00e9sPay\u00e9s=Cong\u00e9s pay\u00e9s
33. form.infos.salaire=Informations Salaire
34. form.salaire.base=Salaire de base
35. form.salaire.cotisationsSociales=Cotisations sociales
36. form.salaire.entretien=Indemnit\u00e9s d'entretien
37. form.salaire.repas=Indemnit\u00e9s de repas
38. form.salaire.net=Salaire net

Ces messages sont tous utiliss dans la page [index.xhtml] l'exception de ceux des lignes 11-15 utiliss dans la page
[exception.xhtml].

9.3.4

La porte des beans

Le bean [web.forms.Form] aura une porte request :


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

import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
@ManagedBean
@RequestScoped
public class Form implements Serializable {

Le bean [web.utils.ChangeLocale] aura une porte session :


1. package web.utils;
2.
3. import java.io.Serializable;
4. import javax.faces.bean.ManagedBean;
5. import javax.faces.bean.SessionScoped;
6.
7. @ManagedBean
8. @ApplicationScoped
9. public class ChangeLocale implements Serializable{
10. // la locale des pages
11. private String locale="fr";
12.
13. public ChangeLocale() {
14. }
15.
16. public String setFrenchLocale(){
17.
locale="fr";
18.
return null;

206/257

19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35. }

9.3.5

}
public String setEnglishLocale(){
locale="en";
return null;
}
public String getLocale() {
return locale;
}
public void setLocale(String locale) {
this.locale = locale;
}

La couche [mtier]

La couche [mtier] implmente l'interface IMetierLocal suivante :


1. package metier;
2.
3. import java.util.List;
4. import javax.ejb.Local;
5. import jpa.Employe;
6.
7. @Local
8. public interface IMetierLocal {
9.
// obtenir la feuille de salaire
10. FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int
nbJoursTravaills );
11. // liste des employs
12. List<Employe> findAllEmployes();
13. }

Cette interface est celle utilise dans la partie serveur de l'application client / serveur dcrite au paragraphe 6.1, page 162.
La classe Metier que nous allons utiliser pour tester la couche [web] implmente cette interface de la faon suivante :
1. package metier;
2.
3. ...
4. public class Metier implements IMetierLocal {
5.
6.
// dictionnaire des employes index par le n SS
7.
private Map<String,Employe> hashEmployes=new HashMap<String,Employe>();
8.
// liste des employs
9.
private List<Employe> listEmployes;
10.
11. // obtenir la feuille de salaire
12. public FeuilleSalaire calculerFeuilleSalaire(String SS,
13.
double nbHeuresTravailles, int nbJoursTravaills) {
14.
// on rcupre l'employ de n SS
15.
Employe e=hashEmployes.get(SS);
16.
// on rend une feuille de salaire fiictive
17.
return new FeuilleSalaire(e,new Cotisation(3.49,6.15,9.39,7.88),new
ElementsSalaire(100,100,100,100,100));
18. }
19.

207/257

20.
21.
22.
23.
24.
25.

// liste des employs


public List<Employe> findAllEmployes() {
if(listEmployes==null){
// on cre une liste de deux employs
listEmployes=new ArrayList<Employe>();
listEmployes.add(new Employe("254104940426058","Jouveinal","Marie","5 rue des
oiseaux","St Corentin","49203",new Indemnite(2,2.1,2.1,3.1,15)));
26.
listEmployes.add(new Employe("260124402111742","Laverti","Justine","La brlerie","St
Marcel","49014",new Indemnite(1,1.93,2,3,12)));
27.
// dictionnaire des employes index par le n SS
28.
for(Employe e:listEmployes){
29.
hashEmployes.put(e.getSS(),e);
30.
}
31.
}
32.
// on rend la liste des employs
33.
return listEmployes;
34. }
35. }

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 [index.xhtml] et son modle [Form.java]

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

Question : Construire le formulaire [index.xhtml] et son modle [Form.java] ncessaires pour obtenir la page suivante :

4
2

Les composants de saisie sont les suivants :


n

id

type JSF

modle

comboEmployes <h:selectOneMenu>

String comboEmployesValue

heuresTravaillees <h:inputText>

List<Employe> getEmployes()
String heuresTravailles

joursTravailles

String joursTravaills

<h:inputText>

rle
contient la liste des employs sous la forme
"prnom nom".
nombre d'heures travailles - nombre rel
nombre de jours travaills - nombre entier

208/257

btnSalaire

<h:commandButton>

lance le calcul du salaire

btnRaz

<h:commandButton>

remet le formulaire dans son tat premier

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.

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. Avec
Glassfish 3.1.2, a plante l'application.

9.4.2

tape 2

Question : complter le formulaire [index.xhtml] et son modle [Form.java] 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="#{form.feuilleSalaire.employe.nom}"/>

L'expression de l'attribut value sera value comme suit :

209/257

[form].getFeuilleSalaire().getEmploye().getNom() o [form] reprsente une instance de la classe [Form.java]. 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

Question : complter le formulaire [index.xhtml] et son modle [Form.java] pour obtenir les informations supplmentaires
suivantes :

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.
<h:outputFormat value="{0,number,currency}">
2.
<f:param value="#{form.feuilleSalaire.employe.indemnite.entretienJour}"/>
3. </h:outputFormat>

On aurait pu crire :
<h:outputText value="#{form.feuilleSalaire.employe.indemnite.entretienJour} ">

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


Question : complter le formulaire [index.xhtml] et son modle [Form.java] 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].

210/257

Le formulaire rendu par le bouton [Raz] n'est pas tout le formulaire mais seulement la partie saisie de celui-ci :

Ce rsultat peut tre obtenu avec une balise <f:subview> utilise de la faon suivante :
1.
<f:subview id="viewInfos" rendered="#{form.viewInfosIsRendered}">
2. ... la partie du formulaire qu'on veut pouvoir ne pas afficher
3. </f:subview>

La balise <f:subview> encadre toute la partie du formulaire qui est susceptible d'tre 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.

211/257

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

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 :

212/257

[1] : on copie le nouveau projet et on le colle dans l'onglet [Projects],


[2] : on lui donne un nom et on fixe son dossier,
[3] : le projet cr,

Le nouveau projet porte le mme nom que l'ancien. Nous changeons cela :

[4] : nous renommons le projet,


[5] : on change son nom ainsi que celui de l'artifactID.

213/257

Nous avons peu de modifications faire pour adapter cette couche web son nouvel environnement : la couche [metier] simule
doit tre remplace par la couche [metier, DAO, JPA] du serveur construit au paragraphe 6.1, page 162. 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 162.

1
3

en [1], on ajoute une dpendance au projet,


en [2], on slectionne le projet Maven de la couche [mtier]. En [3], on prcise son type et en [4] 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 [5], la dpendance a t ajoute.

Le fichier [pom.xml] est alors le suivant :


1. <dependencies>
2.
<dependency>
3.
<groupId>${project.groupId}</groupId>
4.
<artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
5.
<version>${project.version}</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>6.0</version>
13.
<scope>provided</scope>
14.
</dependency>
15. </dependencies>

Nous pouvons maintenant supprimer les paquetages de la couche [mtier] qui ne sont plus ncessaires :

214/257

Il nous faut galement modifier le code du bean [Form.java] :


1.public class Form {
2.
3. public Form() {
4. }
5.
6. // couche mtier
7. private IMetierLocal metier=new Metier();
8.
9. // champs du formulaire
10....

La ligne 7 instanciait la couche [mtier] simule. Dsormais elle doit rfrencer la couche [mtier] relle. Le code prcdent devient
le suivant :
1. public class Form {
2.
3.
public Form() {
4.
}
5.
6.
// couche mtier
7.
@EJB
8.
private IMetierLocal metier;
9.
10. // champs du formulaire

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

Navigateur
4b

JSF1
JSF2
JSFn

2b

2a

Faces Servlet
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 :

215/257

4
3
1

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 [5], nous choisissons Java EE 6


en [6], 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-alone]. 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 [6], nous crons un projet d'entreprise sans modules. Nous allons lui ajouter ses modules web et ejb ultrieurement.
en [7], 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 :

1
2

216/257

en [1], ajout d'une nouvelle dpendance,


en [2], ajout du projet EJB [mv-pam-ejb-metier-dao-eclipselink]. On notera son type ejb,
en [3], ajout du projet web [mv-pam-jsf2-ejb]. On notera son type war.

Le fichier [pom.xml] est alors le suivant :


1. <dependencies>
2.
<dependency>
3.
<groupId>${project.groupId}</groupId>
4.
<artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
5.
<version>${project.version}</version>
6.
<type>ejb</type>
7.
</dependency>
8.
<dependency>
9.
<groupId>${project.groupId}</groupId>
10.
<artifactId>mv-pam-jsf2-ejb</artifactId>
11.
<version>${project.version}</version>
12.
<type>war</type>
13.
</dependency>
14.
</dependencies>

Avant le dploiement de l'application d'entreprise [mv-pam-webapp-ear], on s'assurera que la base MySQL [dbpam_eclipselink]
existe et est remplie. Ceci fait, nous pouvons dployer l'application d'entreprise [mv-pam-webapp-ear] :

en [1], l'application d'entreprise est dploye


en [2], l'application d'entreprise [mv-pam-webapp-ear] a bien t dploye.

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.

217/257

Le lecteur est invit refaire les tests de la version web n 1. Voici un exemple d'excution :

218/257

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]
Form.java
Modle M
Gestionnaires
d'vts

couche
[metier]
simule

219/257

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

220/257

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

221/257

3
7
4
2

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.

11.2.1

Les fichiers de configuration

Les fichiers [web.xml] et [faces-config.xml] sont identiques ceux du projet prcdent l'exception d'un dtail dans [web.xml] :
1. <?xml version="1.0" encoding="UTF-8"?>
2. <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
3.
...
4.
<welcome-file-list>
5.
<welcome-file>faces/saisie.xhtml</welcome-file>
6.
</welcome-file-list>
7.
...
8. </web-app>

11.2.2

lignes 4-6 : la page d'accueil est la page [saisie.xhtml]

La feuille de style

Le fichier [styles.css] est le suivant :


1. .simulationsHeader {
2.
text-align: center;
3.
font-style: italic;
4.
color: Snow;
5.
background: Teal;
6. }

222/257

7.
8. .simuNum {
9.
height: 25px;
10.
text-align: center;
11.
background: MediumTurquoise;
12. }
13. .simuNom {
14.
text-align: left;
15.
background: PowderBlue;
16. }
17. .simuPrenom {
18.
width: 6em;
19.
text-align: left;
20.
color: Black;
21.
background: MediumTurquoise;
22. }
23. .simuHT {
24.
width: 3em;
25.
text-align: center;
26.
color: Black;
27.
background: PowderBlue;
28. }
29. .simuJT {
30.
width: 3em;
31.
text-align: center;
32.
color: Black;
33.
background: MediumTurquoise;
34. }
35. .simuSalaireBase {
36.
width: 9em;
37.
text-align: center;
38.
color: Black;
39.
background: PowderBlue;
40. }
41. .simuIndemnites {
42.
width: 3em;
43.
text-align: center;
44.
color: Black;
45.
background: MediumTurquoise;
46. }
47. .simuCotisationsSociales {
48.
width: 6em;
49.
text-align: center;
50.
background: PowderBlue;
51. }
52.
53. .simuSalaireNet {
54.
width: 10em;
55.
text-align: center;
56.
background: MediumTurquoise;
57. }
58.
59. .erreursHeaders {
60.
background: Teal;
61.
background-color: #ff6633;
62.
color: Snow;
63.
font-style: italic;
64.
text-align: center
65.
66. }
67.
68. .erreurClasse {
69.
background: MediumTurquoise;

223/257

70.
background-color: #ffcc66;
71.
height: 25px;
72.
text-align: center
73. }
74.
75. .erreurMessage {
76.
background: PowderBlue;
77.
background-color: #ffcc99;
78.
text-align: left
79. }

Voici des exemples de code JSF utilisant ces styles :


Vue Simulations
<h:dataTable value="#{form.simulations}" var="simulation"
headerClass="simulationsHeaders"
columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisat
ionsSociales,simuSalaireNet">

Le rsultat obtenu est le suivant :

Vue Erreur
<h:dataTable value="#{form.erreurs}" 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. form.titre=Simulateur de calcul de paie
2. form.comboEmployes.libell\u00e9=Employ\u00e9
3. form.heuresTravaill\u00e9es.libell\u00e9=Heures travaill\u00e9es
4. form.joursTravaill\u00e9s.libell\u00e9=Jours travaill\u00e9s
5. form.heuresTravaill\u00e9es.required=Indiquez le nombre d'heures travaill\u00e9es
6. form.heuresTravaill\u00e9es.validation=Donn\u00e9e incorrecte
7. form.joursTravaill\u00e9s.required=Indiquez le nombre de jours travaill\u00e9s
8. form.joursTravaill\u00e9s.validation=Donn\u00e9e incorrecte
9. form.btnSalaire.libell\u00e9=Salaire
10. form.btnRaz.libell\u00e9=Raz
11. exception.header=L'exception suivante s'est produite
12. exception.httpCode=Code HTTP de l'erreur
13. exception.message=Message de l'exception

224/257

14. exception.requestUri=Url demand\u00e9e lors de l'erreur


15. exception.servletName=Nom de la servlet demand\u00e9e lorsque l'erreur s'est produite
16. form.infos.employ\u00e9=Informations Employ\u00e9
17. form.employe.nom=Nom
18. form.employe.pr\u00e9nom=Pr\u00e9nom
19. form.employe.adresse=Adresse
20. form.employe.ville=Ville
21. form.employe.codePostal=Code postal
22. form.employe.indice=Indice
23. form.infos.cotisations=Informations Cotisations sociales
24. form.cotisations.csgrds=CSGRDS
25. form.cotisations.csgd=CSGD
26. form.cotisations.retraite=Retraite
27. form.cotisations.secu=S\u00e9curit\u00e9 sociale
28. form.infos.indemnites=Informations Indemnit\u00e9s
29. form.indemnites.salaireHoraire=Salaire horaire
30. form.indemnites.entretienJour=Entretien / Jour
31. form.indemnites.repasJour=Repas / Jour
32. form.indemnites.cong\u00e9sPay\u00e9s=Cong\u00e9s pay\u00e9s
33. form.infos.salaire=Informations Salaire
34. form.salaire.base=Salaire de base
35. form.salaire.cotisationsSociales=Cotisations sociales
36. form.salaire.entretien=Indemnit\u00e9s d'entretien
37. form.salaire.repas=Indemnit\u00e9s de repas
38. form.salaire.net=Salaire net
39. form.menu.faireSimulation=| Faire la simulation
40. form.menu.effacerSimulation=| Effacer la simulation
41. form.menu.enregistrerSimulation=| Enregistrer la simulation
42. form.menu.retourSimulateur=| Retour au simulateur
43. form.menu.voirSimulations=| Voir les simulations
44. form.menu.terminerSession=| Terminer la session
45. simulations.headers.nom=Nom
46. simulations.headers.nom=Nom
47. simulations.headers.prenom=Pr\u00e9nom
48. simulations.headers.heuresTravaillees=Heures travaill\u00e9es
49. simulations.headers.joursTravailles=Jours Travaill\u00e9s
50. simulations.headers.salaireBase=Salaire de base
51. simulations.headers.indemnites=Indemnit\u00e9s
52. simulations.headers.cotisationsSociales=Cotisations sociales
53. simulations.headers.salaireNet=SalaireNet
54. simulations.headers.numero=N\u00b0
55. erreur.titre=Une erreur s'est produite.
56. erreur.exceptions=Cha\u00eene des exceptions
57. exception.type=Type de l'exception
58. exception.message=Message associ\u00e9

11.2.4

La couche [mtier]

La couche [mtier] simule est modifie de la faon suivante :


1. package metier;
2.
3. ...
4. public class Metier implements IMetierLocal, Serializable {
5.
6.
// liste des employes
7.
private Map<String,Employe> hashEmployes=new HashMap<String,Employe>();
8.
private List<Employe> listEmployes;
9.
10. // obtenir la feuille de salaire
11. public FeuilleSalaire calculerFeuilleSalaire(String SS,
12.
double nbHeuresTravailles, int nbJoursTravaills) {

225/257

13.
14.
15.
16.
17.
18.
19.
20.

// on rcupre l'employ
Employe e=hashEmployes.get(SS);
// on rend une exception si l'employ n'existe pas
if(e==null){
throw new PamException(String.format("L'employ de n SS [%s] n'existe pas",SS),1);
}
// on rend une feuille de salaire fictive
return new FeuilleSalaire(e,new Cotisation(3.49,6.15,9.39,7.88),new
ElementsSalaire(100,100,100,100,100));
21. }
22.
23. // liste des employs
24. public List<Employe> findAllEmployes() {
25.
if(listEmployes==null){
26.
// on cre une liste de trois employs
27.
listEmployes=new ArrayList<Employe>();
28.
listEmployes.add(new Employe("254104940426058","Jouveinal","Marie","5 rue des
oiseaux","St Corentin","49203",new Indemnite(2,2.1,2.1,3.1,15)));
29.
listEmployes.add(new Employe("260124402111742","Laverti","Justine","La brlerie","St
Marcel","49014",new Indemnite(1,1.93,2,3,12)));
30.
// dictionnaire des employes
31.
for(Employe e:listEmployes){
32.
hashEmployes.put(e.getSS(),e);
33.
}
34.
// on ajoute un employ qui n'existera pas dans le dictionnaire
35.
listEmployes.add(new Employe("X","Y","Z","La brlerie","St Marcel","49014",new
Indemnite(1,1.93,2,3,12)));
36.
}
37.
// on rend la liste des employs
38.
return listEmployes;
39. }
40. }

11.3

ligne 8 : la liste des employs


ligne 7 : la mme liste sous forme de dictionnaire index par le n SS des employs
lignes 24-39 : 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 8.
lignes 27-33 : une liste et un dictionnaire de deux employs sont crs
ligne 35 : un employ est ajout la liste employes (ligne 8) mais pas au dictionnaire hashEmployes (ligne 7). Ceci pour
qu'il apparaisse dans le combo des employs mais qu'ensuite il ne soit pas reconnu par la mthode calculerFeuilleSalaire
(ligne 14) afin que celle-ci lance une exception (ligne 17).
lignes 11-21 : la mthode calculerFeuilleSalaire
ligne 14 : l'employ est cherch dans le dictionnaire hashEmployes via son n SS. S'il n'est pas trouv, une exception est
lance (lignes 16-18). Ainsi aurons-nous une exception pour l'employ de n SS X ajout ligne 35 dans la liste employs
mais pas dans le dictionnaire hashEmployes.
ligne 20, une feuille de salaire fictive est cre et rendue.

Les beans de l'application

Il y aura trois beans de trois portes diffrentes :

226/257

11.3.1

Le bean ApplicationData

Le bean ApplicationData sera de porte application :


1. package web.beans.application;
2.
3. import java.io.Serializable;
4. import java.util.logging.Logger;
5. import javax.annotation.PostConstruct;
6. import javax.enterprise.context.ApplicationScoped;
7. import javax.inject.Named;
8. import metier.IMetierLocal;
9. import metier.Metier;
10.
11. @Named
12. @ApplicationScoped
13. public class ApplicationData implements Serializable {
14.
15. // couche mtier
16. private IMetierLocal metier = new Metier();
17. // logger
18. private boolean logsEnabled = true;
19. private final Logger logger = Logger.getLogger("pam");
20.
21. public ApplicationData() {
22. }
23.
24. @PostConstruct
25. public void init() {
26.
// log
27.
if (isLogsEnabled()) {
28.
logger.info("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 [javax.enterprise.context.ApplicationScoped] (ligne 6) et non [javax.faces.bean.ApplicationScoped] 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.

package web.beans.session;
import
import
import
import

java.io.Serializable;
java.util.ArrayList;
java.util.List;
javax.annotation.PostConstruct;

227/257

7. import javax.enterprise.context.SessionScoped;
8. import javax.inject.Inject;
9. import javax.inject.Named;
10. import web.beans.application.ApplicationData;
11. import web.entities.Simulation;
12.
13. @Named
14. @SessionScoped
15. public class SessionData implements Serializable {
16.
17. // application
18. @Inject
19. private ApplicationData applicationData;
20. // simulations
21. private List<Simulation> simulations = new ArrayList<Simulation>();
22. private int numDerniereSimulation = 0;
23. private Simulation simulation;
24. // menus
25. private boolean menuFaireSimulationIsRendered = true;
26. private boolean menuEffacerSimulationIsRendered = true;
27. private boolean menuEnregistrerSimulationIsRendered;
28. private boolean menuVoirSimulationsIsRendered;
29. private boolean menuRetourSimulateurIsRendered;
30. private boolean menuTerminerSessionIsRendered = true;
31. // locale
32. private String locale="fr_FR";
33.
34. // constructeur
35. public SessionData() {
36. }
37.
38. @PostConstruct
39. public void init() {
40.
// log
41.
if (applicationData.isLogsEnabled()) {
42.
applicationData.getLogger().info("SessionData");
43.
}
44. }
45.
46. // gestion des menus
47. public void setMenu(boolean menuFaireSimulationIsRendered, boolean
menuEnregistrerSimulationIsRendered, boolean menuEffacerSimulationIsRendered, boolean
menuVoirSimulationsIsRendered, boolean menuRetourSimulateurIsRendered, boolean
menuTerminerSessionIsRendered) {
48.
this.setMenuFaireSimulationIsRendered(menuFaireSimulationIsRendered);
49.
this.setMenuEnregistrerSimulationIsRendered(menuEnregistrerSimulationIsRendered);
50.
this.setMenuVoirSimulationsIsRendered(menuVoirSimulationsIsRendered);
51.
this.setMenuEffacerSimulationIsRendered(menuEffacerSimulationIsRendered);
52.
this.setMenuRetourSimulateurIsRendered(menuRetourSimulateurIsRendered);
53.
this.setMenuTerminerSessionIsRendered(menuTerminerSessionIsRendered);
54. }
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,

228/257

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. package web.beans.request;
2.
3. import java.util.ArrayList;
4. import java.util.List;
5. import javax.enterprise.context.RequestScoped;
6. import javax.faces.context.FacesContext;
7. import javax.inject.Inject;
8. import javax.inject.Named;
9. import javax.servlet.http.HttpServletRequest;
10. import jpa.Employe;
11. import metier.FeuilleSalaire;
12. import web.beans.application.ApplicationData;
13. import web.beans.session.*;
14. import web.entities.Erreur;
15. import web.entities.Simulation;
16.
17. @Named
18. @RequestScoped
19. public class Form {
20.
21. public Form() {
22. }
23. // autres beans
24. @Inject
25. private ApplicationData applicationData;
26. @Inject
27. private SessionData sessionData;
28. // le modle des vues
29. private String comboEmployesValue = "";
30. private String heuresTravailles = "";
31. private String joursTravaills = "";
32. private Integer numSimulationToDelete;
33. private List<Erreur> erreurs = new ArrayList<Erreur>();
34. private FeuilleSalaire feuilleSalaire;
35.
36.
37. // liste des employs
38. public List<Employe> getEmployes(){
39.
return applicationData.getMetier().findAllEmployes();
40. }
41.
42. // action du menu
43. public String faireSimulation() {
44.
...
45. }
46.
47. public String enregistrerSimulation() {
48. ...
49. }
50.

229/257

51. public String effacerSimulation() {


52. ...
53. }
54.
55. public String voirSimulations() {
56. ...
57. }
58.
59. public String retourSimulateur() {
60.
...
61. }
62.
63. public String terminerSession() {
64. ...
65. }
66.
67. public String retirerSimulation() {
68. ...
69. }
70.
71. private void razFormulaire() {
72. ...
73. }
74.
75. // getters et setters
76. ...
77. }

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

[layout.xhtml]

La page [layout.xhtml] 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"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3. <html xmlns="http://www.w3.org/1999/xhtml"

230/257

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

xmlns:h="http://java.sun.com/JSF/html"
xmlns:f="http://java.sun.com/JSF/core"
xmlns:ui="http://java.sun.com/JSF/facelets">

<f:view locale="#{sessionData.locale}">
<h:head>
<title><h:outputText value="#{msg['form.titre']}"/></title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<script type="text/javascript">
function raz(){
// on change les valeurs postes
document.forms['formulaire'].elements['formulaire:comboEmployes'].value="0";
document.forms['formulaire'].elements['formulaire:heuresTravaillees'].value="0";
document.forms['formulaire'].elements['formulaire:joursTravailles'].value="0";
}
</script>
<h:body style="background-image: url('$
{request.contextPath}/resources/images/standard.jpg');">
22.
<h:form id="formulaire">
23.
<!-- entete -->
24.
<ui:include src="entete.xhtml" />
25.
<!-- contenu -->
26.
<ui:insert name="part1" >
27.
Gestion des assistantes maternelles
28.
</ui:insert>
29.
<ui:insert name="part2"/>
30.
</h:form>
31.
</h:body>
32. </f:view>
33. </html>

Toute vue est constitue des lments suivants :

un entte affich par le fragment [entete.xhtml] (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 [layout.xhtml] est le suivant :

en [1], l'entte [entete.xhtml],


en [2], le fragment part1.

231/257

11.4.2

L'entte [entete.xhtml]

Le code de la page [entete.xhtml] est le suivant :


1. <?xml version='1.0' encoding='UTF-8' ?>
2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3. <html xmlns="http://www.w3.org/1999/xhtml"
4.
xmlns:h="http://java.sun.com/JSF/html"
5.
xmlns:f="http://java.sun.com/JSF/core"
6.
xmlns:ui="http://java.sun.com/JSF/facelets">
7.
8.
<ui:composition>
9.
<!-- entete -->
10.
<h:panelGrid columns="2">
11.
<h:panelGroup>
12.
<h2><h:outputText value="#{msg['form.titre']}"/></h2>
13.
</h:panelGroup>
14.
<h:panelGroup>
15.
<h:panelGrid columns="1">
16.
<h:commandLink id="cmdFaireSimulation"
value="#{msg['form.menu.faireSimulation']}" action="#{form.faireSimulation}"
rendered="#{sessionData.menuFaireSimulationIsRendered}"/>
17.
<h:commandLink id="cmdEffacerSimulation" onclick="raz()"
value="#{msg['form.menu.effacerSimulation']}" action="#{form.effacerSimulation}"
rendered="#{sessionData.menuEffacerSimulationIsRendered}"/>
18.
<h:commandLink id="cmdEnregistrerSimulation" immediate="true"
value="#{msg['form.menu.enregistrerSimulation']}" action="#{form.enregistrerSimulation}"
rendered="#{sessionData.menuEnregistrerSimulationIsRendered}"/>
19.
<h:commandLink id="cmdVoirSimulations" immediate="true"
value="#{msg['form.menu.voirSimulations']}" action="#{form.voirSimulations}"
rendered="#{sessionData.menuVoirSimulationsIsRendered}"/>
20.
<h:commandLink id="cmdRetourSimulateur" immediate="true"
value="#{msg['form.menu.retourSimulateur']}" action="#{form.retourSimulateur}"
rendered="#{sessionData.menuRetourSimulateurIsRendered}"/>
21.
<h:commandLink id="cmdTerminerSession" immediate="true"
value="#{msg['form.menu.terminerSession']}" action="#{form.terminerSession}"
rendered="#{sessionData.menuTerminerSessionIsRendered}"/>
22.
</h:panelGrid>
23.
</h:panelGroup>
24.
</h:panelGrid>
25.
<hr/>
26. </ui:composition>
27. </html>

11.5
11.5.1

ligne 12 : le titre de l'application,


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 [layout.xhtml],
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

232/257

La page d'accueil est la page [saisie.xhtml] suivante :


1. <?xml version='1.0' encoding='UTF-8' ?>
2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3. <html xmlns="http://www.w3.org/1999/xhtml"
4.
xmlns:h="http://java.sun.com/JSF/html"
5.
xmlns:f="http://java.sun.com/JSF/core"
6.
xmlns:ui="http://java.sun.com/JSF/facelets">
7.
8.
<ui:composition template="layout.xhtml">
9.
<ui:define name="part1">
10.
<ui:include src="saisie2.xhtml"/>
11.
</ui:define>
12. </ui:composition>
13. </html>

ligne 8 : elle s'affiche l'intrieur de la page [layout.xhtml],


ligne 9 : la place du fragment nomm part1. Dans ce fragment, on affiche la page [saisie2.xhtml] :

1. <?xml version='1.0' encoding='UTF-8' ?>


2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3. <html xmlns="http://www.w3.org/1999/xhtml"
4.
xmlns:h="http://java.sun.com/JSF/html"
5.
xmlns:f="http://java.sun.com/JSF/core"
6.
xmlns:ui="http://java.sun.com/JSF/facelets">
7.
8.
<h:panelGrid columns="3">
9.
<h:outputText value="#{msg['form.comboEmployes.libell']}"/>
10.
<h:outputText value="#{msg['form.heuresTravailles.libell']}"/>
11.
<h:outputText value="#{msg['form.joursTravaills.libell']}"/>
12.
<h:selectOneMenu id="comboEmployes" value="#{form.comboEmployesValue}">
13.
<f:selectItems .../>
14.
</h:selectOneMenu>
15.
<h:inputText id="heuresTravaillees" value="#{form.heuresTravailles}" required="true"
requiredMessage="#{msg['form.heuresTravailles.required']}"
validatorMessage="#{msg['form.heuresTravailles.validation']}">
16.
<f:validateDoubleRange minimum="0" maximum="300"/>
17.
</h:inputText>
18.
<h:inputText id="joursTravailles" value="#{form.joursTravaills}" required="true"
requiredMessage="#{msg['form.joursTravaills.required']}"
validatorMessage="#{msg['form.joursTravaills.validation']}">
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 :

233/257

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

Question : 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['form.menu.faireSimulation']}"
action="#{form.faireSimulation}" rendered="#{sessionData.menuFaireSimulationIsRendered}"/>

L'action [faireSimulation] calcule une feuille de salaire :

A partir de la page prcdente, on obtient le rsultat suivant :

234/257

La simulation est affiche avec la page [simulation.xhtml] suivante :


1. <?xml version='1.0' encoding='UTF-8' ?>
2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3. <html xmlns="http://www.w3.org/1999/xhtml"
4.
xmlns:h="http://java.sun.com/JSF/html"
5.
xmlns:f="http://java.sun.com/JSF/core"
6.
xmlns:ui="http://java.sun.com/JSF/facelets">
7.
8.
<ui:composition template="layout.xhtml">
9.
<ui:define name="part1">
10.
<ui:include src="saisie2.xhtml"/>
11.
</ui:define>
12.
<ui:define name="part2">
13.
<h:outputText value="#{msg['form.infos.employ']}" styleClass="titreInfos"/>
14.
<br/><br/>
15.
<h:panelGrid columns="3" rowClasses="libelle,info">
16.
<h:outputText value="#{msg['form.employe.nom']}"/>
17.
<h:outputText value="#{msg['form.employe.prnom']}"/>
18.
<h:outputText value="#{msg['form.employe.adresse']}"/>
19.
<h:outputText value="#{form.feuilleSalaire.employe.nom}"/>
20.
<h:outputText value="#{form.feuilleSalaire.employe.prenom}"/>
21.
<h:outputText value="#{form.feuilleSalaire.employe.adresse}"/>
22.
</h:panelGrid>
23.
<h:panelGrid columns="3" rowClasses="libelle,info">
24.
<h:outputText value="#{msg['form.employe.ville']}"/>
25.
<h:outputText value="#{msg['form.employe.codePostal']}"/>
26.
<h:outputText value="#{msg['form.employe.indice']}"/>

235/257

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.

<h:outputText value="#{form.feuilleSalaire.employe.ville}"/>
<h:outputText value="#{form.feuilleSalaire.employe.codePostal}"/>
<h:outputText value="#{form.feuilleSalaire.employe.indemnite.indice}"/>
</h:panelGrid>
<br/>
<h:outputText value="#{msg['form.infos.cotisations']}" styleClass="titreInfos"/>
<br/><br/>
<h:panelGrid columns="4" rowClasses="libelle,info">
<h:outputText value="#{msg['form.cotisations.csgrds']}"/>
<h:outputText value="#{msg['form.cotisations.csgd']}"/>
<h:outputText value="#{msg['form.cotisations.retraite']}"/>
<h:outputText value="#{msg['form.cotisations.secu']}"/>
<h:outputText value="#{form.feuilleSalaire.cotisation.csgrds} %"/>
<h:outputText value="#{form.feuilleSalaire.cotisation.csgd} %"/>
<h:outputText value="#{form.feuilleSalaire.cotisation.retraite} %"/>
<h:outputText value="#{form.feuilleSalaire.cotisation.secu} %"/>
</h:panelGrid>
<br/>
<h:outputText value="#{msg['form.infos.indemnites']}" styleClass="titreInfos"/>
<br/><br/>
<h:panelGrid columns="4" rowClasses="libelle,info">
<h:outputText value="#{msg['form.indemnites.salaireHoraire']}"/>
<h:outputText value="#{msg['form.indemnites.entretienJour']}"/>
<h:outputText value="#{msg['form.indemnites.repasJour']}"/>
<h:outputText value="#{msg['form.indemnites.congsPays']}"/>
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.employe.indemnite.baseHeure}"/>
</h:outputFormat>
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.employe.indemnite.entretienJour}"/>
</h:outputFormat>
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.employe.indemnite.repasJour}"/>
</h:outputFormat>
<h:outputText value="#{form.feuilleSalaire.employe.indemnite.indemnitesCP} %"/>
</h:panelGrid>
<br/>
<h:outputText value="#{msg['form.infos.salaire']}" styleClass="titreInfos"/>
<br/><br/>
<h:panelGrid columns="4" rowClasses="libelle,info">
<h:outputText value="#{msg['form.salaire.base']}"/>
<h:outputText value="#{msg['form.salaire.cotisationsSociales']}"/>
<h:outputText value="#{msg['form.salaire.entretien']}"/>
<h:outputText value="#{msg['form.salaire.repas']}"/>
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.elementsSalaire.salaireBase}"/>
</h:outputFormat>
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.elementsSalaire.cotisationsSociales}"/>
</h:outputFormat>
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.elementsSalaire.indemnitesEntretien}"/>
</h:outputFormat>
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.elementsSalaire.indemnitesRepas}"/>
</h:outputFormat>
</h:panelGrid>
<br/>
<h:panelGrid columns="3" columnClasses="libelle,col2,info">
<h:outputText value="#{msg['form.salaire.net']}"/>
<h:panelGroup></h:panelGroup>
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.elementsSalaire.salaireNet}"/>

236/257

90.
</h:outputFormat>
91.
</h:panelGrid>
92.
</ui:define>
93. </ui:composition>
94. </html>

ligne 8, la page [simulation.xhtml] s'insre dans la page [layout.xhtml],


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.

Question : 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 affiche la simulation
7.
...
8.
// on met jour le menu
9.
...
10.
// on rend la vue simulation
11.
return "simulation";
12.
}catch(Throwable th){
13.
// on vide la liste des erreurs prcdentes
14.
...
15.
// on cre la nouvelle liste des erreurs
16.
...
17.
// on affiche la vue vueErreur
18.
...
19.
// on met jour le menu
20.
...
21.
// on affiche la vue erreur
22.
return "erreurs";
23.
}
24.}

La liste des erreurs cre ligne 15 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. package web.entities;
2.
3. public class Erreur {
4.
5.
public Erreur() {
6.
}
7.
8.
// champ
9.
private String classe;
10. private String message;

237/257

11.
12. // constructeur
13. public Erreur(String classe, String message){
14.
this.setClasse(classe);
15.
this.message=message;
16. }
17.
18. // getters et setters
19. ...
20. }

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 th.getCause() si elle en a une,

de la cause de la cause h.getCause().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=hashEmployes.get(SS);
4.
// on rend une exception si l'employ n'existe pas
5.
if(e==null){
6.
throw new PamException(String.format("L'employ de n SS [%s] n'existe pas",SS),1);
7.
}
8....
9.}

Le rsultat obtenu est le suivant :

Cette vue est affiche par la page [erreurs.xhtml] suivante :

238/257

1. <?xml version='1.0' encoding='UTF-8' ?>


2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3. <html xmlns="http://www.w3.org/1999/xhtml"
4.
xmlns:h="http://java.sun.com/JSF/html"
5.
xmlns:f="http://java.sun.com/JSF/core"
6.
xmlns:ui="http://java.sun.com/JSF/facelets">
7.
8.
<ui:composition template="layout.xhtml">
9.
<ui:define name="part1">
10.
<h3><h:outputText value="#{msg['erreur.titre']}"/></h3>
11.
<h:dataTable value="#{form.erreurs}" var="erreur"
12.
headerClass="erreursHeaders" columnClasses="erreurClasse,erreurMessage">
13.
<f:facet name="header">
14.
<h:outputText value="#{msg['erreur.exceptions']}"/>
15.
</f:facet>
16.
<h:column>
17.
<f:facet name="header">
18.
<h:outputText value="#{msg['exception.type']}"/>
19.
</f:facet>
20.
<h:outputText value="#{erreur.classe}"/>
21.
</h:column>
22.
<h:column>
23.
<f:facet name="header">
24.
<h:outputText value="#{msg['exception.message']}"/>
25.
</f:facet>
26.
<h:outputText value="#{erreur.message}"/>
27.
</h:column>
28.
</h:dataTable>
29.
</ui:define>
30. </ui:composition>
31. </html>

Question : 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['form.menu.effacerSimulation']}" action="#{form.effacerSimulation}"
rendered="#{sessionData.menuEffacerSimulationIsRendered}"/>

Un clic sur le lien [EffacerSimulation] provoque d'abord l'appel de la fonction Javascript raz(). Cette mthode est dfinie dans la
page [layout.xhtml] :

239/257

1.
<script type="text/javascript">
2.
function raz(){
3.
// on change les valeurs postes
4.
document.forms['formulaire'].elements['formulaire:comboEmployes'].value="0";
5.
document.forms['formulaire'].elements['formulaire:heuresTravaillees'].value="0";
6.
document.forms['formulaire'].elements['formulaire:joursTravailles'].value="0";
7.
}
8. </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.
Question : 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['form.menu.enregistrerSimulation']}" action="#{form.enregistrerSimulation}"
rendered="#{sessionData.menuEnregistrerSimulationIsRendered}"/>

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 web.entities;
2.
3. import metier.FeuilleSalaire;
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;

240/257

15.
16.
17.

// constructeur
public Simulation(Integer num,String heuresTravailles, String joursTravaills,
FeuilleSalaire feuilleSalaire){
18.
this.setNum(num);
19.
this.setFeuilleSalaire(feuilleSalaire);
20.
this.setHeuresTravailles(heuresTravailles);
21.
this.setJoursTravaills(joursTravaills);
22. }
23.
24. public double getIndemnites(){
25.
return feuilleSalaire.getElementsSalaire().getIndemnitesEntretien()+
feuilleSalaire.getElementsSalaire().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.
// simulations
2.
private List<Simulation> simulations = new ArrayList<Simulation>();
3.
private int numDerniereSimulation = 0;
4. private Simulation simulation;

241/257

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 :

Le tableau des simulations est affich par la page [simulations.xhtml] :


1. <?xml version='1.0' encoding='UTF-8' ?>
2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3. <html xmlns="http://www.w3.org/1999/xhtml"
4.
xmlns:h="http://java.sun.com/JSF/html"
5.
xmlns:f="http://java.sun.com/JSF/core"
6.
xmlns:ui="http://java.sun.com/JSF/facelets">
7.
8.
<ui:composition template="layout.xhtml">
9.
<ui:define name="part1">
10.
<!-- tableau des simulations -->
11.
<h:dataTable value="#{sessionData.simulations}" var="simulation"
12.
headerClass="simulationsHeaders"
columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simu
CotisationsSociales,simuSalaireNet">
13.
<h:column>
14.
<f:facet name="header">
15.
<h:outputText value="#{msg['simulations.headers.numero']}"/>
16.
</f:facet>
17.
<h:outputText value="#{simulation.num}"/>
18.
</h:column>
19.
<h:column>
20.
<f:facet name="header">
21.
<h:outputText value="#{msg['simulations.headers.nom']}"/>
22.
</f:facet>
23.
<h:outputText value="#{simulation.feuilleSalaire.employe.nom}"/>
24.
</h:column>
25.
<h:column>
26.
<f:facet name="header">
27.
<h:outputText value="#{msg['simulations.headers.prenom']}"/>
28.
</f:facet>
29.
<h:outputText value="#{simulation.feuilleSalaire.employe.prenom}"/>
30.
</h:column>
31.
<h:column>
32.
<f:facet name="header">
33.
<h:outputText value="#{msg['simulations.headers.heuresTravaillees']}"/>
34.
</f:facet>
35.
<h:outputText value="#{simulation.heuresTravailles}"/>
36.
</h:column>
37.
<h:column>

242/257

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

<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.joursTravailles']}"/>
</f:facet>
<h:outputText value="#{simulation.joursTravaills}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.salaireBase']}"/>
</f:facet>
<h:outputText value="#{simulation.feuilleSalaire.elementsSalaire.salaireBase}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.indemnites']}"/>
</f:facet>
<h:outputText value="#{simulation.indemnites}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.cotisationsSociales']}"/>
</f:facet>
<h:outputText
value="#{simulation.feuilleSalaire.elementsSalaire.cotisationsSociales}"/>
60.
</h:column>
61.
<h:column>
62.
<f:facet name="header">
63.
<h:outputText value="#{msg['simulations.headers.salaireNet']}"/>
64.
</f:facet>
65.
<h:outputText value="#{simulation.feuilleSalaire.elementsSalaire.salaireNet}"/>
66.
</h:column>
67.
<h:column>
68.
<h:commandLink value="Retirer" action="#{form.retirerSimulation}">
69.
<f:setPropertyActionListener target="#{form.numSimulationToDelete}"
value="#{simulation.num}"/>
70.
</h:commandLink>
71.
</h:column>
72.
</h:dataTable>
73.
</ui:define>
74. </ui:composition>
75. </html>

ligne 8, la page [simulations.xhtml] s'insre l'intrieur de la page [layout.xhtml],


ligne 9, la place du fragment nomm part1,
ligne 11, la balise <h:dataTable> utilise le champ #{sessionData.simulations} 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 :

243/257

Le code JSF de la colonne Nom est le suivant :


1.
<h:column>
2.
<f:facet name="header">
3.
<h:outputText value="#{msg['simulations.headers.nom']}"/>
4.
</f:facet>
5.
<h:outputText value="#{simulation.feuilleSalaire.employe.nom}"/>
6. </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="#{sessionData.simulations}" var="simulation" ...>

simulation dsigne la simulation courante de la liste des simulations : d'abord la 1re, puis la 2me, ...
simulation.feuilleSalaire fait rfrence au champ feuilleSalaire de la simulation courante
simulation.feuilleSalaire.employe fait rfrence au champ employe du champ feuilleSalaire
simulation.feuilleSalaire.employe.nom 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.
<h:column>
2.
<f:facet name="header">
3.
<h:outputText value="#{msg['simulations.headers.indemnites']}"/>
4.
</f:facet>
5.
<h:outputText value="#{simulation.indemnites}"/>
6. </h:column>

Ligne 5, on affiche la valeur de simulation.indemnites. 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 simulation.getIndemnites(). 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 240.
Question : 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['form.menu.retourSimulateur']}" action="#{form.retourSimulateur}"
rendered="#{sessionData.menuRetourSimulateurIsRendered}"/>

L'action [retourSimulateur] associe au lien permet l'utilisateur de revenir de la vue [vueSimulations] la vue [vueSaisies] :

244/257

Le rsultat obtenu :

Question : 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['form.menu.voirSimulations']}" action="#{form.voirSimulations}"
rendered="#{sessionData.menuVoirSimulationsIsRendered}"/>

L'action [voirSimulations] associe au lien permet l'utilisateur d'avoir le tableau des simulations, ceci quelque soit l'tat de ses
saisies :

245/257

Le rsultat obtenu :

Question : 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 [simulationsVides.xhtml] suivante :


1. <?xml version='1.0' encoding='UTF-8' ?>
2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3. <html xmlns="http://www.w3.org/1999/xhtml"
4.
xmlns:h="http://java.sun.com/JSF/html"
5.
xmlns:f="http://java.sun.com/JSF/core"
6.
xmlns:ui="http://java.sun.com/JSF/facelets">
7.
8.
<ui:composition template="layout.xhtml">
9.
<ui:define name="part1">
10.
<h2>Votre liste de simulations est vide.</h2>
11.
</ui:define>
12. </ui:composition>
13. </html>

11.5.8

L'action [retirerSimulation]

L'utilisateur peut retirer des simulations de sa liste :

246/257

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="#{form.simulations}" var="simulation"


headerClass="simulationsHeaders"
columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simu
CotisationsSociales,simuSalaireNet">
...
<h:column>
<h:commandLink value="Retirer" action="#{form.retirerSimulation}">
<f:setPropertyActionListener target="#{form.numSimulationToDelete}"
value="#{simulation.num}"/>
</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 #{simulation.num} 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.
Question : crire la mthode [retirerSimulation] de la classe [Form].

11.5.9

L'action [terminerSession]

Le code JSF du lien [Terminer la session] est le suivant :

247/257

<h:commandLink id="cmdTerminerSession" immediate="true"


value="#{msg['form.menu.terminerSession']}" action="#{form.terminerSession}"
rendered="#{sessionData.menuTerminerSessionIsRendered}"/>

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.
Question : 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]
Form.java
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
162 :

248/257

Application web
couche [web]
1

Faces Servlet

2a
3

JSF1
JSF2
JSFn

2b

[MC]
Form.java
Modle M
Gestionnaires
d'vts

couche
[metier, DAO, JPA]

Travail pratique : raliser l'intgration des couches JSF et EJB en suivant la mthodologie du paragraphe 10, page 212.

249/257

12 Version 8 : Portage de l'application dans un environnement Spring /


Tomcat
Question : En suivant l'exemple " Application exemple 02 : rdvmedecins-jsf2-spring " de [ref3], portez l'application prcdente
dans un environnement Spring / Tomcat / Hibernate.

La nouvelle application web est [mv-pam-jsf2-spring-multipages]. L'interface web ne change pas :

Voici une dmarche possible :

crer un projet Maven de type [Java Application] nomm [mv-pam-spring-metier],


ajouter les dpendances ncessaires,
copier les lments du projet EJB prcdent dans le nouveau projet Spring,
corriger les erreurs qui apparaissent dans le projet Spring,
crer le fichier [persistence.xml] qui configure la couche JPA,
crer le fichier de configuration de Spring. A ce stade, le projet [mv-pam-spring-metier] doit tre correct. Imaginez un test
JUnit qui en apporte la preuve,
crer un projet Maven de type [Web Application],
ajouter les dpendances ncessaires (JSF et la couche [mtier]),
copier les lments du projet JSF / EJB de la version prcdente dans le nouveau projet JSF / Spring,
corriger les erreurs qui apparaissent,
copier le fichier de configuration de Spring du projet [mv-pam-spring-metier] dans le projet web,
ajouter au bean [ApplicationData] le code permettant d'instancier les beans Spring,
complter le fichier de configuration [faces-config.xml],
tester l'application web.

250/257

13 Version 9 : Implmentation de la couche web avec Primefaces


Pr-requis : on lira " Introduction Primefaces " dans [ref3].
Question 1 : 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> :

Une saisie errone :

Une simulation :

251/257

La liste des simulations utilise les composants <p:dataTable>, <p:commandLink> :

Les mthodes AJAX mettront jour la zone d'id='formulaire' qui inclut la totalit de la page (cf ligne 11 ci-dessous) :
1. <?xml version='1.0' encoding='UTF-8' ?>
2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3. <html xmlns="http://www.w3.org/1999/xhtml"
4.
xmlns:h="http://java.sun.com/JSF/html"
5.
xmlns:f="http://java.sun.com/JSF/core"
6.
xmlns:ui="http://java.sun.com/JSF/facelets">
7.
8.
<f:view locale="#{sessionData.locale}">
9.
...
10.
<h:body style="background-image: url('$
{request.contextPath}/resources/images/standard.jpg');">
11.
<h:form id="formulaire">
12.
<!-- entete -->
13.
<ui:include src="entete.xhtml" />

252/257

14.
<!-- contenu -->
15.
<ui:insert name="part1" >
16.
Gestion des assistantes maternelles
17.
</ui:insert>
18.
<ui:insert name="part2"/>
19.
</h:form>
20.
</h:body>
21. </f:view>
22. </html>

Une fois que votre application fonctionnera, remplacez la page [entete.xhtml] par la page suivante :
1. <?xml version='1.0' encoding='UTF-8' ?>
2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3. <html xmlns="http://www.w3.org/1999/xhtml"
4.
xmlns:h="http://java.sun.com/JSF/html"
5.
xmlns:p="http://primefaces.org/ui"
6.
xmlns:f="http://java.sun.com/JSF/core"
7.
xmlns:ui="http://java.sun.com/JSF/facelets">
8.
9.
10. <!-- entete -->
11. <h:panelGroup>
12.
<h2><h:outputText value="#{msg['form.titre']}"/></h2>
13. </h:panelGroup>
14.
15. <p:menubar id="menu" header="Menu" style="width: 500px">
16.
<p:submenu label="#{msg['form.menu.simulation']}">
17.
<p:menuitem id="cmdFaireSimulation" style="width: 200px"
value="#{msg['form.menu.faireSimulation']}" actionListener="#{form.faireSimulation}"
rendered="#{sessionData.menu.faireSimulation}" update=":formulaire:contenu"/>
18.
<p:menuitem id="cmdEffacerSimulation" style="width: 200px" onclick="raz();"
immediate="true" value="#{msg['form.menu.effacerSimulation']}"
actionListener="#{form.effacerSimulation}" rendered="#{sessionData.menu.effacerSimulation}"
update=":formulaire:contenu"/>
19.
<p:menuitem id="cmdEnregistrerSimulation" style="width: 200px" immediate="true"
value="#{msg['form.menu.enregistrerSimulation']}"
actionListener="#{form.enregistrerSimulation}"
rendered="#{sessionData.menu.enregistrerSimulation}" update=":formulaire:contenu"/>
20.
</p:submenu>
21.
<p:submenu label="Navigation">
22.
<p:menuitem id="cmdRetourSimulateur" style="width: 200px" immediate="true"
value="#{msg['form.menu.retourSimulateur']}" actionListener="#{form.retourSimulateur}"
rendered="#{sessionData.menu.retourSimulateur}" update=":formulaire:contenu"/>
23.
<p:menuitem id="cmdVoirSimulations" style="width: 200px" immediate="true"
value="#{msg['form.menu.voirSimulations']}" actionListener="#{form.voirSimulations}"
rendered="#{sessionData.menu.voirSimulations}" update=":formulaire:contenu"/>
24.
</p:submenu>
25.
<p:menuitem id="cmdTerminerSession" immediate="true"
value="#{msg['form.menu.terminerSession']}" actionListener="#{form.terminerSession}"
rendered="#{sessionData.menu.terminerSession}" update=":formulaire:contenu"/>
26. </p:menubar>
27.
28. <p:spacer height="50px"/>
29.
30. </html>

ligne 15 : une barre de menu Primefaces,


ligne 16 : une option de menu dans cette barre,
ligne 17 : un lment de cette option de menu.

Le modle n'est pas affect par ce changement. La nouvelle vue est la suivante :

253/257

254/257

Table des matires


1 ARCHITECTURE D'UNE APPLICATION JAVA EN COUCHES......................................................................................4
2 LES OUTILS DU DOCUMENT..............................................................................................................................................7
2.1 MAVEN.......................................................................................................................................................................................7
2.1.1 INTRODUCTION.........................................................................................................................................................................7
2.1.2 EXCUTION DU PROJET...........................................................................................................................................................11
2.1.3 LE SYSTME DE FICHIERS D'UN PROJET MAVEN........................................................................................................................12
2.1.4 LE DPT MAVEN LOCAL........................................................................................................................................................13
2.1.5 CHERCHER UN ARTIFACT AVEC MAVEN....................................................................................................................................14
3 JPA EN RSUM....................................................................................................................................................................19
3.1 LA PLACE DE JPA DANS UNE ARCHITECTURE EN COUCHES......................................................................................................19
3.2 JPA EXEMPLES.....................................................................................................................................................................19
3.2.1 EXEMPLE 1 - REPRSENTATION OBJET D'UNE TABLE UNIQUE.....................................................................................................19
3.2.1.1 La table [personne]...........................................................................................................................................................19
3.2.1.2 L'entit [Personne]............................................................................................................................................................20
3.2.2 CONFIGURATION DE LA COUCHE JPA.......................................................................................................................................23
3.2.3 EXEMPLE 2 : RELATION UN--PLUSIEURS.................................................................................................................................26
3.2.3.1 Le schma de la base de donnes......................................................................................................................................26
3.2.3.2 Les objets @Entity reprsentant la base de donnes........................................................................................................26
3.3 L'API DE LA COUCHE JPA......................................................................................................................................................29
3.4 LES REQUTES JPQL..............................................................................................................................................................31
3.4.1 LA TABLE [MEDECINS].......................................................................................................................................................32
3.4.2 LA TABLE [CLIENTS]...........................................................................................................................................................32
3.4.3 LA TABLE [CRENEAUX]......................................................................................................................................................33
3.4.4 LA TABLE [RV]......................................................................................................................................................................33
3.4.5 GNRATION DE LA BASE........................................................................................................................................................34
3.4.6 LA COUCHE [JPA]..................................................................................................................................................................35
3.4.7 LE PROJET NETBEANS.............................................................................................................................................................36
3.4.8 GNRATION DE LA COUCHE [JPA].........................................................................................................................................36
3.4.9 CRATION D'UNE CONNEXION NETBEANS LA BASE DE DONNES............................................................................................36
3.4.10 CRATION D'UNE UNIT DE PERSISTANCE...............................................................................................................................37
3.4.11 GNRATION DES ENTITS JPA.............................................................................................................................................42
3.4.12 LES ENTITS JPA GNRES.................................................................................................................................................43
3.4.13 LE CODE D'ACCS AUX DONNES...........................................................................................................................................52
3.5 LIENS ENTRE CONTEXTE DE PERSISTANCE ET SGBD...............................................................................................................54
3.5.1 LA CLASSE PERSONNE............................................................................................................................................................54
3.5.2 LE PROGRAMME DE TEST........................................................................................................................................................55
3.5.3 LA CONFIGURATION D'HIBERNATE DANS [PERSISTENCE.XML]....................................................................................................57
3.5.4 LES RSULTATS.......................................................................................................................................................................58
4 VERSION 1 : ARCHITECTURE SPRING / JPA.................................................................................................................60
4.1 LA BASE DE DONNES...............................................................................................................................................................60
4.2 MODE DE CALCUL DU SALAIRE D'UNE ASSISTANTE MATERNELLE.............................................................................................61
4.3 FONCTIONNEMENT DE L'APPLICATION CONSOLE.....................................................................................................................62
4.4 FONCTIONNEMENT DE L'APPLICATION GRAPHIQUE.................................................................................................................63
4.5 CRATION DE LA BASE DE DONNES.........................................................................................................................................64
4.6 IMPLMENTATION JPA............................................................................................................................................................66
4.6.1 COUCHE JPA / HIBERNATE.....................................................................................................................................................66
4.6.1.1 La base de donnes...........................................................................................................................................................67
4.6.1.2 Configuration de la couche JPA........................................................................................................................................67
4.6.1.3 Les dpendances...............................................................................................................................................................70
4.6.1.4 Les entits JPA..................................................................................................................................................................71
4.6.1.5 Le code de la classe principale.........................................................................................................................................73
4.6.1.6 Tests..................................................................................................................................................................................74
4.6.2 COUCHE JPA / ECLIPSELINK...................................................................................................................................................77
4.6.3 TRAVAIL FAIRE.....................................................................................................................................................................80
4.6.4 LAZY OU EAGER ?..................................................................................................................................................................80
4.6.5 TRAVAIL FAIRE.....................................................................................................................................................................85
4.6.6 POUR LA SUITE.......................................................................................................................................................................86
4.7 LES INTERFACES DES COUCHES [METIER] ET [DAO]...............................................................................................................88
4.8 LA CLASSE [PAMEXCEPTION]..................................................................................................................................................94
4.9 LA COUCHE [DAO] DE L'APPLICATION [PAM].......................................................................................................................96
4.9.1 IMPLMENTATION...................................................................................................................................................................96

255/257

4.9.2 CONFIGURATION.....................................................................................................................................................................97
4.9.3 TESTS....................................................................................................................................................................................98
4.9.4 INITDB..................................................................................................................................................................................98
4.9.5 MISE EN OEUVRE DES TESTS...................................................................................................................................................99
4.9.6 JUNITDAO...........................................................................................................................................................................101
4.10 LA COUCHE [METIER] DE L'APPLICATION [PAM]...............................................................................................................105
4.10.1 L'INTERFACE JAVA [IMETIER]..............................................................................................................................................105
4.10.2 LA CLASSE [FEUILLESALAIRE]............................................................................................................................................106
4.10.3 LA CLASSE D'IMPLMENTATION [METIER] DE LA COUCHE [METIER].......................................................................................108
4.10.4 TESTS DE LA COUCHE [METIER]...........................................................................................................................................108
4.11 LA COUCHE [UI] DE L'APPLICATION [PAM] VERSION CONSOLE........................................................................................111
4.11.1 LA CLASSE [UI.CONSOLE.MAIN]..........................................................................................................................................111
4.11.2 EXCUTION........................................................................................................................................................................113
4.12 LA COUCHE [UI] DE L'APPLICATION [PAM] VERSION GRAPHIQUE....................................................................................115
4.12.1 UN RAPIDE TUTORIEL..........................................................................................................................................................116
4.12.2 L'INTERFACE GRAPHIQUE [PAMJFRAME]..............................................................................................................................118
4.12.3 LES VNEMENTS DE L'INTERFACE GRAPHIQUE.....................................................................................................................119
4.12.4 INITIALISATION DE L'INTERFACE GRAPHIQUE.........................................................................................................................121
4.12.5 GESTIONNAIRES D'VNEMENTS..........................................................................................................................................123
4.12.6 EXCUTION DE L'INTERFACE GRAPHIQUE..............................................................................................................................123
4.13 IMPLMENTATION DE LA COUCHE JPA AVEC ECLIPSELINK.................................................................................................123
4.13.1 LE PROJET NETBEANS........................................................................................................................................................123
4.13.2 MISE EN OEUVRE DES TESTS...............................................................................................................................................128
4.13.3 INITDB..............................................................................................................................................................................131
4.13.4 JUNITDAO.........................................................................................................................................................................131
4.13.5 LES AUTRES TESTS..............................................................................................................................................................134
4.13.6 TRAVAIL FAIRE.................................................................................................................................................................134
5 VERSION 2 : ARCHITECTURE OPENEJB / JPA...........................................................................................................135
5.1 INTRODUCTION AUX PRINCIPES DU PORTAGE.........................................................................................................................135
5.1.1 LES NOUVELLES ARCHITECTURES..........................................................................................................................................135
5.1.2 LES BIBLIOTHQUES DES PROJETS..........................................................................................................................................135
5.1.3 CONFIGURATION DE LA COUCHE JPA / ECLIPSELINK / OPENEJB............................................................................................136
5.1.4 IMPLMENTATION DE LA COUCHE [DAO] PAR DES EJB.........................................................................................................136
5.1.5 IMPLMENTATION DE LA COUCHE [METIER] PAR UN EJB........................................................................................................138
5.1.6 LES CLIENTS DES EJB..........................................................................................................................................................139
5.2 TRAVAIL PRATIQUE.................................................................................................................................................................140
5.2.1 MISE EN PLACE DE LA BASE DE DONNES [DBPAM_ECLIPSELINK]............................................................................................141
5.2.2 CONFIGURATION INITIALE DU PROJET NETBEANS....................................................................................................................141
5.2.3 PORTAGE DE LA COUCHE [DAO]..........................................................................................................................................144
5.2.3.1 L'EJB [CotisationDao]....................................................................................................................................................144
5.2.3.2 Les EJB [EmployeDao] et [IndemniteDao]....................................................................................................................145
5.2.3.3 La classe [PamException]...............................................................................................................................................145
5.2.3.4 Entits srialisables.........................................................................................................................................................146
5.2.3.5 Test de la couche [DAO].................................................................................................................................................146
5.2.4 PORTAGE DE LA COUCHE [METIER]........................................................................................................................................150
5.2.4.1 L'EJB [Metier]................................................................................................................................................................150
5.2.4.2 Test de la couche [metier]...............................................................................................................................................151
5.2.5 PORTAGE DE LA COUCHE [CONSOLE].....................................................................................................................................154
5.3 CONCLUSION..........................................................................................................................................................................157
6 VERSION 3 : PORTAGE DE L'APPLICATION PAM SUR UN SERVEUR D'APPLICATIONS GLASSFISH.......159
6.1 LA PARTIE SERVEUR DE L'APPLICATION CLIENT / SERVEUR PAM.........................................................................................159
6.1.1 L'ARCHITECTURE DE L'APPLICATION.......................................................................................................................................159
6.1.1.1 Le projet Netbeans..........................................................................................................................................................160
6.1.1.2 Configuration de la couche de persistance......................................................................................................................161
6.1.1.3 Insertion des couches [JPA, DAO, metier].....................................................................................................................164
6.1.1.4 Configuration du serveur Glassfish.................................................................................................................................164
6.1.1.5 Dploiement du module EJB..........................................................................................................................................165
6.2 CLIENT CONSOLE - VERSION 1...............................................................................................................................................166
6.3 CLIENT CONSOLE - VERSION 2...............................................................................................................................................170
6.4 LE CLIENT SWING..................................................................................................................................................................172
7 VERSION 4 CLIENT / SERVEUR DANS UNE ARCHITECTURE DE SERVICE WEB......................................... 174
7.1 SERVICE WEB IMPLMENT PAR UN EJB...............................................................................................................................175
7.1.1 LA PARTIE SERVEUR..............................................................................................................................................................175

256/257

7.1.2 LA PARTIE CLIENTE...............................................................................................................................................................178


7.1.2.1 Le projet Netbeans du client console..............................................................................................................................178
7.1.2.2 Le client console du service web Metier.........................................................................................................................180
7.1.3 LE CLIENT SWING DU SERVICE WEB METIER...........................................................................................................................182
7.2 SERVICE WEB IMPLMENT PAR UNE APPLICATION WEB........................................................................................................182
7.2.1 LA PARTIE SERVEUR..............................................................................................................................................................182
7.2.2 LA PARTIE CLIENTE...............................................................................................................................................................187
7.3 SERVICE WEB IMPLMENT AVEC SPRING ET TOMCAT..........................................................................................................187
7.3.1 LA PARTIE SERVEUR..............................................................................................................................................................188
7.3.2 LA PARTIE CLIENTE...............................................................................................................................................................192
8 INTRODUCTION JAVA SERVER FACES.....................................................................................................................194
9 VERSION 5 - APPLICATION PAM WEB / JSF................................................................................................................195
9.1 ARCHITECTURE DE L'APPLICATION........................................................................................................................................195
9.2 FONCTIONNEMENT DE L'APPLICATION...................................................................................................................................197
9.3 LE PROJET NETBEANS...........................................................................................................................................................198
9.3.1 LES FICHIERS DE CONFIGURATION..........................................................................................................................................200
9.3.2 LA FEUILLE DE STYLE...........................................................................................................................................................202
9.3.3 LE FICHIER DES MESSAGES....................................................................................................................................................202
9.3.4 LA PORTE DES BEANS..........................................................................................................................................................203
9.3.5 LA COUCHE [MTIER]...........................................................................................................................................................204
9.4 LE FORMULAIRE [INDEX.XHTML] ET SON MODLE [FORM.JAVA]...........................................................................................205
9.4.1 TAPE 1...............................................................................................................................................................................205
9.4.2 TAPE 2...............................................................................................................................................................................206
9.4.3 TAPE 3...............................................................................................................................................................................207
9.4.4 TAPE 4...............................................................................................................................................................................207
10 VERSION 6 - INTGRATION DE LA COUCHE WEB DANS UNE ARCHITECTURE 3 COUCHES JSF / EJB. 209
10.1 ARCHITECTURE DE L'APPLICATION......................................................................................................................................209
10.2 LE PROJET NETBEANS DE LA COUCHE WEB.........................................................................................................................209
10.3 LE PROJET NETBEANS DE L'APPLICATION D'ENTREPRISE....................................................................................................212
11 VERSION 7 - APPLICATION WEB PAM MULTI-VUES / MULTI-PAGES...............................................................216
11.1 LES VUES DE L'APPLICATION................................................................................................................................................217
11.2 LE PROJET NETBEANS DE LA COUCHE [WEB].......................................................................................................................218
11.2.1 LES FICHIERS DE CONFIGURATION........................................................................................................................................219
11.2.2 LA FEUILLE DE STYLE.........................................................................................................................................................219
11.2.3 LE FICHIER DES MESSAGES..................................................................................................................................................221
11.2.4 LA COUCHE [MTIER].........................................................................................................................................................222
11.3 LES BEANS DE L'APPLICATION..............................................................................................................................................223
11.3.1 LE BEAN APPLICATIONDATA................................................................................................................................................223
11.3.2 LE BEAN SESSIONDATA......................................................................................................................................................224
11.3.3 LE BEAN FORM..................................................................................................................................................................225
11.4 LES PAGES DE L'APPLICATION..............................................................................................................................................227
11.4.1 [LAYOUT.XHTML]................................................................................................................................................................227
11.4.2 L'ENTTE [ENTETE.XHTML].................................................................................................................................................228
11.5 LES CAS D'UTILISATION DE L'APPLICATION..........................................................................................................................229
11.5.1 AFFICHAGE DE LA PAGE D'ACCUEIL......................................................................................................................................229
11.5.2 L'ACTION [FAIRESIMULATION].............................................................................................................................................230
11.5.3 LA GESTION DES ERREURS...................................................................................................................................................233
11.5.4 L'ACTION [EFFACERSIMULATION].........................................................................................................................................235
11.5.5 L'ACTION [ENREGISTRERSIMULATION].................................................................................................................................236
11.5.6 L'ACTION [RETOURSIMULATEUR].........................................................................................................................................241
11.5.7 L'ACTION [VOIRSIMULATIONS]............................................................................................................................................241
11.5.8 L'ACTION [RETIRERSIMULATION].........................................................................................................................................243
11.5.9 L'ACTION [TERMINERSESSION]............................................................................................................................................244
11.6 INTGRATION DE LA COUCHE WEB DANS UNE ARCHITECTURE 3 COUCHES JSF / EJB.........................................................245
12 VERSION 8 : PORTAGE DE L'APPLICATION DANS UN ENVIRONNEMENT SPRING / TOMCAT................247
13 VERSION 9 : IMPLMENTATION DE LA COUCHE WEB AVEC PRIMEFACES.................................................248

257/257

Vous aimerez peut-être aussi