Vous êtes sur la page 1sur 49

TP1 2011 EJB 3.

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

1. Introduction 2. Logiciels utiliss 3. Cration d'un projet de type Entreprise Application 4. Crations de classes entits partir d'une base de donnes existante 5. Cration d'un Stateless Session Bean CustomerManager pour la gestion des clients 6. Ajout de mthodes mtier dans le session bean CustomerManager 7. Cration de la partie front-end web 8. Ajout du Framework Java Server Faces (JSF2) au projet web 9. Cration d'un Bean Manag en tant que "contrleur web" 10. Ajout d'une page JSF pour afficher la liste des clients 11. Ajout d'une DataTable JSF dans la page 12. Excution du projet et premier test 13. Remplacement de la DataTable par une provenant de la librairie de composants JSF PrimeFaces 13.1. Ajout de la librairie PrimeFaces dans le projet 13.2. Modification de la page JSF 14. Affichage des dtails d'un client lorsqu'on clique sur une ligne 14.1. Ajout d'un lien dans le tableau pour dclencher l'affichage des dtails d'un client 14.2. Ajout d'une page JSF pour afficher les dtails d'un client 14.3. Executez le projet et testez que les dtails s'affichent bien 14.4. Rgler l'affichage des DiscountCodes dans le dtail d'un client 15. Ajout de boutons pour la mise jour et retour la liste 16. Problmes devant encore tre rgls : 17. Correction de ce TP utilisant diverses amliorations vues en cours, et corrigeant la plupart des problmes voqus prcdemment 18. Pour la semaine prochaine:

2. 3. 4. 15. 16. 17.

18.

Introduction
Ce TP permet une prise de contact directe et violente avec les technologies EJB 3.1, JPA, et JSF 2.0 dans l'IDE NetBeans. Les cours et TPs suivants expliqueront le dtail de ce que

vous allez mettre en oeuvre aujourd'hui. Il s'agit d'une adaptation plus ou moins fidle de l'article paru sur le site developer Zone : http://netbeans.dzone.com/articles/d...-ee-6-appjsf2. Voici l'architecture de l'application que vous allez dvelopper :

Logiciels utiliss
Nous allons travailler avec la dernire version de netbeans, la version la plus complte (colonne de droite de : http://netbeans.org/downloads), qui vient avec la dernire version du serveur Java EE6 Glassfish Veillez avoir un la dernire version du JDK installe sur votre machine. Nous utiliserons galement la fin de ce TP quelques composants JSF2 provenant du site http://www.primefaces.org/

Cration d'un projet de type Entreprise Application


L'anne dernire nous avions travaill avec des projets de type "Web" qui incluaient tout dans un seul fichier .war, cette anne nous allons aussi voir que l'on peut dvelopper des applications de type "entreprise" qui font une sparation trs nette entre les parties "web" et les parties mtier/accs aux donnes d'une application. Pour crer un projet "entreprise" utilisez le menu File/New Project puis choisissez la catgorie Java EE, et un projet de type "Enterprise Application".

Cliquez ensuite sur le bouton "Next" puis choisissez l'emplacement du projet et son nom. Attention, pas de caractres bizarres dans le chemin ni le nom s'il vous plait, pas de "_" par exemple, pas d'accents, etc. Ceci pour viter les mauvaises surprises qui peuvent arriver par la suite (hibernate n'aime pas les noms de chemins bizarres, toplink n'aime pas les "_" dans les noms de projets, etc...) J'ai choisi comme nom de projet TP1CustomerApplication (comme vous voyez : pas d'espace ou de caractre bizarre dans le nom) et j'ai conserv l'endroit o Netbeans place les projets par dfaut (l aussi, je prfre un nom de chemin sans espaces...)

Dans la fentre suivante, on indique le nom du serveur et la version de java EE que l'on souhaite pour le projet. Prenez les valeurs par dfaut, si elles sont diffrentes de celles cidessous, assurez-vous que cela correspond bien la dernire version de glassfish et la version 6 de java EE (qui est celle tudie en cours).

Cliquez ensuite sur le bouton "finish", normalement netbeans gnre un projet pour votre application. En ralit, une application "enteprise" se compose de trois projets : 1. un projet prcd par un triangle bleu qui est en fait celui qui runit les deux autres projets au sein d'un mme fichier .ear (enterprise archive), quivalent du .war mais pour les applications enteprises, 2. Un projet prcd par un grain de caf, c'est le projet "mtier" qui va contenir la partie mtier de l'application ainsi que la partie accs aux donnes, il est prcd d'un grain de caf (un "bean") car il contiendra certainement des "beans", des "composants java" de type "managed beans" "enterprise java beans (EJBs)" ou "entities". 3. Un projet prcd par une icne en forme de globe terrestre, c'est le projet "web" qui va contenir le front-end web de l'application : les jsps, servlets, pages jsf ou html, les web services, etc.

Crations de classes entits partir d'une base de donnes existante


Dans ce projet nous allons utiliser une base de donnes. Comme nous l'avons vu en cours, on ne manipulera pas des donnes directement en SQL mais sous forme d'objets particuliers qu'on appelle des "classes entits". Gnralement une classe entit correspond une table, porte le mme nom (aux majuscules/minuscules prs), et ses attributs correspondent aux colonnes de la table. Les instances de cette classe entit seront des objets correspondant des lignes dans cette table. Dans un premier temps nous allons utiliser un wizard de netbeans pour gnrer une classe entit partir d'une table. On va ajouter la classe entit dans le projet "EJBs", celui prcd d'un grain de caf. Faire clic droit puis New/Entity Class from Database :

Une fentre apparait dans laquelle vous allez indiquer la source de donnes (le "nom" de la base de donnes sur laquelle vous allez travailler. Par dfaut le jdk vient avec un SGBD qui s'appelle JavaDB (ou "Derby") qui contient une base "sample" dont le nom est "jdbc/sample". Elle contient quelques tables correspondant un magasin de produits informatiques. Choisissez donc jdbc/sample dans la liste des sources de donnes :

Normalement ds que vous avez choisi le nom de la base, les tables s'affichent dans la colonne de gauche. Choisissez "CUSTOMER" et cliquez sur le bouton "add", cela va ajouter en fait deux tables : la table CUSTOMER mais aussi la table DISCOUNT_CODE car il existe une jointure entre ces deux tables :

Cliquez sur le bouton "Next". Maintenant on va vous proposer de changer le nom des classes entits correspondants chacune des tables. Je conseille de laisser tout par dfaut, cependant je conseille aussi de faire gnrer ces classes dans un package "entities" pour mieux structurer le projet :

Cliquez sur le bouton "Next". L'tape suivante propose de modifier les valeurs par dfaut pour les types de donnes correspondant la relation 1-N entre les deux tables :

Laissez tout par dfaut et cliquez sur le bouton Finish. Netbeans va un peu travailler puis vous verrez trois nouvelles choses dans le projet : 1. Les classes correspondant aux deux tables, dans le package "entities", regardez donc quoi elles ressemblent... 2. Un fichier persistence.xml sous "configuration", qui correspond la dfinition d'une "persistence unit" par dfaut. En ralit la persistence unit est l'objet qui va s'occuper de manipuler les donnes relationnelles travers les classes et instances des entits. C'est la persistence unit qui connait la base, qui gnre le SQL, qui va syncrhroniser les objets en mmoire et les donnes dans les tables, etc. Double cliquez sur le fichier persistence.xml, vous devriez voir un formulaire de configuration (une "vue" sur le fichier xml, en ralit) :

Sans entrer dans les dtails, nous voyons ici que : 1. On autorise pas la cration de nouvelles tables ni la suppression (None) 2. On va utiliser les transactions JTA (Java Transaction API) lors de l'accs aux donnes, 3. Cette persistence Unit va grer les accs BD pour toutes les classes entits du projet.

Cration d'un Stateless Session Bean CustomerManager pour la gestion des clients
On va centraliser la gestion des Customers (clients, en franais) dans un Stateless Session Bean. De manire classique on cre une "faade" pour les oprations lmentaires sur les clients : cration, suppression, recherche, modification. Faire clic droit New/Session Bean sur le projet EJB (celui prcd par un grain de caf) :

Donnez un nom votre gestionnaire de client, et indiquez que vous le voulez dans le package "sessions" qui contiendra les session beans. On ne va pas prciser d'interface (locale ou remote) pour le moment (on verra plus tard quoi cela peut servir) :

Ajout de mthodes mtier dans le session bean CustomerManager


Double cliquez sur "CustomerManager.java" dans le projet pour voir le source dans l'diteur. Cliquez ensuite dans le source de la classe, entre les { et } de la classe et faites clic droit/insert code (ou Alt+Insert). Une fentre propose alors diverses options, choisissez "add business method" :

Un menu apparait proposant diverses options de gnration automatique de code, choisir "business mthod" puis crez une mthode getAllCustomers qui renvoie une collection de Customer. Cette mthode servira afficher la liste des clients :

Dans le source, ajoutez les imports qui vont bien pour ne plus avoir d'erreurs (un par un: en cliquant sur la petite lampe jaune, ou tous d'un coup: en faisant clic droit/fix imports, ou Alt-Shift-I, ou -Shift-I pour les Mac). Ajoutez de la mme manire une mthode "update" qui prend en paramtre un Customer. Cette mthode servira mettre jour un client.

Vous devriez avoir un code source comme celui-ci :


view plainprint?

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

package session; import entities.Customer; import java.util.Collection; import javax.ejb.Stateless; import javax.ejb.LocalBean;

@Stateless @LocalBean

10. public class CustomerManager { 11. 12. 13. 14. } public List<Customer> getAllCustomers() { return null;

15. 16. 17. 18. 19. 20. } } public Customer update(Customer customer) { return null;

Bien sr vous auriez pu aussi bien taper les mthodes la main... C'est juste pour vous montrer quelques trucs avec netbeans... Maitenant nous allons modifier le contenu de ces mthodes. Les explications sont dans le cours. On va commencer par ajouter un "PersistenceContext" l'application, c'est une variable qui correspond la persistence unit et qui va nous servir envoyer des requtes, insrer/supprimer des objets ou faire des mises jour. Faites clic droit dans le source/insert code/Use Entity Manager. Cela va ajouter automatiquement dans le code une variable avec une annotation, et une mthode :
view plainprint?

1. 2. 3. 4. 5.

@PersistenceContext(unitName = "TP1CustomerApplication-ejbPU") private EntityManager em; public void persist(Object object) { em.persist(object); }

La variable em n'a pas besoin d'tre initialise, en ralit son code d'initialisation va tre "inject" par l'annotation. Regardez le nom "TP1CustomerManager-ejbPU", on le retrouve dans le fichier persistence.xml, en gros, ce que l'on doit comprendre par ces deux lignes : 1. La variable em correspond une persistence unit (celle dont le nom est prcis, qui gre la base jdbc/sample), 2. Je n'ai pas besoin de l'initialiser. On va maintenant modifier le code des deux mthodes pour qu'elles jouent leur rle :
view plainprint?

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

package session; import entities.Customer; import java.util.Collection; import javax.ejb.Stateless; import javax.ejb.LocalBean; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext;

10. @Stateless 11. @LocalBean

12. public class CustomerManager { 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. } } public void persist(Object object) { em.persist(object); } public Customer update(Customer customer) { return em.merge(customer); } public List<Customer> getAllCustomers() { Query query = em.createNamedQuery("Customer.findAll"); return query.getResultList(); @PersistenceContext private EntityManager em;

Voil, on a rempli les deux mthodes avec pour la premire un code qui va excuter une requte JPQL dont le nom est "findAll" et qui est dfinie dans la classe Customer.java (allez voir, la requte correspond un select * sur les clients), la seconde, la mthode update indique que l'on souhaite modifier un client dans la table avec les valeurs de celui qui est pass en paramtre. Cela suppose que l'attribut id du client pass en paramtre correspond une cl primaire dans la table des clients. Normalement vous devez ajouter un import pour enlever l'erreur de syntaxe sur le type Query. Attention, un pige classique est d'ajouter le mauvais import. Faites clic droit/fix import :

Voil, on a termin pour la partie "mtier"/EJB dans ce projet. On va passer au front-end web.

Cration de la partie front-end web


Dans cette partie on va utiliser le framework MVC de java EE6 qui se base sur l'utilisation de backing beans et de pages JSF2.

Ajout du Framework Java Server Faces (JSF2) au projet web


Faites clic droit/properties sur le projet web (celui propos par un globe terrestre bleu) et ajoutez le framework Java Server Faces au projet :

Vrifiez bien que JSF 2.0 est bien le choix par dfaut (JSF 2.1 convient aussi) puis cliquez OK :

Cration d'un Bean Manag en tant que "contrleur web"


Faites sur le projet web clic droit/new/other et choisissez dans la catgorie "java server faces", "jsf managed bean" :

Ensuite, renseignez le nom de la classe que vous souhaitez ainsi que le package o va se trouver la classe gnre (Attention, pas de majuscule dans le nom de package cause d'un bug de NetBeans):

Si tout va bien vous devriez obtenir une classe CustomerMBean.java dans le package "managedbeans" , et le source devrait tre celui-ci :
view plainprint?

1. 2. 3.

package managedbeans; import javax.inject.Named;

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

import javax.enterprise.context.SessionScoped; import java.io.Serializable;

@Named(value = "customerMBean") @SessionScoped public class CustomerMBean implements Serializable {

public CustomerMBean() { }

13. }

Notez l'annotation @SessionScoped qui indique clairement que les instances de cette classe seront places dans la session HTTP. Si vous n'avez pas cette annotation, c'est que vous avez d oublier de la spcifier dans le wizard de gnration, ce n'est pas grave, vous pouvez la rajouter la main ou utiliser la compltion automatique (ctrl-espace). Nous allons maintenant rajouter de la fonctionnalit dans ce bean : 1. Le prparer pour qu'il puisse communiquer avec le session bean stateless de l'autre projet (celui avec les EJBs) : CustomerManager, 2. Ajouter un attribut correspondant un customer dans la session, utile pour afficher dans une page web les dtails d'un clients, ou encore lors de la mise jour d'un client partir d'un formulaire, 3. Ajouter des mthodes de contrle qui seront appeles depuis une page web (une page JSF en fait). Pour que le bean soit client d'un EJB, faites dans le code clic droit/insert code/call enterprise bean, et slectionnez l'EJB CustomerManager, ceci devrait insrer dans le source les lignes suivantes :
view plainprint?

1. 2.

@EJB private CustomerManager customerManager;

L aussi, l'annotation @EJB injectera le code d'initialisation, vous n'avez pas faire de "new" (vous ne DEVEZ PAS faire de "new" !) sur des session beans. Voir le cours pour plus de dtails sur ce mcanisme. Maitenant, compltons le code du bean de manire ce qu'il ressemble ceci (on a rajout des properties et action handlers utiliss par les pages JSF par la suite, vous allez comprendre) :
view plainprint?

1. 2. 3. 4. 5.

package managedbeans; import entities.Customer; import javax.ejb.EJB; import javax.inject.Named;

6. 7. 8. 9. 10.

import javax.enterprise.context.SessionScoped; import java.io.Serializable; import java.util.List; import session.CustomerManager;

11. @Named(value = "customerMBean") 12. @SessionScoped 13. public class CustomerMBean implements Serializable { 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. } /** * Renvoie les dtails du client courant (celui dans l'attribut customer de * cette classe), qu'on appelle une proprit (property) * @return */ public Customer getDetails() { return customer; } /** * Renvoie la liste des clients pour affichage dans une DataTable * @return */ public List<Customer>getCustomers() { return customerManager.getAllCustomers(); public CustomerMBean() { } @EJB private CustomerManager customerManager; /* Client courant dans la session, utilis pour afficher ses dtails ou * pour faire une mise jour du client modifi dans la base */ private Customer customer;

40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. } } /** * Action handler - renvoie vers la page qui affiche la liste des clients * @return */ public String list() { System.out.println("###LIST###"); return "CustomerList"; } /** * Action handler - met jour la base de donnes en fonction du client * pass en paramtres * @return */ public String update() { System.out.println("###UPDATE###"); customer = customerManager.update(customer); return "CustomerList"; } /** * Action handler - appel lorsque l'utilisateur slectionne une ligne dans * la DataTable pour voir les dtails * @param customer * @return */ public String showDetails(Customer customer) { this.customer = customer; return "CustomerDetails";

Ajout d'une page JSF pour afficher la liste des clients

Pour ajouter une page JSF, faire sur le projet web clic droit/new/other/Java Server Faces et choisir "JSF Page" :

Puis faites "next" et renseignez le nom de la page :

Notez que cela ajoute un fichier CustomerList.xhtml dans le projet, ct de la page index.jsp. Double cliquez pour voir le source. Modifiez le Title pour mettre "Liste des clients", vous pouvez rajouter un titre HTML <h1>liste des clients </h1> dans le <h:body>...</h:body> etc.

Ajout d'une DataTable JSF dans la page


Faites apparaitre la Palette dans netbeans (menu Window/Palette ou raccourci ctrl-shift-8). Ouvrez la partie JSF puis faite un drag'n'drop de JSF Data Table from Entity, en dessous du body. Le <h1>... prsent dans la photo d'cran est inutile, car le code que va gnrer netbeans en contiendra un...

Une fentre de dialogue va apparaitre demandant de prciser pour quelle classe entit vous voulez une Data Table, indiquez la classe entit Customer, puis comme property indiquez le nom du managed bean pour les clients suivi du nom de la proprit correspondant une liste de clients. Dans notre cas, il faut mettre customerMbean.customers. Pourquoi ? Car lorsque dans une JSP ou dans une page JSF on rfrencera une "proprit" de lecture, cela appellera la mthode get correspondante. Par exemple, si on rfrence la proprit "customers" du managed bean customerMBean, cela ne fonctionnera que si il existe une mthode getCustomers() qui renvoie une Collection de Customer (en effet, la Data Table gre des Collection). Il n'est pas ncessaire qu'une variable "customers" existe dans le bean, il suffit que la mthode Collection<Customer> getCustomers() existe. La preuve :

view plainprint?

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

@Named(value = "customerMBean") @SessionScoped public class CustomerMBean implements Serializable { ... public Collection getCustomers() { return customerManager.getAllCustomers(); }

Voici donc la fentre avec les bonnes valeurs :

Ceci devrait insrer de nombreuses lignes dans la page JSF. Avec netbeans 7.0.1 il faut rajouter aussi le namespace xmlns:f="http://java.sun.com/jsf/core" au tag html, vous pouvez faire ceci semi-automatiquement en utilisant l'autocompltion dans NetBeans: placez-vous l'intrieur d'un des tags du namespace f, puis faites ctrl+espace

Excution du projet et premier test


Pour excuter le projet, faites clic droit sur le projet en forme de triangle (le projet "enterprise") et Run. Une page va s'afficher, modifiez l'URL pour afficher la bonne page : 1. Ajoutez "faces" dans l'URL, 2. Indiquez le nom de la page JSF avec son suffixe .xhtml Vous devriez obtenir le rsultat suivant :

Pour le moment ce tableau n'est pas trs bien prsent car il n'y a aucune fioriture de mise en page. Les donnes proviennent de la base jdbc/sample. Vous pouvez vrifier que ces donnes sont les bonnes, allez dans l'onglet "Services" de netbeans qui comprend un gestionnaire de base de donnes assez simple, mais trs pratique. Ouvrez la vue sur les tables de la base jdbc/sample et faites clic droit/view data :

Vous pouvez vrifier que ce sont bien les mmes donnes qui ont t affiches dans la page JSF. Maintenant on va modifier l'affichage du discountCode qui est une relation, en effet, voir "entities.DiscountCode[discountCode=M]" n'est pas trs satisfaisant. Si vous avez compris le concept de "proprits", vous pouvez modifier la ligne qui affiche le code. remplacez :
view plainprint?

1.

<h:outputText value="#{item.discountCode}"/>

par :
view plainprint?

1.

<h:outputText value="#{item.discountCode.discountCode} : #{item.discountCode.rate} %" />

Ce qui aura pour effet de remplacer la premire expression par le rsultat de l'appel de la mthode getDiscountCode() de la classe DiscountCode.java, et la seconde par la valeur renvoye par getRate() de cette mme classe. L'affichage sera bien meilleur :

Etudiez maintenant le code de la page JSF, du managed bean, du session bean de l'entity bean, et essayez de retrouver les morceaux que vous avez dvelopps dans le schma prsent dans la toute premire illustration de cette page. Regardez galement comment s'articulent les diffrentes parties et dans quel ordre elles sont excutes.

Remplacement de la DataTable par une provenant de la librairie de composants JSF PrimeFaces


Ajout de la librairie PrimeFaces dans le projet
PrimeFaces propose des composants volus/complmentaires pour JSF2, le site web de rfrence est : http://www.primefaces.org Cette librairie est dj prsente dans netbeans 7.2, dans sa dernire version 3.2. Il suffit de l'ajouter au projet pour pouvoir l'utiliser : faites sur le projet web (celui prcd d'une icne en forme de globe terrestre) clic droit/properties, puis allez dans "Librairies", cliquez sur "Add library", selectionnez Primefaces 3.2.

Modification de la page JSF


Pour pouvoir utiliser des tags provenant de PrimeFaces dans une page JSF il faut ajouter le namespace suivant : (cfhttp://primefaces.org/gettingStarted.html)
view plainprint?

1.

xmlnsxmlns:p=xmlns:p="http://primefaces.org/ui"

A partir de l on pourra utiliser des tags PrimeFaces avec le prfixe p: Remplacez simplement tous les tags <h:dataTable> et </h:dataTable> par <p:dataTable> et </p:dataTable> Idem pour les tags <h:column> et </h:column>, remplacez les h: par p:

Dployez le projet (clic droit sur le projet avec le triangle/deploy) puis excutez nouveau la page qui affiche la liste des clients. Il est ncessaire de dployer car il faut que la librairie que nous venons d'ajouter au projet soit dploye dans le serveur. Il suffit de le faire une fois pour le projet, ensuite le dploiement incrmental (avec save ou ctrl-s) suffira. Vous devriez obtenir le rsultat suivant :

C'est dj mieux prsent non ? Bon, ce qui est intressant, c'est que le composant dataTable de PrimeFaces possde de nombreuses options pour la pagination, rendre les colonnes triables, ditables, etc. Allez donc voir les dmos/sources sur : http://www.primefaces.org/showcase-labs/ui/home.jsf 1. Par exemple, ajoutez dans le tag <p:dataTable ...> les attributs paginator="true" et rows="10", sauvez, rechargez la page, a y est, les rsultats sont pagins (en mmoire, nous verrons plus tard comment faire des requtes pagines sur la BD), 2. Ensuite, faites en sorte que les colonnes soient triables: attribut sortBy="#{item.propritDeLaColonne}" dans chaque tag p:column 3. Enfin, pour installer quelques filtres et champs de recherche sur la table (Attention, un bug dans la dmo, il faut l'attribut widgetVar="customerTable" o customerTable sera l'identifiant de la table du ct client (en javascript, c'est customerTable.filter() qu'on

doit mettre dans l'attribut onkeyup du tag p:inputText pour le champs de recherche global. 4. 1 point pour celui qui a vu le bug de PrimeFaces qui fait que les colonnes sont triables seulement pendant qu'on effectue un filtrage...

Affichage des dtails d'un client lorsqu'on clique sur une ligne
Maintenant nous allons voir comment afficher dans une autre page les dtails d'un client lorsqu'on clique sur une ligne.

Ajout d'un lien dans le tableau pour dclencher l'affichage des dtails d'un client
Modifiez la page CustomerList.xhtml de manire ce que lorsqu'on clique sur la colonne Id on affiche le dtail d'un client, comme la ligne 18 du listing ci-dessous.
view plainprint?

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

<p:dataTable value="#{customerMBean.customers}" var="item" emptyMessage="No customer found with given criteria" widgetVar="customerTable" paginator="true" rows="10">

<f:facet name="header"> <p:outputPanel> <h:outputText value="Search all fields:" /> <p:inputText id="globalFilter" onkeyup="customerTable.filter()" style="width:150px " /> </p:outputPanel>

10. 11.

12. </f:facet> 13. 14. <p:column headerText="CustomerId" 15. 16. 17. 18. sortBy="#{item.customerId}" filterBy="#{item.customerId}" filterMatchMode="contains"> <h:commandLink action="#{customerMBean.showDetails(item)}" value="#{item.cust omerId}"/>

19. </p:column> 20. ...

la ligne ajoute indique que lorsqu'on clique sur la colonne, on appelle la mthode showDetails du managedBean (n'oubliez pas : le managed bean est dans la session), et que l'on affiche dans le texte du lien l'id du client (attribut "value=..."). Dans ce cas, lorqu'on clique sur la colonne, on appelle la mthode d'instance showDetails avec en paramtre l'objet item qui correspond au client de la ligne courante. Si vous regardez le source de la mthode showDetails de CustomerMBean.java :
view plainprint?

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

/**

* Action handler - appel lorsque l'utilisateur slectionne une ligne dans * la DataTable pour voir les dtails * @param customer * @return */ public String showDetails(Customer customer) { this.customer = customer; return "CustomerDetails"; }

10.

vous voyez que le paramtre attendu est un client, et que cette mthode fait deux choses : 1. Elle initialise la variable customer (qui est dans la session, remplie maintenant par le paramtre pass), 2. Elle renvoie vers la page CustomerDetails.xhtml, qui pourra accder la variable de session "customer" pour en afficher les dtails. Si vous sauvegardez la page JSF et l'excutez, vous verrez que la colonne id contient maintenant un lien hypertexte. Pour le moment il ne mne rien car la page qui affiche les dtails n'existe pas encore.

Ajout d'une page JSF pour afficher les dtails d'un client
1. Faites sur le projet web clic droit/new/jsf page et crez une page CustomerDetails.xhtml, 2. Drag and droppez dans le body de la page "jsf form from entity".

Indiquez ensuite le nom de la classe entit dont vous voulez afficher les informations dans un formulaire, ainsi que le nom de la mthode du managedBean qui renvoie les dtails d'un client :

Ici on a mis customerMBean.details qui signifie que c'est la mthode getDetails() qui sera appele. On aurait pu aussi mettre customerMBean.customer qui aurait appel getCustomer(), qui fait la mme chose. Une fois qu'on a valid, la page JSF se remplit avec des lignes qui sont en fait un formulaire qui sera pr-rempli ds l'affichage si il existe un objet "customer" dans la session (ce qui sera le cas, voir ce que nous avons expliqu dans la section prcdente). Il est encore un peu tt pour que tout fonctionne. Il reste maintenant spcifier la navigation entre la page qui affiche la liste des clients et la page qui affiche le dtail d'un client.

Executez le projet et testez que les dtails s'affichent bien


Maintenant vous pouvez excuter le projet et voir si lorsque vous cliquez sur le lien Id dans la liste a affiche bien le dtail d'un client. Vous devriez obtenir ceci pour le client d'id=1 :

Voil, les dtails du client numro 1 s'affichent bien, cependant le code de discount pour ce client n'est pas correctement affich. Nous allons voir maintenant comment arranger ce petit problme, comment ajouter des boutons de navigation et comment tant qu' faire permettre une modification des donnes.

Rgler l'affichage des DiscountCodes dans le dtail d'un client


Dans la base de donnes la table des DISCOUNT_CODE contient juste 4 valeurs correspondants aux quatre types de rductions possibles. Nous allons utiliser un

Converter, qui permet de transformer un objet de type entities.DiscountCode en String, et vice versa. On va commencer par rajouter au projet EJB un session bean stateless dans lequel on ajoutera une mthode List<DiscountCode> getAllDiscountCodes(). Inspirez-vous du gestionnaire de client et de la mthode qui renvoie tous les clients. Plutt que copier/coller le code ci-dessous, essayez de refaire les tapes que nous avions faites lors de l'criture du gestionnaire de clients (avec insert code/call enterprise bean, insert code/user Entity Manager etc...) Vous devriez avoir un code comme ceci :
view plainprint?

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

package session; import entities.DiscountCode; import java.util.List; import javax.ejb.Stateless; import javax.ejb.LocalBean; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query;

10. 11. @Stateless 12. @LocalBean 13. public class DiscountCodeManager { 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. } } public void persist(Object object) { em.persist(object); } public List<DiscountCode> getDiscountCodes() { Query query = em.createNamedQuery("DiscountCode.findAll"); return query.getResultList(); @PersistenceContext(unitName = "TP1CustomerApplication-ejbPU") private EntityManager em;

Donc, maintenant on a un EJB qui renvoie toutes les valeurs possibles de DiscountCode. On va ajouter dans le managed bean du projet web un objet de type javax.faces.convert.Converter, qui nous servira transformer un objet de type entities.DiscountCode en String, et vice versa. On utilisera une reprsentation en string de la forme "L : 7.00%", comme dans la colonne du tableau de dtail des clients. Pour cel, commencez par indiquer au bean qu'il va appeler un EJB : DiscountCodeManager, celui que nous venons d'crire. Ensuite, pour utiliser ce converter dans la page JSF .xhtml, on a besoin de dfinir une mthode get. Voici le code ajouter dans le managed bean CustomerMBean:
view plainprint?

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

@EJB private DiscountCodeManager discountCodeManager;

public Converter getDiscountCodeConverter() { return discountCodeConverter; }

private Converter discountCodeConverter = new Converter() {

10. 11. 12. 13. 14. 15. { 16. 17. 18. 19. };

@Override public Object getAsObject(FacesContext context, UIComponent component, String value ){ return new ConverterException("On verra la conversion String->Objet plus tard..."); } @Override public String getAsString(FacesContext context, UIComponent component, Object value) DiscountCode dc = (DiscountCode) value; return dc.getDiscountCode()+" : "+dc.getRate()+"%"; }

Finalement, on peut dfinir une mthode utilitaire qui servira remplir le menu droulant de la page JSF CustomerDetails.xhtml. Procdez comme dans le dbut du TP (insert code/call enterprise bean), puis insrez dans le bean la mthode suivante :
view plainprint?

1. 2. 3. 4. 5.

/** * renvoie un tableau de SelectItem pour affichage dans le menu droulant * de la page des dtails d'un client * @return */

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

public List<SelectItem> getAllDiscountCodes() { List<SelectItem> options = new ArrayList<SelectItem>(); List<DiscountCode> ldc = discountCodeManager.getDiscountCodes(); for(DiscountCode dc : ldc) { options.add(new SelectItem(dc,discountCodeConverter.getAsString( null, null, dc))); } return options;

13. }

Si vous regardez ce code, il se contente de rcuprer la liste des DiscountCodes et de la transformer en liste de SelectItems qui est une classe JSF utilise par le composant <f:selecteditems ... /> qui indique le contenu du menu <h:selectOneMenu.../> situ dans la page CustomerDetail.xhtml. Chaque SelectItem prends en paramtre lors de la construction un objet (l'objet de type DiscountCode) et une string qui le dcrit (formate par le Converter, qui sera visible par l'utilisateur). On va pouvoir maintenant modifier la partie "menu" de la page CustomerDetails.xhtml afin que la mthode que nous venons d'crire soit appele. Voici le code qu'il faut modifier dans CustomerDetails.xhtml :
view plainprint?

1. 2. 3. 4. 5.

<h:selectOneMenu id="discountCode" value="#{customerMBean.details.discountCode}" title="DiscountCode" required="true" requiredMessage="The DiscountCode field is required." converter="#{customerMBean.discountCodeConverter}"> <f:selectItems value="#{customerMBean.allDiscountCodes}" /> </h:selectOneMenu>

Remarquons plusieurs choses : 1. Le contenu du menu est invoqu par value="#{customerMBean.allDiscountCodes}" du tag f:selectItems, cette expression (il s'agit du langage EL de JSF) correspond un appel getDiscountCodes() sur le bean de nom customerMBean, c'est la mthode que l'on vient d'crire. 2. L'attribut qui correspond la proprit du "modle associ" au menu, c'est dire la proprit dont la valeur sera affiche dans le menu comme choix par dfaut (appel au getter), mais c'est aussi la proprit qui sera modifie (par un appel son setter) lorsque la valeur sera modifie par un nouveau choix dans le menu. Comme les menus renvoient un e String lorsqu'on leur demande la valeur choisie, c'est le Converter qui se charge de transformer un Objet en string et vice-versa. Ici: 1. le modle associ au menu est spcifi par l'attribut value="#{customerMBean.details.discountCode}" du tag h:selectOneMenu (appel de la mthode getDiscountCode() de l'objet renvoy par la mthode getDetails() du customerMBean 2. l'instance de Converter utiliser est dfinie par l'attribut converter="#{customerMBean.discountCodeConverter}"du tag

h:selectOneMenu (appel de la mthode getDiscountCodeConverter() de du customerMBean. On peut utiliser le Converter dans la page JSF CustomerList.xhtml galement :
view plainprint?

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

<p:column headerText="DiscountCode" sortBy="#{item.discountCode.discountCode}" filterBy="#{item.discountCode.rate}%" filterMatchMode="contains"> <h:outputText value="#{item.discountCode}" converter="#{customerMBean.discoun tCodeConverter}"/> </p:column>

Voil, sauvegardez, testez, a doit fonctionner :

Voil ! Maintenant il ne reste plus qu' ajouter des boutons de navigation pour mettre jour les donnes ou revenir la liste des clients.

Ajout de boutons pour la mise jour et retour la liste

Il suffit de rajouter deux lignes dans le fichier CustomerDetails.xhtml ! Ajoutez ces deux lignes :
view plainprint?

1. 2.

<h:commandButton id="back" value="Back" action="#{customerMBean.list}"/> <h:commandButton id="update" value="Update" action="#{customerMBean.update}"/ >

juste aprs le menu droulant pour les discountCodes... Vous comprenez maintenant ce que cela signifie : Si on clique sur le premier bouton, on appelle list() dans CustomerMBean.java, Si on clique sur le second bouton on appelle update()... Regardons la version actuelle de la mthode update() :
view plainprint?

1. 2. 3. 4. 5.

public String update() { System.out.println("###UPDATE###"); customer = customerManager.update(customer); return "CustomerList"; }

Cette mthode prend la proprit "customer", de type Customer et appelle une mthode mtier dans le gestionnaire de clients qui va mettre jour le client en base. Les attributs de cette proprit (nom, etc...) ont pour valeurs celles du formulaire de la page de dtails. Si on a modifi ces valeurs avant de cliquer sur le bouton "update", les attributs de la proprit ont t modifis. Donc on a bien dans "customer" un client mis jour (en mmoire pour le moment, regardez ce que fait customerManager.update() pour voir. On verra en cours que c'est l que se fait la modification en base de donnes). Il existe en effet une relation entre les clients et les codes de remise. Regardez attentivement la classe entit Customer.java, vous verrez que le code de remise est un attribut discountCode de type DiscountCode. Or le menu droulant dans la page de dtails, lorsqu'on modifie le code de remise, nous renvoie une String. C'est le travail du Converter de transformer cette string en objet de type DiscountCode !

Observez la requte nomme DiscountCode.findByDiscountCode dfinie dans un attribut de la classe bean entit DiscountCode. Cette requte possde un paramtre not :discountCode. Quand on donne la valeur d'un code de rduction ce paramtre, on peut rcuprer l'objet de type DiscountCode correspondant. On propose d'utiliser une nouvelle mthode public DiscountCode getDiscountCodeByDiscountCode(char code) dans le bean session session.DiscountCodeManager. Voici cette mthode:
view plainprint?

1. 2. 3. 4. 5.

public DiscountCode getDiscountCodeByDiscountCode(char code) { Query query = em.createNamedQuery("DiscountCode.findByDiscountCode"); query.setParameter("discountCode", code); return (DiscountCode) query.getSingleResult(); }

Observez: on associe le caractre code au paramtre de requte "discountCode", et on rcupre un unique rsultat de type DiscountCode: query.getSingleResult(). Voici finalement ce qu'on vous propose comme transformation String->DiscountCode par le Converter:
view plainprint?

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

private Converter discountCodeConverter = new Converter() { @Override public Object getAsObject(FacesContext context, UIComponent component, String val ue) { char code = value.charAt(0); DiscountCode dc = discountCodeManager.getDiscountCodeByDiscountCode(code); return dc; } @Override public String getAsString(FacesContext context, UIComponent component, Object val ue) { DiscountCode dc = (DiscountCode) value; return dc.getDiscountCode()+" : "+dc.getRate()+"%"; } };

Excutez, testez, amusez-vous modifier les valeurs des clients, vrifiez dans le gestionnaire de base de donnes de netbeans que les modifications ont t prises en compte...

Problmes devant encore tre rgls :


Si on ouvre deux listes dans deux onglets diffrents, et qu'on modifie deux clients en mme temps, que peut-il arriver ? Pourquoi ? Les URLs ont tous un temps de retard, la page de dtails affiche l'URL de la liste, etc. Pourquoi ? Nous verrons en cours comment corriger ce problme. Ajoutez une trace dans la mthode getCustomers() de CustomerMBean.java, affichez la liste des clients, la mthode est appele plusieurs fois. Proposez une optimisation. Attention, cela peut provoquer d'autres problmes...

Correction de ce TP utilisant diverses amliorations vues en cours, et corrigeant la plupart des problmes voqus prcdemment

L'archive de la correction: TP1CustomerApplication.zip

Cette correction fait diverses choses : 1. Elle "cache" la liste des customers qui doivent tre affichs dans la table. Vous avez pu remarquer en mettant un println dans la fonction getCustomers() qu'elle tait appele plusieurs fois. Requter dans cette mthode est non seulement peu performant (on va requter plusieurs fois) mais comme l'ordre des lments retourns peut changer, cela "casse" les fonctionalits de tri et de filtres de la datatable Primefaces.

2. Dans la page jsf CustomerList.xhtml on a rajout un lien ou un bouton "refresh" qui appelle la mthode refresh... dans le backing bean, qui force ma mise jour du modle en faisant un appel la bd. 3. Pour que le bouton "reload" du navigateur fasse la mme chose, on a utilis un "preRenderView" listener qui appelle cette mme mthode refresh. 4. Dans le formulaire, on a mis des f:viewParam pour rcuprer un paramtre http nomm id, pass dans l'url. Cette ligne de gestion des viewParam appelle la mthode setId() du backing bean. Ainsi, lorque la page est invoque avec un paramtre ?id=1 on va initialiser une proprit/modle de nom id et de valeur 1 dans le backing bean. 5. On a mis un preRenderViewListener dans la page du formulaire, qui appelle une mthode du backing bean pour qui va chercher le Customer qui a pour id la valeur initialise dans l'tape prcdente. On initialise donc le modle utilis par les champs du formulaire -> le formulaire sera pr-rempli lors du rendu ! 6. Du coup on a un formulaire autonome !!!! On peut copier/coller son URL et l'envoyer par mail, le lien affichera toujours un formulaire pr-rempli 7. On a mis ?faces-redirect=true partout o on fait de la navigation -> ceci force le mode de navigation Post-Redirect-Get (PRG) ce qui permet d'avoir toujours les bons URLs affichs, ceux correspondant la page affiche. 8. On a modifi les boutons dans la premire colonne pour que ce soient juste des liens statiques, et non des liens ou boutons appelant des mthodes de controle dans le backing bean. 9. J'ai aussi simplifi la manire dont on affiche la liste en utilisant une mthode similaire aux datatables, avec un attribut var=... regardez donc la partie selectItems et la methode getAllDiscountCodes dans le backing bean.

Pour la semaine prochaine:


Inspirez-vous de l'article d'introduction JSF 2.0 du site de Netbeans http://netbeans.org/kb/docs/web/jsf20-intro.html (Cet article contient plein de trucs pour dvelopper plus rapidement et plus efficacement sur NetBeans !!!) pour utiliser une facelet pour rendre le site ergonomique (un en-tte avec le titre du TP (statique) et le titre de chaque page mis en forme, et une colonne de gauche qu'on utilisera plus-tard pour mettre les liens de navigation). Mots cls:
Attacher fichier ou image

Fichiers (2)

Fichier

Taille

Date

Attach par

primefaces-3.0.M3.jar

1967.79 Ko

17:29, 19 Sep 2011

Max

Actions

Aucune description

TP1CustomerApplication.zip
Aucune description

59.34 Ko

08:51, 13 Dc 2012

MichelBuffa

Actions

Images (39) prcdent suivant Voir 1 - 6 sur 39 images | Voir tout

Aucune description

Snap3.jpg Actions

Aucune description Snap28 (2... Actions

Aucune description

Snap23.jpg Actions

Aucune description

Snap40.jpg Actions

Aucune description

Snap39.jpg Actions

Aucune description

Snap38.jpg Actions