Introduction à Java EE avec Glassfish
Introduction à Java EE avec Glassfish
avec Netbeans
et le serveur d'applications Glassfish
[Link] at [Link]
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 [[Link] On y utilise des caractristiques
de la version 2 de JSF,
les projets sont des projets Maven.
Java EE signifie Java Enterprise Edition. J2EE (Java 2 Enterprise Edition) tait le terme prcdent. J2EE dsigne les
technologies Java utilises pour crer des applications d'entreprise avec le JDK 1.4 ou antrieur. En mme temps que le JDK 1.5
amenait de nombreuses nouveauts dans le langage Java, Sun introduisait de nouvelles technologies s'appuyant sur ce langage
amlior afin de remdier des lacunes de ces mmes technologies dans J2EE. Le terme Java EE 5 a alors t utilis pour dsigner
l'ensemble des technologies qui concourent crer une application d'entreprise avec la plate-forme Java. Au moment de la mise
jour de ce document, la dernire version de Java EE est Java EE 6.
sont d'excellents livres pour dcouvrir les technologies de Java EE 5 et Java EE 6. Toutes les technologies importantes de Java EE y
sont passes en revue dans le contexte d'tudes de cas ralistes. L'auteur a un site [[Link] que le lecteur
est invit visiter.
Le document prsent tudie certaines des technologies de Java EE 5. Nous y crons une application basique trois couches
[prsentation, mtier, accs aux donnes] dcline en plusieurs versions :
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 ([Link] / Hibernate ([Link] Afin de permettre au lecteur de comparer
les technologies EJB3 et Spring, l'application prcdente a des versions o Spring remplace les EJB3.
Ce document est un TD (Travail Dirig) utilis en 5ime anne de l'cole d'ingnieurs ISTIA de l'universit d'Angers
[[Link] Ce TD dcrit l'application construire, les technologies Java utiliser, les endroits o trouver de
l'information. La solution propose est souvent trs cadre. Le TD pose des questions dont il ne donne pas les rponses. C'est
l'tudiant de les trouver.
L'apprentissage Java EE propos ici ncessite un investissement du lecteur estim entre 50 et 100 heures. Le document contient
beaucoup de code rendant possible le copier / coller. Par ailleurs, tous les projets Netbeans sont dcrits dans le dtail. Globalement,
le document donne les squelettes des solutions et il est demand l'tudiant d'en donner certains dtails. Le document peut tre
utile mme quelqu'un ne pouvant ou ne voulant pas s'investir autant. On peut s'intresser uniquement aux architectures dcrites et
dlaisser la partie code qui fait l'objet des questions.
Pour dvelopper et excuter l'application, nous utilisons l'IDE Netbeans. Netbeans est un produit assez lourd : prvoir 1 Go de
Ram pour travailler confortablement. On peut le tlcharger l'url [[Link]
2/257
1. Persistance Java 5 par la pratique : [[Link] - donne les outils pour construire la couche
d'accs aux donnes avec JPA (Java Persistence API)
2. Introduction au langage Java [[Link] - pour les dbutants
3. Introduction par l'exemple Java Server Faces, Primefaces et Primefaces mobile
[[Link]
Ces supports de cours sont par la suite rfrencs [ref1] [ref2] et [ref3].
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 :
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 pr-
enregistres (fichiers, bases de donnes, ...) et qui enregistre certains des rsultats fournis par la couche [2].
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 :
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 :
4
Couche d'accs aux Objets image Interface Implmentation JPA Couche Base de
donnes [DAO] de la BD [JPA] [Hibernate / ...] [JDBC] Donnes
3 5 6 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.
4
Couche Couche Objets image Interface Implmentation JPA Couche
[metier] [DAO] de la BD [JPA] [EclipseLink [JDBC]
2 / Hibernate]
3 6
5
7 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
4
Couche Couche Objets image Interface Implmentation JPA Couche
[metier] [DAO] de la BD [JPA] [EclipseLink [JDBC]
2 / Hibernate]
3 6
5
7 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.
4
Couche Couche Couche Objets image Interface Implmentation JPA Couche
[web] [metier] [DAO] de la BD [JPA] [EclipseLink [JDBC]
1 2 / Hibernate]
3 6
5
7 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 Maven
2.1.1 Introduction
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.
1 2
7/257
4
3 5
Cette branche contient les codes source des classes Java du projet. Netbeans a gnr une classe par dfaut :
1. package [Link];
2.
3. /**
4. * Hello world!
5. *
6. */
7. public class App {
8. public static void main(String[] args) {
8/257
9. [Link]("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,
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
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([Link]);
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
6
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], le fichier [[Link]] de configuration du projet Maven. POM signifie Project Object Model. On sera amen
intervenir directement sur ce fichier.
1. <project xmlns="[Link]
xmlns:xsi="[Link]
2. xsi:schemaLocation="[Link] [Link]
[Link]">
3. <modelVersion>4.0.0</modelVersion>
4.
5. <groupId>[Link]</groupId>
6. <artifactId>mv-exemple</artifactId>
7. <version>1.0-SNAPSHOT</version>
8. <packaging>jar</packaging>
9.
10. <name>mv-exemple</name>
11. <url>[Link]
12.
10/257
13. <properties>
14. <[Link]>UTF-8</[Link]>
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 :
[groupId] : une information qui ressemble un nom de package. Ainsi les bibliothques du framework Spring ont
groupId=[Link], celles du framework JSF ont groupId=[Link],
[artifactId] : le nom de l'objet Maven. Dans le groupe [[Link]] on trouve ainsi les artifactId suivants : spring-
context, spring-core, spring-beans, ... Dans le groupe [[Link]], on trouve l'artifactId JSF-api,
[version] : n de version de l'artifact Maven. Ainsi l'artifact [Link]-core a les versions suivantes : 2.5.4,
2.5.5, 2.5.6, 2.5.6.SECO1, ...
[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 [[Link]] (ligne 5), nomm [mv-exemple] (ligne 6) et de version [1.0-
SNAPSHOT] (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.
11/257
1
En [1], le projet Maven est construit puis excut [1]. Les logs dans la console Netbeans sont les suivants :
Le rsultat est ligne 23. On voit que mme pour ce cas simple, Maven a tlcharg des lments (lignes 17 et 20).
12/257
2
4
3
5
Nous avons dit que Maven tlchargeait les dpendances ncessaires au projet et les stockait localement. On peut explorer ce dpt
local :
13/257
6
Apprenons maintenant chercher un artifact avec Maven. Partons de la liste des dpendances actuelles du fichier [[Link]] :
1. <project xmlns="[Link]
xmlns:xsi="[Link]
2. xsi:schemaLocation="[Link] [Link]
[Link]">
3. <modelVersion>4.0.0</modelVersion>
4.
5. <groupId>[Link]</groupId>
6. <artifactId>mv-exemple</artifactId>
7. <version>1.0-SNAPSHOT</version>
8. <packaging>jar</packaging>
9.
10. <name>mv-exemple</name>
11. <url>[Link]
12.
13. <properties>
14. <[Link]>UTF-8</[Link]>
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
1
Tout d'abord, nous supprimons les dpendances actuelles [1]. Le fichier [[Link]] est alors modifi :
1. <project xmlns="[Link]
xmlns:xsi="[Link]
2. xsi:schemaLocation="[Link] [Link]
[Link]">
3. <modelVersion>4.0.0</modelVersion>
4.
5. <groupId>[Link]</groupId>
6. <artifactId>mv-exemple</artifactId>
7. <version>1.0-SNAPSHOT</version>
8. <packaging>jar</packaging>
9.
10. <name>mv-exemple</name>
11. <url>[Link]
12.
13. <properties>
14. <[Link]>UTF-8</[Link]>
15. </properties>
16.
17. <dependencies></dependencies>
18. </project>
Ligne 17, la dpendance supprime n'apparat plus dans [[Link]]. Maintenant, recherchons-la dans les dpts Maven.
1 3
5
6
2
4
15/257
6
En [6], les dpendances ajoutes apparaissent dans le projet. Le fichier [[Link]] reflte ces changements :
1. <project xmlns="[Link]
xmlns:xsi="[Link]
2. xsi:schemaLocation="[Link] [Link]
[Link]">
3. <modelVersion>4.0.0</modelVersion>
4.
5. <groupId>[Link]</groupId>
6. <artifactId>mv-exemple</artifactId>
7. <version>1.0-SNAPSHOT</version>
8. <packaging>jar</packaging>
9.
10. <name>mv-exemple</name>
11. <url>[Link]
12.
13. <properties>
14. <[Link]>UTF-8</[Link]>
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 [[Link]] 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 [[Link] :
En [1], on peut taper des mots cls. Tapons hibernate et lanons la recherche.
16/257
3
1. <project xmlns="[Link]
xmlns:xsi="[Link]
2. xsi:schemaLocation="[Link]
[Link]
3. <modelVersion>4.0.0</modelVersion>
4.
5. <groupId>[Link]</groupId>
6. <artifactId>mv-exemple</artifactId>
7. <version>1.0-SNAPSHOT</version>
8. <packaging>jar</packaging>
9.
10. <name>mv-exemple</name>
11. <url>[Link]
12.
13. <properties>
14. <[Link]>UTF-8</[Link]>
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>[Link]</groupId>
27. <artifactId>hibernate-core</artifactId>
28. <version>[Link]</version>
17/257
29. </dependency>
30. </dependencies>
31. </project>
Nous sauvegardons le fichier [[Link]]. 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 [[Link]]. Ce fichier a t lu et Maven a dcouvert que l'[artifactId] avait des dpendances. Il les tlcharge
galement. Il fera cela pour chaque [artifactId] tlcharg. Au final, on trouve en [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 [[Link]]
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 [[Link]] utilis. Le lecteur n'aura qu' l'utiliser pour se
trouver dans les mmes conditions que le document. Par ailleurs les projets Maven sont reconnus par les principaux IDE Java
(Eclipse, Netbeans, IntelliJ, JDeveloper). Aussi le lecteur pourra-t-il utiliser son IDE favori pour tester les exemples.
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 : [[Link] - donne les outils pour construire la couche
d'accs aux donnes avec JPA
Nous prsentons tout d'abord les fondements de JPA. On attendra le paragraphe 3.4, page 31 pour crer une application
exemple.
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 Objets image Interface Implmentation JPA Couche Base de
donnes [DAO] de la BD [JPA] [Hibernate / ...] [JDBC] Donnes
5 6
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.
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
4
Programme de test Objets image Interface Implmentation Couche Base de
console [main] de la BD [JPA] 6 [Toplink [JDBC] Donnes
3 5 / Hibernate]
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
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 = [Link])
11. private Integer id;
12.
13. @Column(name = "VERSION", nullable = false)
14. @Version
15. private int version;
16.
17. @Column(name = "NOM", length = 30, nullable = false, unique = true)
18. private String nom;
19.
20. @Column(name = "PRENOM", length = 30, nullable = false)
21. private String prenom;
22.
23. @Column(name = "DATENAISSANCE", nullable = false)
24. @Temporal([Link])
25. private Date datenaissance;
26.
27. @Column(name = "MARIE", nullable = false)
28. private boolean marie;
29.
30. @Column(name = "NBENFANTS", nullable = false)
31. private int nbenfants;
32.
33. // constructeurs
34. public Personne() {
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.
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.
Oracle utilise un objet appel SEQUENCE qui l encore jouele rle d'un gnrateur de valeurs
La couche JPA doit gnrer des ordres SQL diffrents selon les SGBD pour crer le gnrateur de valeurs. On lui indique
par configuration le type de SGBD qu'elle a grer. Du coup, elle peut savoir quelle est la stratgie habituelle de
gnration de valeurs de cl primaire de ce SGBD. L'argument strategy = [Link] indique la couche
JPA qu'elle doit utiliser cette stratgie habituelle. 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 [Link] dsigne une date seule sans heure associe. Les autres types possibles sont [Link]
pour coder une heure et [Link] pour coder une date avec heure.
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
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.
Les tests de la couche JPA peuvent tre faits avec l'architecture suivante :
en [7] : la base de donnes qui sera gnre partir des annotations de l'entit [Personne] ainsi que de configurations
complmentaires faites dans un fichier appel [[Link]]
en [5, 6] : une couche JPA implmente par Hibernate
en [4] : l'entit [Personne]
en [3] : un programme de test de type console
1 2
Examinons la configuration de la couche JPA faite dans le fichier [[Link]] de notre projet :
23/257
12. <property name="use_sql_comments" value="true"/>
13. -->
14. <!-- connexion JDBC -->
15. <property name="[Link].driver_class" value="[Link]" />
16. <property name="[Link]" value="jdbc:mysql://localhost:3306/JPA" />
17. <property name="[Link]" value="JPA" />
18. <property name="[Link]" value="JPA" />
19. <!-- cration automatique du schma -->
20. <property name="[Link]" value="create" />
21. <!-- Dialecte -->
22. <property name="[Link]" value="[Link].MySQL5InnoDBDialect"
/>
23. <!-- proprits DataSource c3p0 -->
24. <property name="hibernate.c3p0.min_size" value="5" />
25. <property name="hibernate.c3p0.max_size" value="20" />
26. <property name="[Link]" value="300" />
27. <property name="hibernate.c3p0.max_statements" value="50" />
28. <property name="hibernate.c3p0.idle_test_period" value="3000" />
29. </properties>
30. </persistence-unit>
31. </persistence>
Pour comprendre cette configuration, il nous faut revenir sur l'architecture de l'accs aux donnes de notre application :
Maintenant voyons comment le fichier [[Link]] configure les couches [4, 5, 6] ci-dessus :
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 [[Link]] utilis ici recre une base neuve chaque nouvelle excution de l'application. Les tables sont
recres (create table) aprs avoir t dtruites (drop table) si elles existaient. On notera que ce n'est videmment pas faire avec une
base en production...
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;
1
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);
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).
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 = [Link])
11. private Long id;
12.
13. @SuppressWarnings("unused")
14. @Version
15. private int version;
16.
17. @Column(length = 30)
18. private String nom;
19.
20. // relation principale Article (many) -> Category (one)
26/257
21. // implmente par une cl trangre (categorie_id) dans Article
22. // 1 Article a ncessairement 1 Categorie (nullable=false)
23. @ManyToOne(fetch=[Link])
24. @JoinColumn(name = "categorie_id", nullable = false)
25. private Categorie categorie;
26.
27. // constructeurs
28. public Article() {
29. }
30.
31. // getters et setters
32. ...
33. // toString
34. public String toString() {
35. return [Link]("Article[%d,%d,%s,%d]", id, version, nom, [Link]());
36. }
37.
38. }
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 = [Link])
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 = { [Link] })
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 [Link]("Categorie[%d,%d,%s]", id, version, nom);
35. }
36.
37. // association bidirectionnelle Categorie <--> Article
38. public void addArticle(Article article) {
39. // l'article est ajout dans la collection des articles de la catgorie
40. [Link](article);
41. // l'article change de catgorie
42. [Link](this);
43. }
44. }
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].
28/257
Client JPA Interface [JPA] =
Base de
EntityManager
2 Donnes
1 4
Objets image de la BD =
3 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 :
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 :
[Link] est une classe statique permettant d'obtenir une fabrique (factory) d'objets EntityManager.
Cette fabrique est lie une unit de persistance prcise. On se rappelle que le fichier de configuration [META-
INF/[Link]] permet de dfinir des units de persistance et que celles-ci ont un nom :
Ci-dessus, l'unit de persistance s'appelle elections-dao-JPA-mysql-01PU. Avec elle, vient toute une configuration qui lui est
propre, notamment le SGBD avec lequel elle travaille. L'instruction [[Link]("elections-dao-
JPA-mysql-01PU")] 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 = [Link]();
Les mthodes suivantes de l'interface [EntityManager] permettent de grer le cycle de vie du contexte de persistance :
void close() le contexte de persistance est ferm. Force la synchronisation du contexte de persistance avec
la base de donnes :
si un objet du contexte n'est pas prsent dans la base, il y est mis par une opration
SQL INSERT)
si un objet du contexte est prsent dans la base et qu'il a t modifi depuis qu'il a t
lu, une opration SQL UPDATE est faite pour persister la modification
si un objet du contexte a t marqu comme " supprim " l'issue d'une opration
remove sur lui, une opration SQL DELETE est faite pour le supprimer de la base.
void clear() le contexte de persistance est vid de tous ses objets mais pas ferm.
void flush() le contexte de persistance est synchronis avec la base de 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] :
Rsumons. En mode [Link] qui est le mode par dfaut, le contexte de persistance sera synchronis avec la base
de donnes aux moments suivants :
1. avant chaque opration SELECT sur la base
2. la fin d'une transaction sur la base
3. la suite d'une opration flush ou close sur le contexte de persistance
En mode [Link], c'est la mme chose sauf pour l'opration 1 qui n'a pas lieu. Le mode normal d'interaction
avec la couche JPA est un mode transactionnel. Le client fait diverses oprations sur le contexte de persistance, l'intrieur d'une
transaction. Dans ce cas, les moments de synchronisation du contexte de persistance avec la base de donnes sont les cas 1 et 2 ci-
dessus en mode AUTO, et le cas 2 uniquement en mode COMMIT.
Terminons par l'API de l'interface Query, interface qui permet d'mettre des ordres JPQL sur le contexte de persistance ou bien
des ordres SQL directement sur la base pour y retrouver des donnes. L'interface Query est la suivante :
30/257
3
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. for (Object o : L) {
2. // exploiter objet o
3. }
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.
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 Couche
couche [JPA / [JDBC] SGBD BD
[DAO] Hibernate]
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.
Les clients des diffrents mdecins sont enregistrs dans la table [CLIENTS] :
32/257
NOM : le nom du client
PRENOM : son prnom
TITRE : son titre (Melle, Mme, Mr)
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).
33/257
Cette table a une contrainte d'unicit sur les valeurs des colonnes jointes (JOUR, ID_CRENEAU) :
Si une ligne de la table[RV] a la valeur (JOUR1, ID_CRENEAU1) pour les colonnes (JOUR, ID_CRENEAU), cette valeur ne peut
se retrouver nulle part ailleurs. Sinon, cela signifierait que deux RV ont t pris au 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.
Pour crer les tables et les remplir on pourra utiliser le script [[Link]] (support du cours). Avec [WampServer], on
pourra procder comme suit :
2
3
4 5 6
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
35/257
12
13
11
14
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.
Couche Couche
couche [JPA / [JDBC] SGBD BD
[DAO] Hibernate]
C'est le suivant :
36/257
3
Couche Couche
couche [JPA / [JDBC] SGBD BD
[DAO] Hibernate]
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.
37/257
5
2 1
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
en [10], la connexion est cre. On y voit les quatre tables de la base de donnes connecte.
38/257
Couche Couche
couche [JPA / [JDBC] SGBD BD
[DAO] Hibernate]
Nous sommes en train de construire la couche [JPA]. La configuration de celle-ci est faite dans un fichier [[Link]] dans
lequel on dfinit des units de persistance. Chacune d'elles a besoin des informations suivantes :
cliquer droit sur le projet et choisir la cration d'une unit de persistance [1],
en [2], crer une unit de persistance,
3
6
4
5 7
39/257
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.0" xmlns="[Link]
xmlns:xsi="[Link]
xsi:schemaLocation="[Link]
[Link]
3. <persistence-unit name="mv-rdvmedecins-jpql-hibernatePU" transaction-
type="RESOURCE_LOCAL">
4. <provider>[Link]</provider>
5. <properties>
6. <property name="[Link]"
value="jdbc:mysql://localhost:3306/dbrdvmedecins2"/>
7. <property name="[Link]" value=""/>
8. <property name="[Link]" value="[Link]"/>
9. <property name="[Link]" value="root"/>
10. <property name="[Link].provider_class"
value="[Link]"/>
11. </properties>
12. </persistence-unit>
13. </persistence>
Dans l'onglet [Design], on peut avoir une vue globale du fichier [[Link]] :
14
Pour avoir des logs d'Hibernate, nous compltons le fichier [[Link]] de la faon suivante :
40/257
8. <property name="[Link]" value="[Link]"/>
9. <property name="[Link]" value="root"/>
10. <property name="[Link].provider_class"
value="[Link]"/>
11. <property name="hibernate.show_sql" value="true"/>
12. <property name="hibernate.format_sql" value="true"/>
13. </properties>
14. </persistence-unit>
15. </persistence>
1. <project xmlns="[Link]
xmlns:xsi="[Link]
2. xsi:schemaLocation="[Link] [Link]
[Link]">
3. <modelVersion>4.0.0</modelVersion>
4.
5. <groupId>[Link]</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>[Link]
12.
13. <properties>
14. <[Link]>UTF-8</[Link]>
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>[Link]</groupId>
26. <artifactId>hibernate-entitymanager</artifactId>
27. <version>4.1.2</version>
28. </dependency>
29. <dependency>
30. <groupId>[Link]</groupId>
31. <artifactId>jboss-logging</artifactId>
32. <version>[Link]</version>
33. </dependency>
34. <dependency>
35. <groupId>[Link]</groupId>
36. <artifactId>jboss-transaction-api_1.1_spec</artifactId>
37. <version>[Link]</version>
38. </dependency>
39. <dependency>
40. <groupId>[Link]</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>[Link]</groupId>
56. <artifactId>hibernate-JPA-2.0-api</artifactId>
57. <version>[Link]</version>
58. </dependency>
59. <dependency>
60. <groupId>[Link]</groupId>
61. <artifactId>javassist</artifactId>
62. <version>3.15.0-GA</version>
63. </dependency>
64. <dependency>
65. <groupId>[Link]</groupId>
66. <artifactId>hibernate-commons-annotations</artifactId>
67. <version>[Link]</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 [[Link]] diffrents.
Ce fichier contient beaucoup de redondances. La version minimale suivante peut tre utilise dans la suite :
1. <project xmlns="[Link]
xmlns:xsi="[Link]
2. xsi:schemaLocation="[Link] [Link]
[Link]">
3. <modelVersion>4.0.0</modelVersion>
4.
5. <groupId>[Link]</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>[Link]
12.
13. <properties>
14. <[Link]>UTF-8</[Link]>
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>[Link]</groupId>
42/257
25. <artifactId>hibernate-entitymanager</artifactId>
26. <version>4.1.2</version>
27. </dependency>
28. </dependencies>
29. </project>
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
7
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 [Link];
2.
3. ...
4. @Entity
5. @Table(name = "medecins")
6. public class Medecin implements Serializable {
7.
8. @Id
9. @GeneratedValue(strategy = [Link])
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 = [Link], 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. @Override
40. public boolean equals(Object object) {
41. ...
42. }
43.
44. @Override
45. public String toString() {
46. ...
47. }
48.
49. }
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 [Link] 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].
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link].*;
6. import [Link];
7.
8. @Entity
9. @Table(name = "creneaux")
10. public class Creneau implements Serializable {
11. @Id
12. @GeneratedValue(strategy = [Link])
13. @Column(name = "ID")
14. private Long id;
15.
16. @Column(name = "MDEBUT")
17. private int mdebut;
18.
19. @Column(name = "HFIN")
20. private int hfin;
21.
22. @Column(name = "HDEBUT")
23. private int hdebut;
24.
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 = [Link], mappedBy = "idCreneau")
36. private List<Rv> rvList;
37.
38. // constructeurs
39. ...
40. // getters et setters
41. ...
42. @Override
43. public int hashCode() {
44. ...
45. }
46.
47. @Override
48. public boolean equals(Object object) {
49. ...
50. }
51.
52. @Override
53. public String toString() {
54. ...
55. }
56.
57. }
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 :
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=[Link] 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 [Link];
2.
3. ...
4. @Entity
5. @Table(name = "clients")
6. public class Client implements Serializable {
7. @Id
8. @GeneratedValue(strategy = [Link])
9. @Column(name = "ID")
10. private Long id;
11.
12. @Column(name = "TITRE")
13. private String titre;
14.
15. @Column(name = "NOM")
16. private String nom;
17.
18. @Column(name = "VERSION")
19. private int version;
20.
21. @Column(name = "PRENOM")
22. private String prenom;
23.
24. @OneToMany(cascade = [Link], mappedBy = "idClient")
25. private List<Rv> rvList;
26.
27. // constructeurs
28. ...
29. // getters et setters
30. ...
31.
32. @Override
33. public int hashCode() {
34. ...
35. }
36.
37. @Override
38. public boolean equals(Object object) {
39. ...
40. }
41.
42. @Override
43. public String toString() {
44. ...
45. }
46.
47. }
les lignes 24-25 refltent la relation de cl trangre entre la table [rv] et la table [clients].
47/257
L'entit [Rv] :
1. package [Link];
2.
3. ...
4. @Entity
5. @Table(name = "rv")
6. public class Rv implements Serializable {
7. @Id
8. @GeneratedValue(strategy = [Link])
9. @Column(name = "ID")
10. private Long id;
11.
12. @Column(name = "JOUR")
13. @Temporal([Link])
14. private Date jour;
15.
16. @JoinColumn(name = "ID_CRENEAU", referencedColumnName = "ID")
17. @ManyToOne(optional = false)
18. private Creneau idCreneau;
19.
20. @JoinColumn(name = "ID_CLIENT", referencedColumnName = "ID")
21. @ManyToOne(optional = false)
22. private Client idClient;
23.
24. // constructeurs
25. ...
26.
27. // getters et setters
28. ...
29.
30. @Override
31. public int hashCode() {
32. ...
33. }
34.
35. @Override
36. public boolean equals(Object object) {
37. ...
38. }
39.
40. @Override
41. public String toString() {
42. ...
43. }
44.
45. }
la ligne 13 qualifie le champ jour de type Java Date. On indique que dans la table [rv], la colonne [JOUR] (ligne 12) est de
type date (sans heure),
lignes 16-18 : qualifient la relation de cl 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.
La classe Personne est utilise pour reprsenter les mdecins et les clients :
1. package [Link];
2.
3. import [Link];
4. import [Link].*;
5.
6. @MappedSuperclass
7. public class Personne implements Serializable {
8. private static final long serialVersionUID = 1L;
9. @Id
10. @GeneratedValue(strategy = [Link])
11. @Column(name = "ID")
12. private Long id;
13.
14. @Basic(optional = false)
15. @Column(name = "TITRE")
16. private String titre;
17.
18. @Basic(optional = false)
19. @Column(name = "NOM")
20. private String nom;
21.
22. @Basic(optional = false)
23. @Column(name = "VERSION")
24. @Version
25. private int version;
26.
27. @Basic(optional = false)
28. @Column(name = "PRENOM")
29. private String prenom;
30.
31. // constructeurs
32.
33. public Personne() {
34. }
35.
36. public Personne(Long id) {
37. [Link] = id;
38. }
39.
40. public Personne(Long id, String titre, String nom, int version, String prenom) {
41. [Link] = id;
42. [Link] = titre;
43. [Link] = nom;
44. [Link] = version;
45. [Link] = prenom;
46. }
47.
48. // getters et setters
49. ...
50.
51. @Override
52. public String toString() {
53. return [Link]("[%s,%s,%s,%s,%s]", id, version, titre, prenom, nom);
54. }
55.
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 [Link];
2.
3. import [Link];
4. import [Link].*;
5.
6. @Entity
7. @Table(name = "clients")
8. public class Client extends Personne implements Serializable {
9. private static final long serialVersionUID = 1L;
10.
11. // constructeurs
12. public Client() {
13. super();
14. }
15.
16. public Client(Long id) {
17. super(id);
18. }
19.
20. public Client(Long id, String titre, String nom, int version, String prenom) {
21. super(id, titre, nom, version, prenom);
22. }
23.
24. @Override
25. public int hashCode() {
26. ...
27. }
28.
29. @Override
30. public boolean equals(Object object) {
31. ...
32. }
33.
34. @Override
35. public String toString() {
36. return [Link]("Client[%s,%s,%s,%s]", getId(), getTitre(), getPrenom(),
getNom());
37. }
38.
39. }
L'entit [Medecin] qui encapsule les lignes de la table [medecins] suit le mme modle :
1. package [Link];
2.
3. import [Link];
4. import [Link].*;
5.
6. @Entity
7. @Table(name = "medecins")
8. public class Medecin extends Personne implements Serializable {
9. private static final long serialVersionUID = 1L;
50/257
10.
11. // constructeurs
12. public Medecin() {
13. super();
14. }
15.
16. public Medecin(Long id) {
17. super(id);
18. }
19.
20. public Medecin(Long id, String titre, String nom, int version, String prenom) {
21. super(id, titre, nom, version, prenom);
22. }
23.
24. @Override
25. public int hashCode() {
26. ...
27. }
28.
29. @Override
30. public boolean equals(Object object) {
31. ...
32. }
33.
34. @Override
35. public String toString() {
36. return [Link]("Mdecin[%s,%s,%s,%s]", getId(), getTitre(), getPrenom(),
getNom());
37. }
38.
39. }
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link].*;
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 = [Link])
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. @Basic(optional = false)
32. @Column(name = "MFIN")
33. private int mfin;
34.
35. @Basic(optional = false)
36. @Column(name = "VERSION")
37. @Version
38. private int version;
39.
40. @JoinColumn(name = "ID_MEDECIN", referencedColumnName = "ID")
41. @ManyToOne(optional = false)
42. private Medecin medecin;
43.
44. // constructeurs
45. ...
46.
47. // getters et setters
48. ...
49.
50. @Override
51. public int hashCode() {
52. ...
53. }
54.
55. @Override
56. public boolean equals(Object object) {
57. // TODO: Warning - this method won't work in the case the id fields are not set
58. ...
59. }
60.
61. @Override
62. public String toString() {
63. return [Link]("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.
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link].*;
6.
7. @Entity
8. @Table(name = "rv")
9. public class Rv implements Serializable {
10.
11. private static final long serialVersionUID = 1L;
12. @Id
13. @GeneratedValue(strategy = [Link])
14. @Basic(optional = false)
15. @Column(name = "ID")
16. private Long id;
17.
18. @Basic(optional = false)
19. @Column(name = "JOUR")
20. @Temporal([Link])
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 [Link]("Rv[%s, %s, %s]", id, creneau, client);
50. }
51. }
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).
Nous allons ajouter maintenant au projet, le code d'accs aux donnes via la couche JPA :
Couche Couche
couche [JPA / [JDBC] SGBD BD
[console] Hibernate]
53/257
La classe [MainJpql] est la suivante :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7.
8. public class MainJpql {
9.
10. public static void main(String[] args) {
11. // EntityManagerFactory
12. EntityManagerFactory emf = [Link]("mv-rdvmedecins-jpql-
hibernatePU");
13. // entityManager
14. EntityManager em = [Link]();
15. // scanner clavier
16. Scanner clavier = new Scanner([Link]);
17. // boucle de saisie des requtes JPQL
18. [Link]("Requete JPQL sur la base dbrdvmedecins2 (* pour arrter) :");
19. String requete = [Link]();
20. while (![Link]().equals("*")) {
21. try {
22. // affichage rsultat requte
23. for (Object o : [Link](requete).getResultList()) {
24. [Link](o);
25. }
26. } catch (Exception e) {
27. [Link]("L'exception suivante s'est produite : " + e);
28. }
29. // on vide le contexte de persistance
30. [Link]();
31. // nouvelle requte
32. [Link]("---------------------------------------------");
33. [Link]("Requete JPQL sur la base dbrdvmedecins2 (* pour arrter) :");
34. requete = [Link]();
35. }
36. // fermeture des ressources
37. [Link]();
38. [Link]();
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 :
Question : donner les requtes JPQL permettant d'obtenir les informations suivantes :
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
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 = [Link])
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([Link])
26. private Date datenaissance;
27.
28. @Column(name = "MARIE", nullable = false)
55/257
29. private boolean marie;
30.
31. @Column(name = "NBENFANTS", nullable = false)
32. private int nbenfants;
33.
34. // constructeurs
35.
36. public Personne() {
37. }
38.
39. 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 [Link]("[%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. }
1. package tests;
2.
3. ....
4. import [Link];
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 = [Link]("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. // fermeture EntityManagerFactory
32. [Link]();
33. }
34.
35. // affichage contenu table
36. private static void dump() {
37. // contexte de persistance
38. EntityManager em = [Link]();
39. // dbut transaction
40. EntityTransaction tx = [Link]();
41. [Link]();
42. // affichage personnes
43. for (Object p : [Link]("select p from Personne p order by [Link]
asc").getResultList()) {
44. [Link](p);
45. }
46. // fin transaction
47. [Link]();
48. // fin contexte
49. [Link]();
50. }
51.
52. // raz BD
53. private static void clean() {
54. // contexte de persistance
55. EntityManager em = [Link]();
56. // dbut transaction
57. EntityTransaction tx = [Link]();
58. [Link]();
59. // supprimer les lments de la table PERSONNES
60. [Link]("delete from " + TABLE_NAME).executeUpdate();
61. // fin transaction
62. [Link]();
63. // fin contexte
64. [Link]();
65. }
66.
67. // logs
68. private static void log(String message) {
69. [Link]("main : ----------- " + message);
70. }
71.
72. // gestion d'objets persists
73. public static void test1() throws ParseException {
74. // contexte de persistance
75. EntityManager em = [Link]();
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 = [Link]();
81. [Link]("dbut transaction");
82. [Link]();
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. [Link]([Link]("Personne p1 %s non persiste", p1));
87. [Link]("[Link](p1)");
88. [Link](p1);
57/257
89. [Link]([Link]("Personne p1 %s persiste", p1));
90. // personne p2
91. // INSERT est gnr ds l'opration persist
92. [Link]([Link]("Personne p2 %s non persiste", p2));
93. [Link]("[Link](p2)");
94. [Link](p2);
95. [Link]([Link]("Personne p2 %s persiste", p2));
96. [Link](true);
97. [Link]([Link]("Personne p2 %s modifie", p2));
98. // l'opration DELETE lie l'opration remove n'est faite qu' la fin de la
transaction
99. [Link]("[Link](p2)");
100. [Link](p2);
101. [Link]([Link]("Personne p2 %s supprime", p2));
102. // modification p1
103. [Link]("P1");
104. // fin transaction
105. [Link]("fin transaction");
106. [Link]();
107. // fin contexte
108. [Link]();
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 = [Link]();
117. // dbut transaction
118. EntityTransaction tx = [Link]();
119. [Link]("dbut transaction");
120. [Link]();
121. // on modifie la personne p1 actuellement dtache
122. [Link]([Link]("Personne p1 %s actuelle non persiste", p1));
123. [Link](false);
124. [Link]([Link]("Personne p1 %s nouvelle non persiste", p1));
125. // on rattache la personne P1
126. [Link]("[Link](p1)");
127. Personne p1b = [Link](p1);
128. [Link]([Link]("Personne p1b %s attache", p1b));
129. // fin transaction
130. [Link]("fin transaction");
131. [Link]();
132. // fin contexte
133. [Link]();
134. // on affiche la table
135. dump();
136. }
137.}
58/257
10. ....
11. <!-- cration automatique du schma -->
12. <property name="[Link]" value="create" />
13. ....
14. </properties>
15. </persistence-unit>
16. </persistence>
1. init:
2. deps-jar:
3. Compiling 1 source file to C:\data\travail\2008-2009\netbeans\JPA\hibernate-personnes-
entites\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. [Link](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. [Link](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. [Link](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. [Link](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)
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 :
7 Spring
Table EMPLOYES : rassemble des informations sur les diffrentes assistantes maternelles
Structure :
ID cl primaire
VERSION n de version augmente chaque modification de la ligne
SS numro de scurit sociale de l'employ - unique
NOM nom de l'employ
PRENOM son prnom
ADRESSE son adresse
VILLE sa ville
CODEPOSTAL son code postal
INDEMNITE_ID cl trangre sur le champ [ID] de la table [INDEMNITES]
Table COTISATIONS : rassemble des pourcentages ncessaires au calcul des cotisations sociales
Structure :
ID cl primaire
VERSION n de version augmente chaque modification de la ligne
CSGRDS pourcentage : contribution sociale gnralise + contribution au remboursement de la dette sociale
CSGD pourcentage : contribution sociale gnralise dductible
SECU pourcentage : scurit sociale, veuvage, vieillesse
RETRAITE pourcentage : retraite complmentaire + assurance chmage
61/257
Les taux des cotisations sociales sont indpendants du salari. La table prcdente n'a qu'une ligne.
ID cl primaire
VERSION n de version augmente chaque modification de la ligne
INDICE indice de traitement - unique
BASEHEURE prix net en euro dune heure de garde
ENTRETIENJOUR indemnit dentretien en euro par jour de garde
REPASJOUR indemnit de repas en euro par jour de garde
INDEMNITESCP indemnit de congs pays. C'est un pourcentage appliquer au salaire de
base.
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).
Les lments suivants sont pris en compte [TOTALHEURES]: total des heures [TOTALHEURES]=150
: travailles dans le mois [TOTALJOURS]= 20
62/257
Les lments suivants sont pris en compte [TOTALHEURES]: total des heures [TOTALHEURES]=150
: travailles dans le mois [TOTALJOURS]= 20
63/257
1. n de scurit sociale de l'assistante maternelle ( 254104940426058 dans l'exemple - ligne 1)
2. nombre total d'heures travailles (150 dans l'exemple - ligne 1)
3. 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 graphique permet le calcul des salaires des assistantes maternelles au travers d'un formulaire Swing :
64/257
6
1
2 3
4
les informations passes en paramtres au programme console, sont maintenant saisies au moyen des champs de saisie [1,
2, 3].
le bouton [4] demande le calcul du salaire
le formulaire affiche les diffrents lments du salaire jusqu'au salaire net payer [5]
La liste droulante [1, 6] ne prsente pas les ns SS des employs mais les noms et prnoms de ceux-ci. On fait ici l'hypothse qu'il
n'y a pas deux employs de mmes nom et prnom.
65/257
3 4
5
6
7
8
10
66/257
table EMPLOYES
table INDEMNITES
table COTISATIONS
la base de donnes,
le pilote JDBC du SGBD MySQL,
67/257
la couche JPA / Hibernate (entits et configuration),
le programme console de test.
Crons tout d'abord la base de donnes vide. Nous lanons WampServer et utilisons l'outil PhpMyAdmin [1] :
3 4
La liaison entre la couche JDBC et la base de donnes se fait dans le fichier [[Link]] qui configure la couche JPA. Ce
fichier peut tre construit avec Netbeans :
68/257
1
5
2
4
7
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
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.
1
3
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] .
ligne 3 : le nom de l'unit de persistance et le type de transactions. RESOURCE_LOCAL indique que le projet gre lui-
mme 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 :
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 :
70/257
Cette option cre les tables si elles n'existent pas mais elle ne les dtruit pas si elles existent dj.
Elles demandent Hibernate d'afficher les ordres SQL qu'il envoie la base de donnes. Le fichier complet est donc le suivant :
Nous avons configur la couche JPA via le fichier [[Link]]. 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 [[Link]] de la faon suivante :
1. <project xmlns="[Link]
xmlns:xsi="[Link]
2. xsi:schemaLocation="[Link]
[Link]
3. <modelVersion>4.0.0</modelVersion>
4.
5. <groupId>[Link]</groupId>
6. <artifactId>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>[Link]
12.
13. <properties>
14. <[Link]>UTF-8</[Link]>
15. </properties>
16.
17. <dependencies>
18. <dependency>
19. <groupId>[Link]</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>
72/257
1
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. public Employe() {
16. }
17.
18. 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. }
Nous incluons dans le projet les entits JPA dveloppes prcdemment [1] :
74/257
1. package main;
2.
3. import [Link];
4. import [Link];
5. import [Link];
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 = [Link]("mv-pam-jpa-
hibernatePU");
12. EntityManager em=[Link]();
13. // libration ressources
14. [Link]();
15. [Link]();
16. }
17. }
ligne 12 : on cre l'EntityManager. Cette cration cre la couche JPA. Le fichier [[Link]] va tre exploit et donc les
tables de la base de donnes vont tre cres,
lignes 14-15 : on libre les ressources.
[Link] Tests
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 [Link] <clinit>
7. INFO: HCANN000001: Hibernate Commons Annotations {[Link]}
8. sept. 09, 2013 2:05:13 PM [Link] logVersion
9. INFO: HHH000412: Hibernate Core {4.1.2}
10. sept. 09, 2013 2:05:13 PM [Link] <clinit>
11. INFO: HHH000206: [Link] not found
12. sept. 09, 2013 2:05:13 PM [Link] buildBytecodeProvider
13. INFO: HHH000021: Bytecode provider name : javassist
14. sept. 09, 2013 2:05:13 PM
[Link]
configure
15. INFO: HHH000402: Using Hibernate built-in connection pool (not for production use!)
16. sept. 09, 2013 2:05:13 PM
[Link]
configure
17. INFO: HHH000115: Hibernate connection pool size: 20
18. sept. 09, 2013 2:05:13 PM
[Link]
configure
19. INFO: HHH000006: Autocommit mode: true
20. sept. 09, 2013 2:05:13 PM
[Link]
configure
21. INFO: HHH000401: using driver [[Link]] at URL
[jdbc:mysql://localhost:3306/dbpam_hibernate]
22. sept. 09, 2013 2:05:13 PM
[Link]
configure
23. INFO: HHH000046: Connection properties: {user=root, autocommit=true, release_mode=auto}
24. sept. 09, 2013 2:05:17 PM [Link] <init>
25. INFO: HHH000400: Using dialect: [Link]
26. sept. 09, 2013 2:05:17 PM [Link]
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
[Link] initiateService
29. INFO: HHH000268: Transaction strategy:
[Link]
30. sept. 09, 2013 2:05:18 PM [Link] <init>
31. INFO: HHH000397: Using ASTQueryTranslatorFactory
32. sept. 09, 2013 2:05:18 PM [Link] 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 [Link] perform
39. ERROR: HHH000389: Unsuccessful: alter table EMPLOYES drop foreign key FK75C8D6BC73F24A67
40. sept. 09, 2013 2:05:18 PM [Link] 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 [Link] execute
89. INFO: HHH000230: Schema export complete
90. sept. 09, 2013 2:05:20 PM
[Link] 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 :
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.
1. crer une base MySQL [dbpam_eclipselink]. On utilisera le script [dbpam_eclipselink.sql] pour la gnrer,
2. crer le fichier [[Link]] du projet. Prendre l'implmentation JPA 2.0 EclipseLink,
3. ajouter dans les dpendances gnres la dpendance du pilote JDBC de MySQL,
4. 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,
5. faire les tests.
78/257
13. <property name="[Link]" value="root"/>
14. <property name="[Link]" value="FINE"/>
15. <property name="[Link]-generation" value="drop-and-create-tables"/>
16. </properties>
17. </persistence-unit>
18. </persistence>
1. ------------------------------------------------------------------------
2. Building mv-pam-JPA-eclipselink 1.0-SNAPSHOT
3. ------------------------------------------------------------------------
4.
5. --- exec-maven-plugin:1.2.1:exec (default-cli) @ mv-pam-JPA-eclipselink ---
6. [EL Config]: 2013-09-09 14:10:15.181--ServerSession(303405156)--
Thread(Thread[main,5,main])--The access type for the persistent class [class
[Link]] 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 [Link]]
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 [Link].
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 [Link]]
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 [Link]] 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 [Link]] 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 [Link]] 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: $
{[Link]} )
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-JPA-
eclipselinkPU 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): [Link]
31. Internal Exception: [Link]: 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-JPA-
eclipselinkPU 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
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 :
1. crer et tester un projet [mv-pam-jpa-hibernate-oracle] utilisant une implmentation JPA Hibernate et un SGBD Oracle,
2. crer et tester un projet [mv-pam-jpa-hibernate-mssql] utilisant une implmentation JPA Hibernate et un SGBD SQL
server,
3. crer et tester un projet [mv-pam-jpa-eclipselink-oracle] utilisant une implmentation JPA EclipseLink et un SGBD
Oracle,
4. crer et tester un projet [mv-pam-jpa-eclipselink-mssql] utilisant une implmentation JPA EclipseLink et un SGBD SQL
server,
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 = [Link])
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= [Link])
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 :
[Link] : 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.
[Link] : lorsqu'un employ est cherch, l'indemnit qui lui correspond est ramene. C'est le mode par
dfaut lorsqu'aucun mode n'est prcis.
Pour comprendre l'intrt de l'option [Link], on peut prendre l'exemple suivant. Une liste d'employs sans les indemnits
est prsente dans une page web avec un lien [Details]. Un clic sur ce lien prsente alors les indemnits de l'employ slectionn. On
voit que :
pour afficher la premire page on n'a pas besoin des employs avec leurs indemnits. Le mode [Link] convient
alors,
pour afficher la seconde page avec les dtails, une requte supplmentaire doit tre faite la base de donnes pour avoir
les indemnits de l'employ slectionn.
Le mode [Link] vite de ramener trop de donnes dont l'application n'a pas besoin tout de suite. Voyons un exemple.
82/257
3
2 4
83/257
en [2], on renomme le projet et son artifactId,
en [3], le nouveau projet.
1. package main;
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8.
9. public class Main {
10.
11. // la requte JPQL ci-dessous ramne un employ
12. // la cl trangre [Employe].indemnite est en [Link]
13. public static void main(String[] args) {
14. // crer l'Entity Manager suffit construire la couche JPA
15. EntityManagerFactory emf = [Link]("pam-jpa-
hibernatePU");
16. // premier essai
17. EntityManager em = [Link]();
18. Employe employe = (Employe) [Link]("select e from Employe e where
[Link]=:nom").setParameter("nom", "Jouveinal").getSingleResult();
19. [Link]();
20. // on affiche l'employ
21. try {
22. [Link](employe);
23. } catch (Exception ex) {
24. [Link](ex);
25. }
26. // deuxime essai
27. em = [Link]();
28. employe = (Employe) [Link]("select e from Employe e left join fetch [Link]
where [Link]=:nom").setParameter("nom", "Jouveinal").getSingleResult();
29. // librer les ressources
30. [Link]();
31. // on affiche l'employ
32. try {
33. [Link](employe);
34. } catch (Exception ex) {
35. [Link](ex);
36. }
37. // libration ressources
38. [Link]();
39. }
40. }
1. package jpa;
2.
3. ...
4.
5. @Entity
6. @Table(name="EMPLOYES")
84/257
7. public class Employe implements Serializable {
8.
9. @Id
10. @GeneratedValue(strategy = [Link])
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= [Link])
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 "[Link][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. }
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 [[Link]] doit tre modifi :
85/257
1. <?xml version="1.0" encoding="UTF-8"?>
2. <persistence version="2.0" xmlns="[Link]
xmlns:xsi="[Link]
xsi:schemaLocation="[Link]
[Link]
3. <persistence-unit name="mv-pam-jpa-hibernatePU" transaction-type="RESOURCE_LOCAL">
4. <provider>[Link]</provider>
5. <class>[Link]</class>
6. <class>[Link]</class>
7. <class>[Link]</class>
8. <properties>
9. <property name="[Link]"
value="jdbc:mysql://localhost:3306/dbpam_hibernate"/>
10. <property name="[Link]" value=""/>
11. <property name="[Link]" value="[Link]"/>
12. <property name="[Link]" value="root"/>
13. <property name="[Link].provider_class"
value="[Link]"/>
14. </properties>
15. </persistence-unit>
16. </persistence>
on a enlev l'option qui 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.
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.
1. [Link][id=453,version=1,SS=254104940426058,nom=Jouveinal,prenom=Marie,adresse=5 rue
des oiseaux,ville=St Corentin,code postal=49203,indice=2]
2. [Link][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 [[Link]] (ligne 1) :
1. @ManyToOne(fetch= [Link])
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
7 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] :
6 4
1
1. <project xmlns="[Link]
xmlns:xsi="[Link]
2. xsi:schemaLocation="[Link] [Link]
[Link]">
3. <modelVersion>4.0.0</modelVersion>
4.
5. <groupId>[Link]</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. <url>[Link]
12. <repositories>
13. <repository>
14. <url>[Link]
15. <id>swing-layout</id>
16. <layout>default</layout>
17. <name>Repository for library Library[swing-layout]</name>
18. </repository>
19. </repositories>
20. <properties>
21. <[Link]>UTF-8</[Link]>
22. </properties>
23.
24. <dependencies>
25. <dependency>
26. <groupId>junit</groupId>
27. <artifactId>junit</artifactId>
28. <version>4.10</version>
29. <scope>test</scope>
30. <type>jar</type>
31. </dependency>
32. <dependency>
33. <groupId>commons-dbcp</groupId>
34. <artifactId>commons-dbcp</artifactId>
35. <version>1.2.2</version>
36. </dependency>
37. <dependency>
38. <groupId>commons-pool</groupId>
39. <artifactId>commons-pool</artifactId>
40. <version>1.6</version>
41. </dependency>
42. <dependency>
43. <groupId>[Link]</groupId>
44. <artifactId>spring-tx</artifactId>
45. <version>[Link]</version>
46. <type>jar</type>
47. </dependency>
48. <dependency>
49. <groupId>[Link]</groupId>
50. <artifactId>spring-beans</artifactId>
51. <version>[Link]</version>
52. <type>jar</type>
53. </dependency>
54. <dependency>
55. <groupId>[Link]</groupId>
56. <artifactId>spring-context</artifactId>
57. <version>[Link]</version>
58. <type>jar</type>
59. </dependency>
60. <dependency>
61. <groupId>[Link]</groupId>
62. <artifactId>spring-orm</artifactId>
63. <version>[Link]</version>
64. <type>jar</type>
65. </dependency>
66. <dependency>
67. <groupId>[Link]</groupId>
68. <artifactId>hibernate-entitymanager</artifactId>
69. <version>4.1.2</version>
70. <type>jar</type>
71. </dependency>
72. <dependency>
73. <groupId>mysql</groupId>
88/257
74. <artifactId>mysql-connector-java</artifactId>
75. <version>5.1.6</version>
76. </dependency>
77. <dependency>
78. <groupId>[Link]</groupId>
79. <artifactId>swing-layout</artifactId>
80. <version>1.0.3</version>
81. </dependency>
82. </dependencies>
83. </project>
7 Spring
Dans l'architecture ci-dessus, quelle interface doit offrir la couche [DAO] la couche [metier] et quelle interface doit offrir la
couche [metier] la couche [ui] ? Une 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.
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
A partir de ces information et d'autres enregistres dans des fichiers de configuration, l'application affiche les informations suivantes
:
Un certain nombre d'informations doivent tre fournies par la couche [metier] la couche [ui] :
1. 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.
2. 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.
3. 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.
4. 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 [Link];
4. import [Link];
5. import [Link];
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]
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. package metier;
2.
3. import [Link];
4. import [Link];
5.
6. public interface IMetier {
7. // obtenir la feuille de salaire
8. public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int
nbJoursTravaills );
9. // liste des employs
10. public List<Employe> findAllEmployes();
11. }
ligne [10] : la mthode qui va permettre la couche [ui] de demander la liste de tous les employs la couche [mtier].
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 [Link];
4. import [Link];
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
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 :
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 :
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 :
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]. Celle-
ci 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.
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] :
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
:
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] :
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 [Link];
4. import [Link];
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. }
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 [Link];
4. import [Link];
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 [Link];
4. import [Link];
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. public void destroy(Employe employe);
13. // chercher une entit Employe via son identifiant id
14. public Employe find(Long id);
15. // chercher une entit Employe via son n SS
16. public Employe find(String SS);
17. // obtenir toutes les entits Employe
18. public List<Employe> findAll();
19. }
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 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].
[JPA]Exception SQLException
PamException
Spring
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. [Link] = code;
12. }
13.
14. public PamException(String message, int code) {
15. super(message);
16. [Link] = code;
17. }
18.
19. public PamException(Throwable cause, int code) {
20. super(cause);
21. [Link] = code;
22. }
23.
24. public PamException(String message, Throwable cause, int code) {
25. super(message, cause);
26. [Link] = code;
27. }
28.
29. // getter et setter
30.
31. public int getCode() {
32. return code;
33. }
34.
35. public void setCode(int code) {
36. [Link] = 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.
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.
7 Spring
4.9.1 Implmentation
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.
1. package dao;
2.
3. ...
4. import [Link];
5.
6. @Transactional
7. public class CotisationDao implements ICotisationDao {
8.
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. [Link](cotisation);
20. return cotisation;
21. }catch (Throwable th){
22. throw new PamException(th,11);
23. }
24. }
25. ....
26.
27. }
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).
4.9.2 Configuration
L'intgration DAO / JPA est configure par le fichier Spring [[Link]] et le fichier JPA [[Link]] :
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.
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 :
7 Spring
4.9.4 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].
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("[Link]");
15. // couches DAO
16. employeDao = (IEmployeDao) [Link]("employeDao");
17. cotisationDao = (ICotisationDao) [Link]("cotisationDao");
18. indemniteDao = (IIndemniteDao) [Link]("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
[Link]. Ici il n'y en aura aucune. La mthode est donc un faux test. Elle a pour rle de remplir la base de
donnes avec quelques lignes puis d'afficher le contenu de la base sur la console. Ce sont les mthodes create et findAll des
couches [DAO] qui sont ici utilises.
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.
2 3
1
les classes [1], les fichiers de configuration [2] et les classes de test de la couche [DAO] [3] sont mis en place,
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 [Link] qui pourrait provoquer l'chec du test.
Nanmoins, cela montre qu'il n'y a pas eu d'exception l'excution du test.
La fentre [Output] contient les logs de l'excution, ceux de Spring et ceux du test lui-mme. Les affichages faits par la classe
[JUnitInitDB] sont les suivants :
Les tables [EMPLOYES, INDEMNITES, COTISATIONS] ont t remplies. On peut le vrifier avec une connexion Netbeans la
base [dbpam_hibernate].
1 3
en [1], dans l'onglet [services], on visualise les donnes de la table [employes] de la connexion [dbpam_hibernate] [2],
en [3] le rsultat.
4.9.6 JUnitDao
Note : la classe [JUnitDao] peut tre trouve dans le support de cours.
102/257
1. package dao;
2.
3. import [Link];
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("[Link]");
19. // couches DAO
20. employeDao = (IEmployeDao) [Link]("employeDao");
21. indemniteDao = (IIndemniteDao) [Link]("indemniteDao");
22. cotisationDao = (ICotisationDao) [Link]("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. [Link]("----------- " + message);
37. }
38.
39. // tests
40. @Test
41. public void test01() {
42. log("test01");
43. // liste des cotisations
44. List<Cotisation> cotisations = [Link]();
45. int nbCotisations = [Link]();
46. // on ajoute une cotisation
47. Cotisation cotisation = [Link](new Cotisation(3.49, 6.15, 9.39, 7.88));
48. // on la demande
49. cotisation = [Link]([Link]());
50. // vrification
51. [Link](cotisation);
52. [Link](3.49, [Link](), 1e-6);
53. [Link](6.15, [Link](), 1e-6);
54. [Link](9.39, [Link](), 1e-6);
55. [Link](7.88, [Link](), 1e-6);
56. // on la modifie
57. [Link](-1);
58. [Link](-1);
59. [Link](-1);
60. [Link](-1);
61. Cotisation cotisation2 = [Link](cotisation);
62. // vrifications
103/257
63. [Link]([Link]() + 1, [Link]());
64. [Link](-1, [Link](), 1e-6);
65. [Link](-1, [Link](), 1e-6);
66. [Link](-1, [Link](), 1e-6);
67. [Link](-1, [Link](), 1e-6);
68. // on demande l'lment modifi
69. Cotisation cotisation3 = [Link]([Link]());
70. // vrifications
71. [Link]([Link](), [Link]());
72. [Link](-1, [Link](), 1e-6);
73. [Link](-1, [Link](), 1e-6);
74. [Link](-1, [Link](), 1e-6);
75. [Link](-1, [Link](), 1e-6);
76. // on supprime l'lment
77. [Link](cotisation3);
78. // vrifications
79. Cotisation cotisation4 = [Link]([Link]());
80. [Link](cotisation4);
81. cotisations = [Link]();
82. [Link](nbCotisations, [Link]());
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. // puis avec un employ inexistant
126. ...
127. }
128.
129. @Test
130. public void test05(){
131. log("test05");
132. // on cre deux indemnits avec le mme indice
133. // enfreint la contrainte d'unicit de l'indice
134. // on vrifie qu'une exception de type PamException se produit
135. // et qu'elle a le n d'erreur attendu
136. ...
137. }
138.
139. @Test
140. public void test06(){
141. log("test06");
142. // on cre deux employs avec le mme n SS
143. // enfreint la contrainte d'unicit sur le n SS
144. // on vrifie qu'une exception de type PamException se produit
145. // et qu'elle a le n d'erreur attendu
146. ...
147.
148. }
149.
150. @Test
151. public void test07(){
152. log("test07");
153. // on cre deux employs avec le mme n SS, le 1er avec create, le 2me avec edit
154. // enfreint la contrainte d'unicit sur le n SS
155. // on vrifie qu'une exception de type PamException se produit
156. // et qu'elle a le n d'erreur attendu
157. ...
158. }
159.
160. @Test
161. public void test08(){
162. log("test08");
163. // supprimer un employ qui n'existe pas ne provoque pas d'exception
164. // il est ajout puis dtruit on le vrifie
165. ...
166. }
167.
168. @Test
169. public void test09(){
170. log("test09");
171. // modifier un employ sans avoir la bonne version doit provoquer une exception
172. // on le vrifie
173. ...
174. }
175.
176. @Test
177. public void test10(){
178. log("test10");
179. // supprimer un employ sans avoir la bonne version doit provoquer une exception
180. // on le vrifie
181. ...
182.
183. }
184.
185. // getters et setters
186. ...
187. }
105/257
Dans la classe de tests prcdente, la base est vide avant chaque test.
En procdant de la mme faon que pour la classe de tests [JUnitInitDB], on obtient les rsultats suivants :
Provoquons une erreur pour voir comment cela est signal dans la page des rsultats :
1. @Test
2. public void test01() {
3. log("test01");
4. // liste des cotisations
5. List<Cotisation> cotisations = [Link]();
6. int nbCotisations = [Link]();
7. // on ajoute une cotisation
8. Cotisation cotisation = [Link](new Cotisation(3.49, 6.15, 9.39, 7.88));
9. // on la demande
10. cotisation = [Link]([Link]());
11. // vrification
12. [Link](cotisation);
13. [Link](0, [Link](), 1e-6);
14. [Link](6.15, [Link](), 1e-6);
15. [Link](9.39, [Link](), 1e-6);
16. [Link](7.88, [Link](), 1e-6);
17. // on la modifie
18. ....
19. }
Ligne 13, l'assertion va provoquer une erreur, la valeur de Csgrds tant 3.49 (ligne 8). L'excution de la classe de tests donne les
rsultats suivants :
106/257
1
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.
Maintenant que la couche [DAO] a t crite, nous passons l'tude de la couche mtier [2] :
7 Spring
1. package metier;
2.
3. import [Link];
4. import [Link];
5.
6. public interface IMetier {
7. // obtenir la feuille de salaire
8. public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravailles, int
nbJoursTravaills );
9. // liste des employs
10. public List<Employe> findAllEmployes();
11. }
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.
1. package metier;
2.
3. import [Link];
4. import [Link];
5. import [Link];
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 [Link];
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. private double indemnitesRepas;
12. private double salaireNet;
13.
14. // constructeurs
15. public ElementsSalaire() {
16.
17. }
18.
19. public ElementsSalaire(double salaireBase, double cotisationsSociales,
20. double indemnitesEntretien, double indemnitesRepas,
21. double salaireNet) {
22. setSalaireBase(salaireBase);
23. setCotisationsSociales(cotisationsSociales);
24. setIndemnitesEntretien(indemnitesEntretien);
25. setIndemnitesRepas(indemnitesRepas);
26. setSalaireNet(salaireNet);
27. }
28.
29. // toString
30. @Override
31. public String toString() {
32. 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. }
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
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.
2
3
Les classes de tests [3] sont crs dans un paquetage [metier] [2] de la branche [Test Packages] [1] du projet.
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-metier-
[Link]");
17. metier = (IMetier) [Link]("metier");
18. // couches dao
19. IEmployeDao employeDao=(IEmployeDao) [Link]("employeDao");
20. ICotisationDao cotisationDao=(ICotisationDao) [Link]("cotisationDao");
21. IIndemniteDao indemniteDao=(IIndemniteDao) [Link]("indemniteDao");
22. // on vide la base
23. for(Employe employe:[Link]()){
24. [Link](employe);
25. }
26. for(Cotisation cotisation:[Link]()){
27. [Link](cotisation);
28. }
29. for(Indemnite indemnite : [Link]()){
30. [Link](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. [Link](indemnite1);
36. [Link](indemnite2);
37. [Link](new Employe("254104940426058","Jouveinal","Marie","5 rue des
oiseaux","St Corentin","49203",indemnite2));
38. [Link](new Employe("260124402111742","Laverti","Justine","La brlerie","St
Marcel","49014",indemnite1));
39. [Link](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. [Link]("----------- " + message);
49. }
50.
51. // test
52. @Test
53. public void test01(){
54. // calcul de feuilles de salaire
55. [Link]([Link]("260124402111742",30, 5));
56. [Link]([Link]("254104940426058",150, 20));
57. try {
58. [Link]([Link]("xx", 150, 20));
59. } catch (PamException ex) {
60. [Link]([Link]("PamException[Code=%d, message=%s]",[Link](),
[Link]()));
61. }
62. }
63. }
Il n'y a pas d'assertion [Link] dans la classe. On cherche simplement calculer quelques salaires afin de les
vrifier ensuite la main. L'affichage cran obtenu par l'excution de la classe prcdente est le suivant :
1. Testsuite: metier.JUnitMetier_1
2. ----------- init
3. ....
4. [[Link][id=22,version=0,SS=260124402111742,nom=Laverti,prenom=Justine,adresse=La
brlerie,ville=St Marcel,code
postal=49014,indice=1],[Link][id=6,version=0,csgrds=3.49,csgd=6.15,secu=9.39,retrai
te=7.88],[Link][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. [[Link][id=21,version=0,SS=254104940426058,nom=Jouveinal,prenom=Marie,adresse=5 rue
des oiseaux,ville=St Corentin,code
postal=49203,indice=2],[Link][id=6,version=0,csgrds=3.49,csgd=6.15,secu=9.39,retrai
te=7.88],[Link][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
Question : la ligne 17 de [JUnitMetier_1] utilise le bean Spring nomm metier. Donner la dfinition de ce bean dans le fichier
[[Link]]. Ce fichier est identique au fichier Spring utilis pour tester la couche [DAO]. On y ajoute
simplement la dfinition du bean de la couche [mtier].
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. [Link]("----------- " + 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.
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] :
7 Spring
Nous crerons deux implmentations diffrentes de la couche [ui] : une version console et une version graphique swing :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6.
7. import [Link];
8. import [Link];
9. import [Link];
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. [Link]("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. [Link]("Le nombre de jours travaills [" + args[2]
35. + "] est erron");
36. }
37. // des erreurs ?
38. if ([Link]() != 0) {
39. for (int i = 0; i < [Link](); i++) {
40. [Link]([Link](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. [Link]("L'erreur suivante s'est produite : "+ [Link]());
53. return;
54. } catch (Exception ex) {
55. [Link]("L'erreur suivante s'est produite : "+ [Link]());
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", [Link]().getNom());
66. output += ajouteInfo("Prnom", [Link]().getPrenom());
67. output += ajouteInfo("Adresse", [Link]().getAdresse());
68. output += ajouteInfo("Ville", [Link]().getVille());
69. output += ajouteInfo("Code Postal", [Link]().getCodePostal());
70. output += ajouteInfo("Indice", ""+
[Link]().getIndemnite().getIndice());
71. output += ajouteInfo("\nInformations Cotisations", "");
72. output += ajouteInfo("CSGRDS", ""+ [Link]().getCsgrds() + " %");
73. output += ajouteInfo("CSGD", ""+ [Link]().getCsgd() + " %");
74. output += ajouteInfo("Retraite", ""+ [Link]().getRetraite() + "
%");
75. output += ajouteInfo("Scurit sociale", ""+ [Link]().getSecu() +
" %");
76. output += ajouteInfo("\nInformations Indemnits", "");
77. output += ajouteInfo("Salaire horaire", ""+
[Link]().getIndemnite().getBaseHeure() + " euro");
78. output += ajouteInfo("Entretien/jour", ""+
[Link]().getIndemnite().getEntretienJour() + " euro");
79. output += ajouteInfo("Repas/jour", ""+
[Link]().getIndemnite().getRepasJour() + " euro");
80. output += ajouteInfo("Congs Pays", ""+
[Link]().getIndemnite().getIndemnitesCP()+ " %");
81. output += ajouteInfo("\nInformations Salaire", "");
82. output += ajouteInfo("Salaire de base", ""+
[Link]().getSalaireBase()+ " euro");
114/257
83. output += ajouteInfo("Cotisations sociales", ""+
[Link]().getCotisationsSociales()+ " euro");
84. output += ajouteInfo("Indemnits d'entretien", ""+
[Link]().getIndemnitesEntretien()+ " euro");
85. output += ajouteInfo("Indemnits de repas", ""+
[Link]().getIndemnitesRepas()+ " euro");
86. output += ajouteInfo("Salaire net", ""+
[Link]().getSalaireNet() + " euro");
87.
88. [Link](output);
89. }
90.
91. static String ajouteInfo(String message, String valeur) {
92. return message + " : " + valeur + "\n";
93. }
94. }
4.11.2 Excution
Pour excuter la classe [[Link]], on procdera de la faon suivante :
3
5
6
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 [[Link]] qui va tre
excute.
7 Spring
116/257
2
5
8
6
9
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
119/257
17
18
16
19
20 21
en [20] et [21] : dans la perspective [Source] du formulaire, du code Java a t ajout pour grer le JLabel ajout.
120/257
1
JLabel1
JPanel1
JPanel2
121/257
JPanel3
JPanel4
JPanel5
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 :
Le code Java qui associe la mthode prcdente au clic sur le bouton [JButtonSalaire] est lui aussi gnr :
1. [Link]("Salaire");
2. [Link](new [Link]() {
3. public void actionPerformed([Link] evt) {
4. jButtonSalaireActionPerformed(evt);
5. }
6. });
Ce sont les lignes 2-5 qui indiquent que le clic (evt de type ActionPerformed) sur le bouton [jButtonSalaire] (ligne 2) doit tre 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 code Java qui associe la mthode prcdente l'vnement [caretUpdate] sur le champ de saisie [jTextFieldHT] est lui aussi
gnr :
1. [Link](new [Link]() {
2. public void caretUpdate([Link] 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).
123/257
Revenons l'architecture de notre application :
7 Spring
La couche [ui] a besoin d'une rfrence sur la couche [metier]. Rappelons comment cette rfrence avait t obtenue dans
l'application console :
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 [Link];
2.
3. ...
4. public class PamJFrame extends [Link] {
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([Link] evt) {
22. ...
23. }
24.
25. private void jButtonSalaireActionPerformed([Link] evt) {
26. ...
27. }
28.
29. public static void main(String args[]) {
30. [Link](new Runnable() {
31. public void run() {
32. new PamJFrame().setVisible(true);
33. }
34. });
35. }
36.
37. // Variables declaration - do not modify
38. private [Link] jButtonSalaire;
39. ...
40. // End of variables declaration
124/257
41.
42. }
Pour ajouter au code prcdent nos propres initialisations, nous pouvons procder comme suit :
ligne 4 : on appelle une mthode propritaire pour faire nos propres initialisations. Celles-ci sont dfinies par le code des
lignes 10-42
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].
Le projet doit tre complet avec ses fichiers de configuration ([Link], [Link]) et la classe de
l'interface graphique. On lancera Le SGBD cible avant d'excuter le projet.
Nous nous intressons l'architecture suivante o la couche JPA est dsormais implmente par EclipseLink :
7 Spring
126/257
4
3
2
5
127/257
1
4
3
2
Le projet doit tre modifi en deux points pour l'adapter la nouvelle couche JPA / EclipseLink :
1. en [4], les fichiers de configuration de Spring doivent tre modifis. On y trouve en effet la configuration de la couche JPA.
2. 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 [[Link]] pour le nouveau projet sera celui-ci :
1. <project xmlns="[Link]
xmlns:xsi="[Link]
2. xsi:schemaLocation="[Link]
[Link]
3. <modelVersion>4.0.0</modelVersion>
4.
5. <groupId>[Link]</groupId>
6. <artifactId>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>[Link]
12. <repositories>
13. <repository>
14. <url>[Link]
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. <url>[Link]
21. <id>eclipselink</id>
22. <layout>default</layout>
23. <name>Repository for library Library[eclipselink]</name>
24. </repository>
25. </repositories>
26.
27. <properties>
28. <[Link]>UTF-8</[Link]>
29. </properties>
30.
31. <dependencies>
32. <dependency>
33. <groupId>junit</groupId>
34. <artifactId>junit</artifactId>
35. <version>4.10</version>
36. <scope>test</scope>
37. <type>jar</type>
38. </dependency>
39. <dependency>
40. <groupId>commons-dbcp</groupId>
41. <artifactId>commons-dbcp</artifactId>
42. <version>1.2.2</version>
43. </dependency>
44. <dependency>
45. <groupId>commons-pool</groupId>
46. <artifactId>commons-pool</artifactId>
47. <version>1.6</version>
48. </dependency>
49. <dependency>
50. <groupId>[Link]</groupId>
51. <artifactId>spring-tx</artifactId>
52. <version>[Link]</version>
53. <type>jar</type>
54. </dependency>
55. <dependency>
56. <groupId>[Link]</groupId>
57. <artifactId>spring-beans</artifactId>
58. <version>[Link]</version>
59. <type>jar</type>
60. </dependency>
61. <dependency>
62. <groupId>[Link]</groupId>
63. <artifactId>spring-context</artifactId>
64. <version>[Link]</version>
65. <type>jar</type>
66. </dependency>
67. <dependency>
68. <groupId>[Link]</groupId>
69. <artifactId>spring-orm</artifactId>
70. <version>[Link]</version>
71. <type>jar</type>
72. </dependency>
73. <dependency>
74. <groupId>[Link]</groupId>
75. <artifactId>eclipselink</artifactId>
76. <version>2.3.0</version>
77. </dependency>
78. <dependency>
79. <groupId>[Link]</groupId>
80. <artifactId>[Link]</artifactId>
81. <version>2.0.3</version>
82. </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>[Link]</groupId>
90. <artifactId>swing-layout</artifactId>
91. <version>1.0.3</version>
92. </dependency>
93. </dependencies>
94. </project>
Les fichiers de configuration de Spring doivent tre modifis pour indiquer que l'implmentation JPA a chang. Dans les deux
fichiers, seule la section configurant la couche JPA change. Par exemple dans [[Link]] on a :
130/257
38. <!-- la source de donnees DBCP -->
39. <bean id="dataSource" class="[Link]" destroy-
method="close">
40. <property name="driverClassName" value="[Link]" />
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 :
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 :
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 :
Caused by: [Link]: Error creating bean with name 'entityManagerFactory' defined in class
path resource [[Link]]: Invocation of init method failed; nested exception is [Link]: 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:\...\[Link]
[[Link]] est l'agent Java dont a besoin la JVM pour grer la configuration Spring / EclipseLink.
2
3
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 [[Link]] :
1. <build>
2. <plugins>
3. <plugin>
4. <groupId>[Link]</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/[Link]</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'.
4.13.3 InitDB
Maintenant, nous sommes prts pour tester de nouveau [InitDB]. Cette fois-ci les rsultats obtenus sont les suivants :
1 2 3
4.13.4 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.
1. package dao;
2.
3. ...
4. @Transactional(propagation=[Link])
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. [Link](indemnite);
18. }catch(Throwable th){
19. throw new PamException(th,31);
20. }
21. return indemnite;
22. }
23.
24. ...
25. }
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("[Link]");
18. // couches DAO
19. employeDao = (IEmployeDao) [Link]("employeDao");
20. indemniteDao = (IIndemniteDao) [Link]("indemniteDao");
21. cotisationDao = (ICotisationDao) [Link]("cotisationDao");
22. }
23.
24. @Before()
25. public void clean() {
26. // on vide la base
27. for (Employe employe : [Link]()) {
28. [Link](employe);
29. }
30. for (Cotisation cotisation : [Link]()) {
31. [Link](cotisation);
32. }
33. for (Indemnite indemnite : [Link]()) {
34. [Link](indemnite);
35. }
36. }
37.
38. // logs
39. private static void log(String message) {
40. [Link]("----------- " + message);
41. }
42.
43. // tests
44. .
45. @Test
46. public void test05() {
47. log("test05");
135/257
48. // on cre deux indemnits avec le mme indice
49. // enfreint la contrainte d'unicit de l'indice
50. boolean erreur = true;
51. Indemnite indemnite1 = null;
52. Indemnite indemnite2 = null;
53. Throwable th = null;
54. try {
55. indemnite1 = [Link](new Indemnite(1, 1.93, 2, 3, 12));
56. indemnite2 = [Link](new Indemnite(1, 1.93, 2, 3, 12));
57. erreur = false;
58. } catch (PamException ex) {
59. th = ex;
60. // vrifications
61. [Link](31, [Link]());
62. } catch (Throwable th1) {
63. th = th1;
64. }
65. // vrifications
66. [Link](erreur);
67. // chane des exceptions
68. [Link]("Chane des exceptions --------------------------------------");
69. [Link]([Link]().getName());
70. while ([Link]() != null) {
71. th = [Link]();
72. [Link]([Link]().getName());
73. }
74. // la 1re indemnit a du tre persiste
75. Indemnite indemnite = [Link]([Link]());
76. // vrification
77. [Link](indemnite);
78. [Link](1, [Link]());
79. [Link](1.93, [Link](), 1e-6);
80. [Link](2, [Link](), 1e-6);
81. [Link](3, [Link](), 1e-6);
82. [Link](12, [Link](), 1e-6);
83. // la seconde indemnit n'a pas du tre persiste
84. List<Indemnite> indemnites = [Link]();
85. int nbIndemnites = [Link]();
86. [Link](nbIndemnites, 1);
87. }
88.
89. ...
90. }
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 [Link] logExceptions
3. ATTENTION: SQL Error: 1062, SQLState: 23000
4. 4 juin 2010 16:45:43 [Link] logExceptions
5. GRAVE: Duplicate entry '1' for key 2
6. Chane des exceptions --------------------------------------
7. [Link]
8. [Link]
9. [Link]
10. [Link]
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.
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):
[Link]
3. Internal Exception: [Link]:
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([Link][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. [Link]
10. [Link]
11. [Link]
12. [Link]
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 : de ces deux exemples, que peut-on conclure de l'interchangeabilit des implmentations JPA ? Est-elle totale ici ?
Une fois la couche [DAO] teste et considre correcte, on pourra passer aux tests de la couche [metier] et ceux du projet lui-
mme 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.
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
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.
71 Spring
1 OpenEjb
Dans cette implmentation, la couche [ui] sera un client local de la couche [mtier].
1 OpenEjb
Dans cette implmentation, la couche [ui] sera un client distant de la couche [mtier].
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
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/[Link]] suivant :
1. <?xml version="1.0"?>
2. <openejb>
3. <Resource id="Default JDBC Database">
4. JdbcDriver [Link]
5. JdbcUrl jdbc:mysql://localhost:3306/dbpam_eclipselink
6. UserName root
7. </Resource>
8. </openejb>
ligne 3 : on utilise l'id Default JDBC Database lorsqu'on travaille avec un conteneur OpenEJB embarqu (embedded)
dans l'application elle-mme.
ligne 5 : nous utilisons une base MySQL [dbpam_eclipselink]
Les classes implmentant la couche [DAO] deviennent des EJB. Prenons l'exemple de la classe [CotisationDao] :
1. package dao;
2.
3. import [Link];
4. import [Link];
5.
6. public interface ICotisationDao {
7. // crer une nouvelle cotisation
8. Cotisation create(Cotisation cotisation);
9. // modifier une cotisation existante
139/257
10. Cotisation edit(Cotisation cotisation);
11. // supprimer une cotisation existante
12. void destroy(Cotisation cotisation);
13. // chercher une cotisation particulire
14. Cotisation find(Long id);
15. // obtenir tous les objets Cotisation
16. List<Cotisation> findAll();
17.
18. }
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. package dao;
2.
3. import [Link];
4.
5. @Local
6. public interface ICotisationDaoLocal extends ICotisationDao{
7. }
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.
L'interface distante :
1. package dao;
2.
3. import [Link];
4.
5. @Remote
6. public interface ICotisationDaoRemote extends ICotisationDao{
7. }
ligne 6 : l'interface [ICotisationDaoRemote] 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.
La couche [DAO] est implmente par un EJB implmentant les deux interfaces (ce n'est pas obligatoire) :
1. @Stateless()
2. @TransactionAttribute([Link])
3. public class CotisationDao implements ICotisationDaoLocal, ICotisationDaoRemote {
4.
5. @PersistenceContext
6. private EntityManager em;
Lorsque l'interface locale de la couche [DAO] est utilise, le client de cette interface s'excute dans la mme JVM.
140/257
Couche interface Couche mtier Couche d'accs aux Donnes
utilisateur
utilisateur [ui] [metier] donnes [DAO]
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.
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.
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. package metier;
2.
3. import [Link];
4. import [Link];
5.
6. public interface IMetier {
7. // obtenir la feuille de salaire
8. FeuilleSalaire calculerFeuilleSalaire(String SS, double 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. package metier;
2.
3. import [Link];
4.
5. @Local
6. public interface IMetierLocal extends IMetier{
7. }
1. package metier;
2.
3. import [Link];
4.
5. @Remote
6. public interface IMetierRemote extends IMetier{
141/257
7. }
1. @Stateless()
2. @TransactionAttribute([Link])
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.
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].
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 :
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] :
On se propose de porter l'application Netbeans Spring / Hibernate vers une architecture OpenEJB / EclipseLink.
143/257
Couche Couche Couche Objets image Interface Implmentation Couche
[ui] [metier] [DAO] de la BD [JPA] [Hibernate] [JDBC]
2
Spring
OpenEjb
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.
2
3
1
dans l'onglet [Files] [2], crer un dossier [conf] [3] sous la racine du projet
placer dans ce dossier, le fichier [[Link]] [4-8] suivant :
1. <?xml version="1.0"?>
2. <openejb>
3. <Resource id="Default JDBC Database">
4. JdbcDriver [Link]
5. JdbcUrl jdbc:mysql://localhost:3306/dbpam_eclipselink
6. UserName root
7. </Resource>
8. </openejb>
144/257
5 6
9
8
10
1. <project xmlns="[Link]
xmlns:xsi="[Link]
145/257
2. xsi:schemaLocation="[Link]
[Link]
3. <modelVersion>4.0.0</modelVersion>
4.
5. <groupId>[Link]</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>[Link]
12.
13. <properties>
14. <[Link]>UTF-8</[Link]>
15. </properties>
16.
17. <dependencies>
18. <dependency>
19. <groupId>[Link]</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>[Link]</groupId>
32. <artifactId>eclipselink</artifactId>
33. <version>2.3.0</version>
34. </dependency>
35. <dependency>
36. <groupId>[Link]</groupId>
37. <artifactId>[Link]</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>[Link]</groupId>
47. <artifactId>swing-layout</artifactId>
48. <version>1.0.3</version>
49. </dependency>
50. </dependencies>
51.
52. <repositories>
53. <repository>
54. <url>[Link]
55. <id>eclipselink</id>
56. <layout>default</layout>
57. <name>Repository for library Library[eclipselink]</name>
58. </repository>
59. </repositories>
60.
61. </project>
146/257
lignes 30-39 : les dpendances EclipseLink,
lignes 41-44 : la dpendance du pilote JDBC de MySQL
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.
1. package dao;
2.
3. import [Link];
4.
5. @Local
6. public interface ICotisationDaoLocal extends ICotisationDao{
7. }
Pour avoir les bons import de paquetages, faire [clic droit sur le code / Fix Imports].
1. package dao;
2.
3. import [Link];
4.
5. @Remote
6. public interface ICotisationDaoRemote extends ICotisationDao{
7. }
1. ...
2. import [Link];
3. import [Link];
4.
5. @Stateless
6. @TransactionAttribute([Link])
7. 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. package exception;
2.
3. import [Link];
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
Couche Couche Proxy Couche d'accs aux Donnes
utilisateur
interface mtier EJB donnes [DAO]
utilisateur [ui] [metier]
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.
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.
Revenons sur l'architecture o la couche [UI] est un client distant de la couche [mtier] :
1 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 :
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.
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
1 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].
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 [[Link]] 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 3
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
5 6 7
1. Infos - PersistenceUnit(name=dbpam_eclipselinkPU,
provider=[Link]) - provider time 396ms
2. Infos - Jndi(name=CotisationDaoLocal) --> Ejb(deployment-id=CotisationDao)
3. Infos - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/CotisationDao!
[Link]) --> Ejb(deployment-id=CotisationDao)
4. Infos - Jndi(name=CotisationDaoRemote) --> Ejb(deployment-id=CotisationDao)
5. Infos - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/CotisationDao!
[Link]) --> Ejb(deployment-id=CotisationDao)
151/257
6. Infos - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/CotisationDao) -->
Ejb(deployment-id=CotisationDao)
7. Infos - Jndi(name=EmployeDaoLocal) --> Ejb(deployment-id=EmployeDao)
8. Infos - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/EmployeDao!
[Link]) --> Ejb(deployment-id=EmployeDao)
9. Infos - Jndi(name=EmployeDaoRemote) --> Ejb(deployment-id=EmployeDao)
10. Infos - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/EmployeDao!
[Link]) --> Ejb(deployment-id=EmployeDao)
11. Infos - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/EmployeDao) -->
Ejb(deployment-id=EmployeDao)
12. Infos - Jndi(name=IndemniteDaoLocal) --> Ejb(deployment-id=IndemniteDao)
13. Infos - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/IndemniteDao!
[Link]) --> Ejb(deployment-id=IndemniteDao)
14. Infos - Jndi(name=IndemniteDaoRemote) --> Ejb(deployment-id=IndemniteDao)
15. Infos - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/IndemniteDao!
[Link]) --> Ejb(deployment-id=IndemniteDao)
16. Infos - Jndi(name=global/[Link]/mv-pam-openejb-eclipselink/IndemniteDao) -->
Ejb(deployment-id=IndemniteDao)
17. Infos - existing thread singleton service in SystemInstance()
[Link]@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-pam-
openejb-eclipselink\[Link])
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. [Link](Context.INITIAL_CONTEXT_FACTORY,
"[Link]");
12. // initialisation du contexte JNDI avec les proprits prcdentes
13. InitialContext initialContext = new InitialContext(properties);
14. // instanciation couches DAO distantes
15. employeDao = (IEmployeDaoRemote) [Link]("EmployeDaoRemote");
16. cotisationDao = (ICotisationDaoRemote) [Link]("CotisationDaoRemote");
17. indemniteDao = (IIndemniteDaoRemote) [Link]("IndemniteDaoRemote");
18. }
Ceci fait, la nouvelle classe de test peut tre excute. Auparavant, avec la connexion Netbeans [dbpam_eclipselink], supprimez les
tables de la base [dbpam_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.
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. import [Link];
4.
5. @Local
6. public interface IMetierLocal extends IMetier{
7.
8. }
1. package metier;
2.
3. import [Link];
4.
5. @Remote
6. public interface IMetierRemote extends IMetier{
7.
8. }
Ceci fait, en [3] nous modifions la classe [Metier] afin qu'elle devienne un EJB :
1. @Stateless
2. @TransactionAttribute([Link])
3. public class Metier implements IMetierLocal, IMetierRemote {
4.
5. // 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. ...
154/257
2
3
Note : avant d'excuter ce test, il faut modifier le fichier [[Link]] pour que les tables ne soient plus dtruites et recres au
moment de l'instanciation de la couche [JPA] (lignes 13-15 ci-dessous) :
155/257
3. <persistence-unit name="dbpam_eclipselinkPU" transaction-type="JTA">
4. <!-- le fournisseur JPA est EclipseLink -->
5. <provider>[Link]</provider>
6. <!-- entits Jpa -->
7. <class>[Link]</class>
8. <class>[Link]</class>
9. <class>[Link]</class>
10. <!-- proprits provider EclipseLink -->
11. <properties>
12. <property name="[Link]" value="FINE"/>
13. <!--
14. <property name="[Link]-generation" value="drop-and-create-tables"/>
15. -->
16. </properties>
17. </persistence-unit>
18. </persistence>
1 2
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.
156/257
28. for(Indemnite indemnite : [Link]()){
29. [Link](indemnite);
30. }
31. // on la remplit
32. Indemnite indemnite1=new Indemnite(1,1.93,2,3,12);
33. Indemnite indemnite2=new Indemnite(2,2.1,2.1,3.1,15);
34. indemnite1=[Link](indemnite1);
35. indemnite2=[Link](indemnite2);
36. [Link](new Employe("254104940426058","Jouveinal","Marie","5 rue des
oiseaux","St Corentin","49203",indemnite2));
37. [Link](new Employe("260124402111742","Laverti","Justine","La brlerie","St
Marcel","49014",indemnite1));
38. [Link](new Cotisation(3.49,6.15,9.39,7.88));
39. }
40. }
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. En [2], la classe [Main] est renomme [MainLocal]. Elle utilisera l'interface locale de l'EJB [Metier].
157/257
7. for (int i = 0; i < [Link](); i++) {
8. [Link]([Link](i));
9. }
10. return;
11. }
12. // c'est bon - on peut demander la feuille de salaire la couche [mtier]
13. IMetierLocal metier = null;
14. FeuilleSalaire feuilleSalaire = null;
15. try {
16. // on configure le conteneur Open EJB embarqu
17. Properties properties = new Properties();
18. [Link](Context.INITIAL_CONTEXT_FACTORY,
"[Link]");
19. // initialisation du contexte JNDI avec les proprits prcdentes
20. InitialContext initialContext = new InitialContext(properties);
21. // instanciation couche mtier locale
22. metier = (IMetierLocal) [Link]("MetierLocal");
23. // calcul de la feuille de salaire
24. feuilleSalaire = [Link](args[0], nbHeuresTravailles,
nbJoursTravaills);
25. } catch (PamException ex) {
26. [Link]("L'erreur suivante s'est produite : " + [Link]());
27. return;
28. } catch (Exception ex) {
29. [Link]("L'erreur suivante s'est produite : " + [Link]());
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 17-
22). 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/[Link]] :
158/257
7. <class>[Link]</class>
8. <class>[Link]</class>
9. <class>[Link]</class>
10. <!-- proprits provider EclipseLink -->
11. <properties>
12. <property name="[Link]" value="FINE"/>
13. <!--
14. <property name="[Link]-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=[Link])
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/pam-
console-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
2
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] :
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.
Spring
Objets image
Couche Couche Couche Interface Implmentation Couche
de la BD
[ui] [metier] [DAO] [JPA] [EclipseLink] [JDBC]
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.
OpenEjb
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.
Couche C
Couche Couche Couche Couche
[ui] [metier] [DAO] [JPA / [JDBC]
EclipseLink]
Nous tudions ici la partie serveur qui sera hberge par le conteneur EJB3 du serveur Glassfish :
162/257
Conteneur
Client Jpa / Eclipselink
Ejb3 Donnes
Java SE
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.
3
2
5 6
4b 7
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.
2 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
[[Link]] 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
[Link] Configuration de la couche de persistance
Par configuration de la couche de persistance, nous entendons l'criture du fichier [[Link]] qui dfinit :
l'implmentation JPA utiliser
la dfinition de la source de donnes exploite par la couche JPA. Celle-ci sera une source JDBC gre par le serveur
Glassfish.
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 :
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
en [5], indiquer que la ressource JDBC va utiliser un nouveau pool de connexions. On rappelle qu'un pool de connexions
est un pool de connexions ouvertes qui sert acclrer les changes de l'application avec la base de donnes.
en [6], donner un nom JNDI la ressource JDBC cre. Ce nom peut tre quelconque mais il a souvent la forme jdbc/nom.
Ce nom JNDI sera utilis dans le fichier [[Link]] pour dsigner la source de donnes que l'implmentation JPA
doit utiliser.
en [7], donner un nom qui peut tre quelconque au pool de connexions qui va tre cr
dans la liste droulante [8], choisir la connexion JDBC cre prcdemment sur la base MySQL / dbpam_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 [[Link]] 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 "-//[Link]//DTD GlassFish Application Server 3.1 Resource
Definitions//EN" "[Link]
3. <resources>
4. <jdbc-resource enabled="true" jndi-name="jdbc/dbpam_eclipselink" object-type="user" pool-
name="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" connection-
validation-method="auto-commit" datasource-
classname="[Link]" fail-all-connections="false"
idle-timeout-in-seconds="300" is-connection-validation-required="false" is-isolation-level-
guaranteed="true" lazy-connection-association="false" lazy-connection-enlistment="false"
match-connections="false" max-connection-usage-count="0" max-pool-size="32" max-wait-time-
in-millis="60000" name="dbpamEclipselinkConnectionPool" non-transactional-
connections="false" pool-resize-quantity="2" res-type="[Link]" statement-
timeout-in-seconds="-1" steady-pool-size="8" validate-atmost-once-period-in-seconds="0"
wrap-jdbc-objects="false">
8. <property name="URL" value="jdbc:mysql://localhost:3306/dbpam_eclipselink"/>
9. <property name="User" value="root"/>
10. <property name="Password" value=""/>
11. </jdbc-connection-pool>
12. </resources>
Le fichier [[Link]] est un fichier XML qui reprend toutes les donnes collectes par l'assistant. Il va tre utilis par
Netbeans pour, lors du dploiement du module EJB sur le serveur Glassfish, demander la cration de la ressource JDBC dont a
besoin ce module.
On peut dsormais crer le fichier [[Link]] qui va configurer la couche JPA du module EJB :
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
7
8
9
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 [[Link]] a t cr [10]. Son contenu est le suivant :
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 :
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]
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 :
1 2
168/257
3
1 4
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-JPA-
eclipselink/IndemniteDao![Link], java:global/pam-serveur-metier-dao-JPA-
eclipselink/IndemniteDao![Link]]
4. INFO: Glassfish-specific (Non-portable) JNDI names for EJB IndemniteDao :
[[Link]#[Link], [Link]]
5. ...
6. INFO: Portable JNDI names for EJB CotisationDao : [java:global/pam-serveur-metier-dao-JPA-
eclipselink/CotisationDao![Link], java:global/pam-serveur-metier-dao-JPA-
eclipselink/CotisationDao![Link]]
7. INFO: Glassfish-specific (Non-portable) JNDI names for EJB CotisationDao :
[[Link], [Link]#[Link]]
8. INFO: Portable JNDI names for EJB Metier : [java:global/pam-serveur-metier-dao-JPA-
eclipselink/Metier![Link], java:global/pam-serveur-metier-dao-JPA-
eclipselink/Metier![Link]]
9. INFO: Glassfish-specific (Non-portable) JNDI names for EJB Metier :
[[Link]#[Link], [Link]]
10. ...
11. INFO: Portable JNDI names for EJB EmployeDao : [java:global/pam-serveur-metier-dao-JPA-
eclipselink/EmployeDao![Link], java:global/pam-serveur-metier-dao-JPA-
eclipselink/EmployeDao![Link]]
12. INFO: Glassfish-specific (Non-portable) JNDI names for EJB EmployeDao :
[[Link]#[Link], [Link]]
13. INFO: pam-serveur-metier-dao-JPA-eclipselink was successfully deployed in 12 891
milliseconds.
Ces noms seront utiles l'application console que nous allons crire pour utiliser le module EJB dploy.
169/257
Maintenant que nous avons dploy la partie serveur de notre application client / serveur, nous en venons tudier la partie client
[1] :
Nous crons un nouveau projet Maven de type [Java Application] nomm [mv-pam-client-ejb-metier-dao-eclipselink] :
1 2
1. <project xmlns="[Link]
xmlns:xsi="[Link]
2. xsi:schemaLocation="[Link] [Link]
[Link]">
3. <modelVersion>4.0.0</modelVersion>
4.
5. <groupId>[Link]</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>[Link]
12. <repositories>
13. <repository>
14. <url>[Link]
15. <id>eclipselink</id>
16. <layout>default</layout>
17. <name>Repository for library Library[eclipselink]</name>
18. </repository>
19. <repository>
20. <url>[Link]
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. <[Link]>UTF-8</[Link]>
28. </properties>
29.
30. <dependencies>
31. <dependency>
32. <groupId>[Link]</groupId>
33. <artifactId>gf-client</artifactId>
34. <version>3.1.1</version>
35. </dependency>
36. <dependency>
37. <groupId>${[Link]}</groupId>
38. <artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
39. <version>${[Link]}</version>
40. <type>ejb</type>
41. </dependency>
42. <dependency>
43. <groupId>[Link]</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],
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) [Link]("java:global/istia.st_mv-pam-ejb-
metier-dao-eclipselink_ejb_1.0-SNAPSHOT/Metier![Link]");
9. // calcul de la feuille de salaire
10. feuilleSalaire = [Link](args[0], nbHeuresTravailles,
nbJoursTravaills);
11. } catch (PamException ex) {
12. [Link]("L'erreur suivante s'est produite : "
13. + [Link]());
14. return;
15. } catch (Exception ex) {
16. [Link]("L'erreur suivante s'est produite : "
17. + [Link]());
18. return;
19. }
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.
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.
Les lignes 7 et 8 dsignent la machine du service JNDI et le port d'coute de celui-ci. Ce fichier ne permet pas d'interroger un
serveur JNDI autre que localhost ou travaillant sur un port autre que le port 3700. Si on veut changer ces deux paramtres, on peut
construire son propre fichier [[Link]] ou utiliser une configuration Spring. Nous montrons cette deuxime technnique.
Nous commenons par crer un nouveau projet partir du projet [pam-client-metier-dao-JPA-eclipselink] initial.
173/257
2
1
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 [[Link]] devient le contenu de la balise <jee:environment> (ligne 15) qui sert dfinir les
paramtres de connexion au service JNDI.
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-config-
[Link]");
8. metier = (IMetierRemote) [Link]("metier");
9. // calcul de la feuille de salaire
174/257
10. feuilleSalaire = [Link](args[0], nbHeuresTravailles,
nbJoursTravaills);
11. } catch (PamException ex) {
12. [Link]("L'erreur suivante s'est produite : "
13. + [Link]());
14. return;
15. } catch (Exception ex) {
16. [Link]("L'erreur suivante s'est produite : "
17. + [Link]());
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 [[Link]] changerait. On retrouverait
alors une configuration analogue l'architecture Spring / JPA tudie au paragraphe 4.11.
Nous construisons maintenant le client swing de notre application client / serveur EJB.
1. <dependency>
2. <groupId>[Link]</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 :
7 Spring
Maintenant cette classe doit devenir le client distant d'un EJB dploy sur le serveur Glassfish.
175/257
Couche Couche Couche Couche Couche
[ui] [metier] [DAO] [JPA / [JDBC]
swing EclipseLink]
Travail pratique : en suivant l'exemple du client console [[Link]] 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 :
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] :
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.
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 Conteneur
Conteneur web Jpa
du Ejb3 Donnes
service web tcp-ip serveur Java EE
Client Conteneur
Jpa
du Ejb3 Donnes
service web serveur Java EE
177/257
7.1 Service web implment par un EJB
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([Link])
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 :
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.
En [1], nous voyons notre application serveur dploye sur le serveur Glassfish. Une fois le service web dploy, il peut tre test :
3
1
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
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
2
3
4 5
181/257
7
10
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.
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.
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
[[Link]] et [[Link]], o l'une encapsule l'appel la mthode et l'autre son rsultat.
Enfin, la classe MetierService est la classe qui permet au client d'avoir une rfrence sur le service web Metier distant :
1. @WebEndpoint(name = "MetierPort")
2. public Metier getMetierPort() {
3. return [Link](new QName("[Link] "MetierPort"), [Link]);
4. }
La mthode getMetierPort de la ligne 2 permet d'obtenir une rfrence sur le service web Metier distant.
182/257
[Link] Le client console du service web Metier
Il ne nous reste plus qu' crire le client du service web Metier. Nous recopions la classe [MainRemote] du projet [mv-pam-client-
metier-dao-JPA-eclipselink] qui tait un client d'un serveur EJB, dans le nouveau projet.
2
3
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.
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 [[Link]] (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 = [Link](args[0], nbHeuresTravailles,
nbJoursTravaills);
10. } catch (Throwable th) {
11. // chane des exceptions
12. [Link]("Chane des exceptions --------------------------------------");
13. [Link]([Link]().getName() + ":" + [Link]());
14. while ([Link]() != null) {
15. th = [Link]();
16. [Link]([Link]().getName() + ":" + [Link]());
17. }
18. [Link](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.
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
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 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].
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.
Client Conteneur
Conteneur web Jpa
du EJB3 Donnes
service web tcp-ip 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.
3
1
2
185/257
6
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 Conteneur
Conteneur web Jpa
du EJB3 Donnes
service web tcp-ip 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.
186/257
La classe [PamWsEjbMetier] est la suivante :
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11.
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 [Link](SS, nbHeuresTravailles, nbJoursTravaills);
21. }
22.
23. @WebMethod
24. public List<Employe> findAllEmployes() {
25. return [Link]();
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 :
1 2
3
Pour dployer le service web sur le serveur Glassfish, il nous faut la fois dployer :
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].
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-pam-
webapp]. Nous ne nous en occuperons pas.
en [8], nous ajoutons des dpendances au projet d'entreprise
188/257
9 10
11
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.
189/257
1
Travail faire : en suivant la dmarche dcrite au paragraphe [Link], page 181, construire un client console du service web
prcdent.
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 :
Spring
7
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 [[Link]] pour y inclure les dpendances [2] suivantes :
1. <dependencies>
2. <dependency>
3. <groupId>${[Link]}</groupId>
4. <artifactId>mv-pam-spring-hibernate</artifactId>
5. <version>${[Link]}</version>
6. </dependency>
7. <!-- Apache CXF dependencies -->
8. <dependency>
9. <groupId>[Link]</groupId>
10. <artifactId>cxf-rt-frontend-jaxws</artifactId>
11. <version>2.2.12</version>
12. </dependency>
13. <dependency>
14. <groupId>[Link]</groupId>
15. <artifactId>cxf-rt-transports-http</artifactId>
16. <version>2.2.12</version>
17. </dependency>
18. </dependencies>
191/257
Couche Couche Couche Interface Implmentation Couche
[web] [metier] [DAO] [JPA] [Hibernate] [JDBC]
7 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 / [Link]] de la faon suivante :
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 /
[Link]] :
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. package [Link];
2.
3. import [Link];
4. import [Link];
5.
6. @WebService
7. public interface IWsMetier extends IMetier{
8.
9. }
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.
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
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 [Link](SS, nbHeuresTravaillees, nbJoursTravailles);
24. }
25.
26. @WebMethod
27. public List<Employe> findAllEmployes() {
28. return [Link]();
29. }
30.
31. // getters et setters
32.
33. public void setMetier(IMetier metier) {
34. [Link] = metier;
35. }
36.
193/257
37. }
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 [Link]), cette URL devient /ws/metier.
194/257
Notre projet est prt tre excut. Nous l'excutons (Run) et demandons l'URL [[Link]
tomcat/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 :
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.
Travail faire : en suivant la dmarche dcrite au paragraphe [Link], 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
3
196/257
8 Introduction Java Server Faces
On lira le document Introduction Java Server Faces, Primefaces et Primefaces mobile l'URL
[[Link] On suivra le tutoriel sur Java Server Faces (JSF).
197/257
9 Version 5 - Application PAM Web / JSF
7 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)
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 serveur
Couche Couche Couche Couche Couche
[ui] [metier] [DAO] [JPA / [JDBC]
EclipseLink]
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 :
client serveur
198/257
Conteneur web Conteneur EJB3 Jpa 3
Navigateur [web / jsf] [metier, DAO] 2 EclipseLink SGBD
1
HTTP 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]
2a 2b
1
Faces Servlet Gestionnaire
3 d'vts couche Donnes
Navigateur JSF1 [metier, dao, jpa]
4b JSF2 2c
Modles 4a
JSFn
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 :
199/257
9.2 Fonctionnement de l'application
200/257
C
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].
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]
2a 2b
1
Faces Servlet Gestionnaire
3 d'vts couche
Navigateur JSF1 [metier]
4b JSF2 2c simule
Modles 4a
JSFn
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]
2a 2b
1
Faces Servlet Gestionnaire
3 d'vts couche Donnes
Navigateur JSF1 [metier, dao, jpa]
4b JSF2 2c
Modles 4a
JSFn
5
1
4
202/257
Nous passons en revue certains de ces lments.
203/257
7. <f:view locale="#{[Link]}">
8. <h:head>
9. <title>JSF</title>
10. <h:outputStylesheet library="css" name="[Link]"/>
11. </h:head>
12. <h:body style="background-image: url('$
{[Link]}/resources/images/[Link]');">
13. <h:form id="formulaire">
14. <h3><h:outputText value="#{msg['[Link]']}"/></h3>
15. <h:panelGrid columnClasses="col1,col2" columns="2" border="1">
16. <h:outputText value="#{msg['[Link]']}"/>
17. <h:outputText value="#{requestScope['[Link].status_code']}"/>
18. <h:outputText value="#{msg['[Link]']}"/>
19. <h:outputText value="#{requestScope['[Link]']}"/>
20. <h:outputText value="#{msg['[Link]']}"/>
21. <h:outputText value="#{requestScope['[Link].request_uri']}"/>
22. <h:outputText value="#{msg['[Link]']}"/>
23. <h:outputText value="#{requestScope['[Link].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 ci-
dessous :
lignes 9-14 : le fichier [[Link]] sera utilis pour l'internationalisation des pages. Il sera accessible dans les
pages XHTML via la cl msg.
204/257
ligne 15 : dfinit le fichier [[Link]] comme devant tre explor en priorit pour les messages d'erreur affichs
par les balises <h:messages> et <h:message>. Cela permet de redfinir certains messages d'erreur par dfaut de JSF. Cette
possibilit n'est pas utilise ici.
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. }
<h:outputText value="#{msg['[Link]']}"
styleClass="titreInfos"/>
1. [Link]=Feuille de salaire
2. [Link]\u00e9=Employ\u00e9
3. [Link]\[Link]\u00e9=Heures travaill\u00e9es
4. [Link]\[Link]\u00e9=Jours travaill\u00e9s
5. [Link]\[Link]=Indiquez le nombre d'heures travaill\u00e9es
6. [Link]\[Link]=Donn\u00e9e incorrecte
7. [Link]\[Link]=Indiquez le nombre de jours travaill\u00e9s
8. [Link]\[Link]=Donn\u00e9e incorrecte
9. [Link]\u00e9=Salaire
10. [Link]\u00e9=Raz
11. [Link]=L'exception suivante s'est produite
12. [Link]=Code HTTP de l'erreur
205/257
13. [Link]=Message de l'exception
14. [Link]=Url demand\u00e9e lors de l'erreur
15. [Link]=Nom de la servlet demand\u00e9e lorsque l'erreur s'est produite
16. [Link]\u00e9=Informations Employ\u00e9
17. [Link]=Nom
18. [Link]\u00e9nom=Pr\u00e9nom
19. [Link]=Adresse
20. [Link]=Ville
21. [Link]=Code postal
22. [Link]=Indice
23. [Link]=Informations Cotisations sociales
24. [Link]=CSGRDS
25. [Link]=CSGD
26. [Link]=Retraite
27. [Link]=S\u00e9curit\u00e9 sociale
28. [Link]=Informations Indemnit\u00e9s
29. [Link]=Salaire horaire
30. [Link]=Entretien / Jour
31. [Link]=Repas / Jour
32. [Link]\u00e9sPay\u00e9s=Cong\u00e9s pay\u00e9s
33. [Link]=Informations Salaire
34. [Link]=Salaire de base
35. [Link]=Cotisations sociales
36. [Link]=Indemnit\u00e9s d'entretien
37. [Link]=Indemnit\u00e9s de repas
38. [Link]=Salaire net
Ces messages sont tous utiliss dans la page [[Link]] l'exception de ceux des lignes 11-15 utiliss dans la page
[[Link]].
1. import [Link];
2. import [Link];
3. import [Link];
4.
5. @ManagedBean
6. @RequestScoped
7. public class Form implements Serializable {
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
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. public String setEnglishLocale(){
22. locale="en";
23. return null;
24. }
25.
26. public String getLocale() {
27. return locale;
28. }
29.
30. public void setLocale(String locale) {
31. [Link] = locale;
32. }
33.
34.
35. }
1. package metier;
2.
3. import [Link];
4. import [Link];
5. import [Link];
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=[Link](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. // liste des employs
21. public List<Employe> findAllEmployes() {
22. if(listEmployes==null){
23. // on cre une liste de deux employs
24. listEmployes=new ArrayList<Employe>();
25. [Link](new Employe("254104940426058","Jouveinal","Marie","5 rue des
oiseaux","St Corentin","49203",new Indemnite(2,2.1,2.1,3.1,15)));
26. [Link](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. [Link]([Link](),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].
Nous construisons maintenant la page XHTML du formulaire ainsi que son modle.
9.4.1 tape 1
Question : Construire le formulaire [[Link]] et son modle [[Link]] ncessaires pour obtenir la page suivante :
1 4 5
2 3
208/257
4 btnSalaire <h:commandButton> lance le calcul du salaire
5 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 [[Link]] et son modle [[Link]] pour obtenir la page suivante une fois que le bouton
[Salaire] a t cliqu :
Le bouton [Salaire] sera connect au gestionnaire d'vnement calculerSalaire du modle. Cette mthode utilisera la mthode
calculerFeuilleSalaire de la couche [mtier]. Cette feuille de salaire sera faite pour l'employ slectionn en [1].
Dans le modle, la feuille de salaire sera reprsente par le champ priv suivant :
Pour obtenir les informations contenues dans cet objet, on pourra crire dans la page JSF, des expressions comme la suivante :
<h:outputText value="#{[Link]}"/>
209/257
[form].getFeuilleSalaire().getEmploye().getNom() o [form] reprsente une instance de la classe [[Link]]. Le lecteur pourra vrifier que
les mthodes get utilises ici existent bien respectivement dans les classes [Form], [FeuilleSalaire] et [Employe]. Si ce n'tait pas le cas,
une exception serait lance lors de l'valuation de l'expression.
9.4.3 tape 3
Question : complter le formulaire [[Link]] et son modle [[Link]] 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="#{[Link]}"/>
3. </h:outputFormat>
On aurait pu crire :
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 :
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.
9.4.4 tape 4
Question : complter le formulaire [[Link]] et son modle [[Link]] pour grer le bouton [Raz].
Le bouton [Raz] ramne le formulaire dans l'tat qu'il avait lorsqu'on l'a demand la premire fois par un GET. Il y a plusieurs
difficults ici. Certaines ont t expliques dans [ref3].
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 :
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.
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
Application web
couche [web]
2a 2b
1
Faces Servlet Gestionnaire
3 d'vts couche
Navigateur JSF1 [metier]
4b JSF2 2c simule
Modles 4a
JSFn
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]
2a 2b
1
Faces Servlet Gestionnaire
3 d'vts couches Donnes
Navigateur JSF1 [metier, DAO, JPA]
4b JSF2 2c
Modles 4a
JSFn
Le projet Netbeans de la version web n 2 est obtenue par copie du projet prcdent :
212/257
2
Le nouveau projet porte le mme nom que l'ancien. Nous changeons cela :
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.
3 4
2
5
1. <dependencies>
2. <dependency>
3. <groupId>${[Link]}</groupId>
4. <artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
5. <version>${[Link]}</version>
6. <scope>provided</scope>
7. <type>ejb</type>
8. </dependency>
9. <dependency>
10. <groupId>javax</groupId>
11. <artifactId>javaee-web-api</artifactId>
12. <version>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 [[Link]] :
La ligne 7 instanciait la couche [mtier] simule. Dsormais elle doit rfrencer la couche [mtier] relle. Le code prcdent devient
le suivant :
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]
2a 2b
1
Faces Servlet Gestionnaire
3 d'vts couche Donnes
Navigateur JSF1 [metier, dao, jpa]
4b JSF2 2c
Modles 4a
JSFn
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.
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.
215/257
4
3
1
2
7
5
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.
2 3
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.
1. <dependencies>
2. <dependency>
3. <groupId>${[Link]}</groupId>
4. <artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
5. <version>${[Link]}</version>
6. <type>ejb</type>
7. </dependency>
8. <dependency>
9. <groupId>${[Link]}</groupId>
10. <artifactId>mv-pam-jsf2-ejb</artifactId>
11. <version>${[Link]}</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] :
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]
2a 2b
1
Faces Servlet Gestionnaires
3 d'vts couche
Navigateur JSF1 [metier]
4b JSF2 2c simule
Modles 4a
JSFn
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.
Application web
couche [web]
2a 2b
1
Faces Servlet [MC]
3 [Link] couche
JSF1 [metier]
4 Modle M
simule
JSF2 Gestionnaires
d'vts
JSFn
219/257
11.1 Les vues de l'application
- 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 :
221/257
5 6
1
3
7
4
2
Les fichiers [[Link]] et [[Link]] sont identiques ceux du projet prcdent l'exception d'un dtail dans [[Link]] :
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. }
Vue Simulations
Vue Erreur
224/257
14. [Link]=Url demand\u00e9e lors de l'erreur
15. [Link]=Nom de la servlet demand\u00e9e lorsque l'erreur s'est produite
16. [Link]\u00e9=Informations Employ\u00e9
17. [Link]=Nom
18. [Link]\u00e9nom=Pr\u00e9nom
19. [Link]=Adresse
20. [Link]=Ville
21. [Link]=Code postal
22. [Link]=Indice
23. [Link]=Informations Cotisations sociales
24. [Link]=CSGRDS
25. [Link]=CSGD
26. [Link]=Retraite
27. [Link]=S\u00e9curit\u00e9 sociale
28. [Link]=Informations Indemnit\u00e9s
29. [Link]=Salaire horaire
30. [Link]=Entretien / Jour
31. [Link]=Repas / Jour
32. [Link]\u00e9sPay\u00e9s=Cong\u00e9s pay\u00e9s
33. [Link]=Informations Salaire
34. [Link]=Salaire de base
35. [Link]=Cotisations sociales
36. [Link]=Indemnit\u00e9s d'entretien
37. [Link]=Indemnit\u00e9s de repas
38. [Link]=Salaire net
39. [Link]=| Faire la simulation
40. [Link]=| Effacer la simulation
41. [Link]=| Enregistrer la simulation
42. [Link]=| Retour au simulateur
43. [Link]=| Voir les simulations
44. [Link]=| Terminer la session
45. [Link]=Nom
46. [Link]=Nom
47. [Link]=Pr\u00e9nom
48. [Link]=Heures travaill\u00e9es
49. [Link]=Jours Travaill\u00e9s
50. [Link]=Salaire de base
51. [Link]=Indemnit\u00e9s
52. [Link]=Cotisations sociales
53. [Link]=SalaireNet
54. [Link]=N\u00b0
55. [Link]=Une erreur s'est produite.
56. [Link]=Cha\u00eene des exceptions
57. [Link]=Type de l'exception
58. [Link]=Message associ\u00e9
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. // on rcupre l'employ
14. Employe e=[Link](SS);
15. // on rend une exception si l'employ n'existe pas
16. if(e==null){
17. throw new PamException([Link]("L'employ de n SS [%s] n'existe pas",SS),1);
18. }
19. // on rend une feuille de salaire fictive
20. 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. [Link](new Employe("254104940426058","Jouveinal","Marie","5 rue des
oiseaux","St Corentin","49203",new Indemnite(2,2.1,2.1,3.1,15)));
29. [Link](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. [Link]([Link](),e);
33. }
34. // on ajoute un employ qui n'existera pas dans le dictionnaire
35. [Link](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. }
226/257
11.3.1 Le bean ApplicationData
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10.
11. @Named
12. @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 = [Link]("pam");
20.
21. public ApplicationData() {
22. }
23.
24. @PostConstruct
25. public void init() {
26. // log
27. if (isLogsEnabled()) {
28. [Link]("ApplicationData");
29. }
30. }
31.
32. // getters et setters
33. ...
34. }
ligne 11 : l'annotation @Named fait de la classe un bean manag. On notera qu' la diffrence du projet prcdent, on n'a
pas utilis l'annotation @ManagedBean. La raison en est que la rfrence de cette classe doit tre injecte dans une autre
classe l'aide de l'annotation @Inject et que celle-ci n'injecte que des classes annotes @Named.
ligne 12 : l'annotation @ApplicationScoped fait de la classe, un objet de porte application. On notera que la classe de
l'annotation est [[Link]] (ligne 6) et non [[Link]] comme
dans les beans du projet prcdent.
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
227/257
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
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 ([Link]()) {
42. [Link]().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. [Link](menuFaireSimulationIsRendered);
49. [Link](menuEnregistrerSimulationIsRendered);
50. [Link](menuVoirSimulationsIsRendered);
51. [Link](menuEffacerSimulationIsRendered);
52. [Link](menuRetourSimulateurIsRendered);
53. [Link](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).
1. package [Link];
2.
3. import [Link];
4. import [Link];
5. import [Link];
6. import [Link];
7. import [Link];
8. import [Link];
9. import [Link];
10. import [Link];
11. import [Link];
12. import [Link];
13. import [Link].*;
14. import [Link];
15. import [Link];
16.
17. @Named
18. @RequestScoped
19. public class Form {
20.
21. public Form() {
22. }
23. // autres beans
24. @Inject
25. private ApplicationData applicationData;
26. @Inject
27. private SessionData sessionData;
28. // le 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 [Link]().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.1 [[Link]]
230/257
4. xmlns:h="[Link]
5. xmlns:f="[Link]
6. xmlns:ui="[Link]
7.
8. <f:view locale="#{[Link]}">
9. <h:head>
10. <title><h:outputText value="#{msg['[Link]']}"/></title>
11. <h:outputStylesheet library="css" name="[Link]"/>
12. </h:head>
13. <script type="text/javascript">
14. function raz(){
15. // on change les valeurs postes
16. [Link]['formulaire'].elements['formulaire:comboEmployes'].value="0";
17. [Link]['formulaire'].elements['formulaire:heuresTravaillees'].value="0";
18. [Link]['formulaire'].elements['formulaire:joursTravailles'].value="0";
19. }
20. </script>
21. <h:body style="background-image: url('$
{[Link]}/resources/images/[Link]');">
22. <h:form id="formulaire">
23. <!-- entete -->
24. <ui:include src="[Link]" />
25. <!-- contenu -->
26. <ui:insert name="part1" >
27. Gestion des assistantes maternelles
28. </ui:insert>
29. <ui:insert name="part2"/>
30. </h:form>
31. </h:body>
32. </f:view>
33. </html>
231/257
11.4.2 L'entte [[Link]]
232/257
La page d'accueil est la page [[Link]] 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 ?
234/257
La simulation est affiche avec la page [[Link]] suivante :
235/257
27. <h:outputText value="#{[Link]}"/>
28. <h:outputText value="#{[Link]}"/>
29. <h:outputText value="#{[Link]}"/>
30. </h:panelGrid>
31. <br/>
32. <h:outputText value="#{msg['[Link]']}" styleClass="titreInfos"/>
33. <br/><br/>
34. <h:panelGrid columns="4" rowClasses="libelle,info">
35. <h:outputText value="#{msg['[Link]']}"/>
36. <h:outputText value="#{msg['[Link]']}"/>
37. <h:outputText value="#{msg['[Link]']}"/>
38. <h:outputText value="#{msg['[Link]']}"/>
39. <h:outputText value="#{[Link]} %"/>
40. <h:outputText value="#{[Link]} %"/>
41. <h:outputText value="#{[Link]} %"/>
42. <h:outputText value="#{[Link]} %"/>
43. </h:panelGrid>
44. <br/>
45. <h:outputText value="#{msg['[Link]']}" styleClass="titreInfos"/>
46. <br/><br/>
47. <h:panelGrid columns="4" rowClasses="libelle,info">
48. <h:outputText value="#{msg['[Link]']}"/>
49. <h:outputText value="#{msg['[Link]']}"/>
50. <h:outputText value="#{msg['[Link]']}"/>
51. <h:outputText value="#{msg['[Link]']}"/>
52. <h:outputFormat value="{0,number,currency}">
53. <f:param value="#{[Link]}"/>
54. </h:outputFormat>
55. <h:outputFormat value="{0,number,currency}">
56. <f:param value="#{[Link]}"/>
57. </h:outputFormat>
58. <h:outputFormat value="{0,number,currency}">
59. <f:param value="#{[Link]}"/>
60. </h:outputFormat>
61. <h:outputText value="#{[Link]} %"/>
62. </h:panelGrid>
63. <br/>
64. <h:outputText value="#{msg['[Link]']}" styleClass="titreInfos"/>
65. <br/><br/>
66. <h:panelGrid columns="4" rowClasses="libelle,info">
67. <h:outputText value="#{msg['[Link]']}"/>
68. <h:outputText value="#{msg['[Link]']}"/>
69. <h:outputText value="#{msg['[Link]']}"/>
70. <h:outputText value="#{msg['[Link]']}"/>
71. <h:outputFormat value="{0,number,currency}">
72. <f:param value="#{[Link]}"/>
73. </h:outputFormat>
74. <h:outputFormat value="{0,number,currency}">
75. <f:param value="#{[Link]}"/>
76. </h:outputFormat>
77. <h:outputFormat value="{0,number,currency}">
78. <f:param value="#{[Link]}"/>
79. </h:outputFormat>
80. <h:outputFormat value="{0,number,currency}">
81. <f:param value="#{[Link]}"/>
82. </h:outputFormat>
83. </h:panelGrid>
84. <br/>
85. <h:panelGrid columns="3" columnClasses="libelle,col2,info">
86. <h:outputText value="#{msg['[Link]']}"/>
87. <h:panelGroup></h:panelGroup>
88. <h:outputFormat value="{0,number,currency}">
89. <f:param value="#{[Link]}"/>
236/257
90. </h:outputFormat>
91. </h:panelGrid>
92. </ui:define>
93. </ui:composition>
94. </html>
Question : crire la mthode [faireSimulation] de la classe [Form]. La simulation sera enregistre dans le bean SessionData.
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.}
1. package [Link];
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. [Link](classe);
15. [Link]=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.
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) :
238/257
1. <?xml version='1.0' encoding='UTF-8' ?>
2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"[Link]
3. <html xmlns="[Link]
4. xmlns:h="[Link]
5. xmlns:f="[Link]
6. xmlns:ui="[Link]
7.
8. <ui:composition template="[Link]">
9. <ui:define name="part1">
10. <h3><h:outputText value="#{msg['[Link]']}"/></h3>
11. <h:dataTable value="#{[Link]}" var="erreur"
12. headerClass="erreursHeaders" columnClasses="erreurClasse,erreurMessage">
13. <f:facet name="header">
14. <h:outputText value="#{msg['[Link]']}"/>
15. </f:facet>
16. <h:column>
17. <f:facet name="header">
18. <h:outputText value="#{msg['[Link]']}"/>
19. </f:facet>
20. <h:outputText value="#{[Link]}"/>
21. </h:column>
22. <h:column>
23. <f:facet name="header">
24. <h:outputText value="#{msg['[Link]']}"/>
25. </f:facet>
26. <h:outputText value="#{[Link]}"/>
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].
Un clic sur le lien [EffacerSimulation] provoque d'abord l'appel de la fonction Javascript raz(). Cette mthode est dfinie dans la
page [[Link]] :
239/257
1. <script type="text/javascript">
2. function raz(){
3. // on change les valeurs postes
4. [Link]['formulaire'].elements['formulaire:comboEmployes'].value="0";
5. [Link]['formulaire'].elements['formulaire:heuresTravaillees'].value="0";
6. [Link]['formulaire'].elements['formulaire:joursTravailles'].value="0";
7. }
8. </script>
Au cours du post, les valeurs postes vont suivre un cheminement normal : validation puis affectation aux champs du modle. Ceux-
ci sont les suivants dans la classe [Form] :
Ces trois champs vont recevoir les trois valeurs postes {"0","0","0"}. Une fois cette affectation opre, la mthode effacerSimulation
va tre excute.
L'action [enregistrerSimulation] associe au lien permet d'enregistrer la simulation courante dans une liste de simulations maintenue
dans la classe [SessionData] :
1. package [Link];
2.
3. import [Link];
4.
5. public class Simulation {
6.
7. public Simulation() {
8. }
9.
10. // champs d'une simulation
11. private Integer num;
12. private FeuilleSalaire feuilleSalaire;
13. private String heuresTravailles;
14. private String joursTravaills;
240/257
15.
16. // constructeur
17. public Simulation(Integer num,String heuresTravailles, String joursTravaills,
FeuilleSalaire feuilleSalaire){
18. [Link](num);
19. [Link](feuilleSalaire);
20. [Link](heuresTravailles);
21. [Link](joursTravaills);
22. }
23.
24. public double getIndemnites(){
25. return [Link]().getIndemnitesEntretien()+
[Link]().getIndemnitesRepas();
26. }
27.
28. // getters et setters
29. ...
30. }
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.
242/257
38. <f:facet name="header">
39. <h:outputText value="#{msg['[Link]']}"/>
40. </f:facet>
41. <h:outputText value="#{[Link]}"/>
42. </h:column>
43. <h:column>
44. <f:facet name="header">
45. <h:outputText value="#{msg['[Link]']}"/>
46. </f:facet>
47. <h:outputText value="#{[Link]}"/>
48. </h:column>
49. <h:column>
50. <f:facet name="header">
51. <h:outputText value="#{msg['[Link]']}"/>
52. </f:facet>
53. <h:outputText value="#{[Link]}"/>
54. </h:column>
55. <h:column>
56. <f:facet name="header">
57. <h:outputText value="#{msg['[Link]']}"/>
58. </f:facet>
59. <h:outputText
value="#{[Link]}"/>
60. </h:column>
61. <h:column>
62. <f:facet name="header">
63. <h:outputText value="#{msg['[Link]']}"/>
64. </f:facet>
65. <h:outputText value="#{[Link]}"/>
66. </h:column>
67. <h:column>
68. <h:commandLink value="Retirer" action="#{[Link]}">
69. <f:setPropertyActionListener target="#{[Link]}"
value="#{[Link]}"/>
70. </h:commandLink>
71. </h:column>
72. </h:dataTable>
73. </ui:define>
74. </ui:composition>
75. </html>
1. // simulations
[Link] 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['[Link]']}"/>
4. </f:facet>
5. <h:outputText value="#{[Link]}"/>
6. </h:column>
simulation dsigne la simulation courante de la liste des simulations : d'abord la 1re, puis la 2me, ...
[Link] fait rfrence au champ feuilleSalaire de la simulation courante
[Link] fait rfrence au champ employe du champ feuilleSalaire
[Link] fait rfrence au champ nom du champ employe
La mme technique est rpte pour toutes les colonnes du tableau. Il y a une difficult pour la colonne Indemnits qui est
gnre avec le code suivant :
1. <h:column>
2. <f:facet name="header">
3. <h:outputText value="#{msg['[Link]']}"/>
4. </f:facet>
5. <h:outputText value="#{[Link]}"/>
6. </h:column>
Ligne 5, on affiche la valeur de [Link]. Or la classe Simulation n'a pas de champ indemnites. Il faut se rappeler
ici que le champ indemnites n'est pas utilis directement mais via la mthode [Link](). Il suffit donc que
cette mthode existe. Le champ indemnites peut lui ne pas exister. La mthode getIndemnites doit rendre le total des indemnits de
l'employ. Cela ncessite un calcul intermdiaire car ce total n'est pas disponible directement dans la feuille de salaire. La mthode
getIndemnites est donne page 240.
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 ci-
dessus.
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 :
On fera en sorte que si la liste des simulations est vide, la vue affiche soit [vueSimulationsVides] :
246/257
Le rsultat obtenu est le suivant :
ligne 5 : le lien [Retirer] est associ la mthode [retirerSimulation] de la classe [Form]. Cette mthode a besoin de
connatre le n de la simulation retirer. Celui-ci lui est fourni par la balise <f:setPropertyActionListener> de la ligne 8.
Cette balise a deux attributs target et value : l'attribut target dsigne un champ du modle auquel la valeur de l'attribut
value sera affecte. Ici le n de la simulation retirer #{[Link]} sera affecte au champ
numSimulationToDelete de la classe [Form] :
Lorsque la mthode [retirerSimulation] de la classe [Form] s'excutera, elle pourra utiliser la valeur qui aura t stocke
auparavant dans le champ numSimulationToDelete.
247/257
<h:commandLink id="cmdTerminerSession" immediate="true"
value="#{msg['[Link]']}" action="#{[Link]}"
rendered="#{[Link]}"/>
L'action [terminerSession] associe au lien permet l'utilisateur d'abandonner sa session et de revenir au formulaire de saisies vide :
Si l'utilisateur avait une liste de simulations, celle-ci est vide. Par ailleurs, la numrotation des simulations repart 1.
11.6 Intgration de la couche web dans une architecture 3 couches JSF / EJB
Application web
couche [web]
2a 2b
1
Faces Servlet [MC]
3 [Link] couche
JSF1 [metier]
4 Modle M
simule
JSF2 Gestionnaires
d'vts
JSFn
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]
2a 2b
1
Faces Servlet [MC]
3 [Link]
couche
JSF1 [metier, DAO, JPA]
4 Modle M
JSF2 Gestionnaires
d'vts
JSFn
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.
250/257
13 Version 9 : Implmentation de la couche web avec Primefaces
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.
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) :
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 [[Link]] par la page suivante :
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
[Link] La table [personne]...........................................................................................................................................................19
[Link] L'entit [Personne]............................................................................................................................................................20
3.2.2 CONFIGURATION DE LA COUCHE JPA.......................................................................................................................................23
3.2.3 EXEMPLE 2 : RELATION UN--PLUSIEURS.................................................................................................................................26
[Link] Le schma de la base de donnes......................................................................................................................................26
[Link] 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 [[Link]]....................................................................................................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
[Link] La base de donnes...........................................................................................................................................................67
[Link] Configuration de la couche JPA........................................................................................................................................67
[Link] Les dpendances...............................................................................................................................................................70
[Link] Les entits JPA..................................................................................................................................................................71
[Link] Le code de la classe principale.........................................................................................................................................73
[Link] 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 [[Link]]..........................................................................................................................................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
[Link] L'EJB [CotisationDao]....................................................................................................................................................144
[Link] Les EJB [EmployeDao] et [IndemniteDao]....................................................................................................................145
[Link] La classe [PamException]...............................................................................................................................................145
[Link] Entits srialisables.........................................................................................................................................................146
[Link] Test de la couche [DAO].................................................................................................................................................146
5.2.4 PORTAGE DE LA COUCHE [METIER]........................................................................................................................................150
[Link] L'EJB [Metier]................................................................................................................................................................150
[Link] 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
[Link] Le projet Netbeans..........................................................................................................................................................160
[Link] Configuration de la couche de persistance......................................................................................................................161
[Link] Insertion des couches [JPA, DAO, metier].....................................................................................................................164
[Link] Configuration du serveur Glassfish.................................................................................................................................164
[Link] 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
[Link] Le projet Netbeans du client console..............................................................................................................................178
[Link] 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 [[Link]] ET SON MODLE [[Link]]...........................................................................................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 [[Link]]................................................................................................................................................................227
11.4.2 L'ENTTE [[Link]].................................................................................................................................................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
Hibernate utilise plusieurs options de configuration pour contrôler la gestion des tables. L'option "create" supprime et recrée les tables à l'initialisation de la couche JPA. "create-drop" fait de même, mais supprime les tables à la fin de la session JPA. L'option "update" crée les tables si elles n'existent pas, sans les détruire si elles sont déjà présentes, offrant ainsi une persistance continue .
Le choix entre LAZY et EAGER influence les performances en termes de mémoire et de temps de réponse. LAZY charge des collections ou associations uniquement lorsqu'elles sont explicitement accessées, réduisant consommation mémoire et latence initiale. EAGER charge immédiatement toutes les entités liées, ce qui peut ralentir le démarrage mais évite des problèmes de LazyInitializationException. Le contexte d'utilisation et les besoins de l'application détermineront la stratégie optimale .
Pour la gestion des clés primaires, JPA utilise différentes stratégies. Dans ce contexte, la stratégie GenerationType.IDENTITY est adoptée, ce qui signifie que la couche JPA s'appuie sur le mode auto_increment des tables MySQL pour générer automatiquement les clés primaires lors de l'insertion de nouvelles lignes .
Pour désactiver certaines fonctionnalités par défaut de JPA, comme les vérifications de nullité sur les clés primaires, les annotations @NotNull sont supprimées sur ceux-ci. De plus, les relations @OneToMany inverses sont éliminées pour prévenir des complexités inutiles, amenant ainsi à une configuration optimisée répondant uniquement aux besoins essentiels de l'application .
Les entités JPA sont générées en utilisant un assistant de NetBeans. Le processus commence par la création d'entités à partir d'une base de données existante. La connexion à la base de données [dbrdvmedecins2] est sélectionnée, puis toutes les tables associées sont choisies. Des classes Java sont créées pour ces tables, où le pluriel des noms de classe est retiré pour correspondre à des entités telles que [Medecin]. JPA regroupe les lignes de tables dans des collections comme des listes .
Dans un projet Java EE, une architecture avec EJB permet de gérer les exceptions de manière centralisée. Lors du calcul d'une feuille de salaire, si l'employé n'existe pas, une PamException est lancée. Cette exception peut être interceptée par des mécanismes de gestion des transactions dans EJB où l'exception est encapsulée dans une EJBException si l'annotation @ApplicationException n'est pas utilisée. Cela permet aux couches supérieures de récupérer l'exception sans affecter la transaction .
Les entités générées automatiquement requièrent des ajustements, notamment l'ajout de l'annotation @Version pour les champs de version. Il est également nécessaire de créer des méthodes toString plus élaborées. De plus, pour les entités comme [Medecin] et [Client], une classe parent [Personne] est introduite pour dériver ces entités, et les relations @OneToMany inverses des @ManyToOne sont supprimées car elles compliquent la programmation inutilement .
Dans une architecture Java EE, les services web peuvent être gérés par des EJB annotés @WebService, qui s'exécutent dans un conteneur EJB. Cela permet une communication via HTTP/SOAP multicouche, remplaçant les protocoles RMI/EJB précédents, et permet des échanges interplateformes grâce à des standards tels que WSDL pour la description des services .
L'annotation @MappedSuperclass est utilisée pour indiquer qu'une classe sert de base pour d'autres entités JPA, mais n'est pas elle-même une entité persistante. Cette configuration permet de définir des propriétés communes, telles que des champs d'identification ou des versions, qui seront héritées par les sous-classes qui sont de véritables entités .
Dans cette architecture, la sérialisation permet l'échange d'objets entre couches situées dans différentes JVM. Cela est réalisé en implémentant l'interface Serializable dans les classes concernées. Lorsqu'une couche utilisateur (UI) envoie un objet à la couche métier, elle envoie la valeur de l'objet sérialisé. La couche métier désérialise cette valeur pour recréer un nouvel objet identique dans sa propre JVM .