Création d'une application de type CRUD avec JSF et JPA
Date de publication : 3 Mars 2008
Par
Jawher Moussa (Accueil)
Cet article a pour objectif de vous présenter la tâche de
création d'une application complète de type CRUD (Create, Read, Update, Delete) permettant
d'effectuer les opérations de création, consultation,
suppression et modification d'une entité et ce en utilisant
le framework web JSF et le framework de persistance JPA.
I. Introduction
II. Squelette de l'application
II-A. Configuration de JSF
II-B. Configuration de JPA
II-C. Couche métier
II-C-1. Entité
II-C-2. DAO
II-D. Couche contrôle
III. Implémenter l'opération Read
III-A. Dans la couche DAO
III-B. Dans la couche Control
III-C. Dans la couche View
III-D. Test
IV. Implémenter l'opération Create
IV-A. Dans la couche DAO
IV-B. Dans la couche Control
IV-C. Dans la couche View
IV-D. Test
V. Implémenter l'opération Delete
V-A. Dans la couche DAO
V-B. Dans la couche Control
V-C. Dans la couche View
V-D. Test
VI. Implémenter l'opération Update
VI-A. Dans la couche DAO
VI-B. Dans la couche Control
VI-C. Dans la couche View
VI-D. Test
VII. Télécharger
VIII. Conclusion
IX. Remerciements
I. Introduction
Une application CRUD permet d'effectuer les opérations
de listing, ajout, modification et suppression sur une entité donnée.
Ce cas d'utilisation est si fréquent dans le développement logiciel qu'il est
rare de trouver une application qui ne fasse pas du CRUD.
La mise en place d'une telle application nécessite de pouvoir effectuer les opérations
CRUD sur une source de données (Base de données, fichier plat, etc.) et de fournir une
interface graphique (client lourd ou web, etc.) pour réaliser ces opérations.
Le but de cet article est donc de présenter et expliquer la création d'une
application web Java permettant de faire du
CRUD
sur une seule entité en utilisant
JSF
comme framework de présentation et
JPA comme framework de persistance.
II. Squelette de l'application
Il s'agit d'une application web Java. Si vous utilisez
un
EDI
tel qu'
Eclipse ou
NetBeans, il est possible de
générer automatiquement la structure d'une application
web JSF grâce aux assistants de ces
EDIs.
 |
Eclipse
prend en charge le développement d'applications JSF via le pack Web Tools Project 2.
Les étapes de configuration d'Eclipse et de la création d'un projet JSF sont présentées sous
forme de démo Flash dans
cet article.
|
II-A. Configuration de JSF
Ceci revient à:
-
Ajouter une implémentation JSF au classpath.
Dans l'exemple fourni avec cet article, J'utilise la Sun Reference
Implementation 1.2.6
téléchargeable ici.
Cette version supporte JSF 1.2.
-
Déclarer la Faces Servlet dans web.xml et
l'associer à *.jsf. Cette servlet
joue le rôle de la Front Servlet du
MVC/Model 2.
-
Ajouter le fichier faces-config.xml qui permet de configurer l'application JSF
(déclaration des managed-beans/controllers, déclaration des règles de navigation, etc.)
Après avoir déclaré la Faces Servlet ainsi que son affectation aux urls de type *.jsf,
voici ce que devient web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>jsf-crud</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
</web-app>
|
Et voici le fichier faces-config.xml (vide pour l'instant,
mais il sera mis à jour au fur et à mesure de l'ajout de nouvelles fonctionnalités à l'application):
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
version="1.2">
</faces-config>
|
II-B. Configuration de JPA
La configuration de JPA consiste à:
-
Ajouter une implémentation JPA au classpath.
Dans le cadre de cet article, j'utilise Toplink 2.41 comme implémentation
qui est
téléchargeable ici.
-
Créer le descripteur de persistance qui sert à spécifier
les paramètres de connexion à la base de données
(url de connexion, login, mot de passe, etc.)
ainsi qu'à la déclaration des classes persistantes.
J'utilise Toplink comme implémentation JPA et HSQLDB
comme base de données. Il faut donc mettre
toplink.jar et hsqldb.jar dans le classpath de
l'application.
Il faut ensuite créer un descripteur de persistance
JPA (persistence.xml) dans un dossier META-INF à la
racine du dossier source.
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="jsf-crud">
<properties>
<property name="toplink.logging.level" value="INFO" />
<property name="toplink.target-database" value="HSQL" />
<property name="toplink.jdbc.driver"
value="org.hsqldb.jdbcDriver" />
<property name="toplink.jdbc.url"
value="jdbc:hsqldb:file:test" />
<property name="toplink.jdbc.user" value="sa" />
<property name="toplink.ddl-generation"
value="create-tables" />
</properties>
</persistence-unit>
</persistence>
|
Pour l'instant, ce fichier ne contient que le nom de l'unité de persistance
(jsf-crud) ainsi que les paramètres de connexion à la base de données qui sont:
- Pilote JDBC: "org.hsqldb.jdbcDriver"
- URL de connexion: "jdbc:hsqldb:file:test" qui indique
à HSQLDB de stocker la base de données dans un fichier nommé "test".
Il est aussi possible de stocker la base dans la mémoire vive
(RAM) en utilisant un chemin du type "jdbc:hsqldb:mem:test". Mais les données seraient alors perdues à l'arrêt de l'application.
- Login: "sa" (l'équivalent du root dans HSQLDB)
- Pilote JDBC: "org.hsqldb.jdbcDriver"
- toplink.ddl-generation: "create-tables" indique à Toplink de créer les tables si elles n'existent pas.
II-C. Couche métier
L'application qu'on désire développer permet de
faire les opérations de création, modification, suppression et listing sur une seule entité.
Normalement, la
partie Model de l'application devrait être séparée en
deux sous parties: DAO (Data Access Object) pour l'accès aux données et
Service pour faire la logique métier. Mais dans le
cadre de cet article, on se limitera à la couche DAO
sans passer par la couche service vu que l'on n'a pas
de logique métier à proprement parler.
II-C-1. Entité
L'entité sur laquelle on va travailler représente
une personne. On se contentera de deux attributs,
nom et prénom. Voici son code:
package model.dto;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@author<a href="mailto:djo.mos.contact@gmail.com"></a>
@Entity
public class Person {
private Long id;
private String firstName;
private String lastName;
<br />
<br />
@return
@Id
@GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
|
Comme vous le remarquez, cette classe est annotée
avec JPA pour pouvoir être persistée ou retrouvé dans la base de données:
-
L'annotation @Entity sur la classe Person
pour la déclarer comme classe persistante.
Cette annotation est obligatoire pour une classe persistante.
-
L'annotation @Id sur le getter du champ id
pour le déclarer comme l'identifiant.
Cette annotation est obligatoire pour une classe persistante.
-
L'annotation @GeneratedValue sur le getter
du champ id pour déclarer qua sa valeur est
auto générée.
 |
Notez que les champs firstName et lastName ne sont pas annotés.
Ils seront pourtant persistés car dans un souci de simplification,
JPA considère que tout champ d'une classe persistante est
implicitement persistant, à moins qu'il soit annoté avec @Transient.
|
Il faut ensuite déclarer cette classe dans le
descripteur de persistance persistence.xml pour qu'elle soit prise en charge:
<class>model.dto.Person</class>
|
II-C-2. DAO
On va maintenant encapsuler les opérations de
persistance (création, modification, suppression
et lecture) sur l'entité Person dans un DAO
.
package model.dao;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Persistence;
import model.dto.Person;
@author<a href="mailto:djo.mos.contact@gmail.com"></a>
public class PersonDao {
private static final String JPA_UNIT_NAME = "jsf-crud";
private EntityManager entityManager;
protected EntityManager getEntityManager() {
if (entityManager == null) {
entityManager = Persistence.createEntityManagerFactory(
JPA_UNIT_NAME).createEntityManager();
}
return entityManager;
}
}
|
Le DAO qu'on va développer va utiliser l'API de JPA pour
réaliser les opérations de persistance.
Pour pouvoir l'utiliser, il faut d'abord créer une instance de la classe EntityManager.
Pour le faire, on passe par une fabrique (Factory) qu'on récupère via la méthode statique Persistence.createEntityManagerFactory()
(Je sais, encore une autre fabrique :) ) en lui passant comme paramètre le nom de l'unité de persistance (le même déclaré dans persistence.xml):
<persistence-unit name="jsf-crud">
|
On verra plus tard comment utiliser l'EntityManager crée pour effectuer les opérations de persistance sur une entité.
 |
Dans un souci de simplicité, la gestion de l'EntityManager dans cet article est faîte à la main.
Mais dans la pratique, cette approche a de nombreuses limites, et on lègue la gestion de l'EntityManager à un conteneur tel que
le serveur d'application ou encore un conteneur leger comme Spring. Il suffit alors d'indiquer au conteneur d'injecter l'EntityManager crée dans nos DAOs.
|
II-D. Couche contrôle
Il s'agit ici de créer un managed-bean JSF (Controller) qui fera
le lien entre les pages et la couche métier.
package control;
public class PersonCtrl {
}
|
Il faut ensuite déclarer cette classe dans
faces-config.xml pour l'exposer aux pages JSF:
<managed-bean>
<managed-bean-name>personCtrl</managed-bean-name>
<managed-bean-class>control.PersonCtrl</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
|
La déclaration d'un managed-bean JSF prend ces paramètres:
- managed-bean-name: Le nom sous lequel le bean sera accessible depuis les pages.
En général, c'est le nom de la classe avec la première lettre en minuscule (C'est ce qui est fait dans ce cas: personCtrl).
Mais rien ne vous empêche de le nommer comme bon vous semble.
- managed-bean-class: Le nom complet de la classe du managed-bean.
- managed-bean-scope: La durée de vie du managed-bean (request, session, application, none).
Dans ce cas, je l'ai mis à session car on aura des opérations sur plusieurs requêtes.
Une fois le managed-bean déclaré dans faces-config.xml, il devient alos accessible depuis les pages JSF via l'EL (Expression Language) "#{nomDuBean}".
III. Implémenter l'opération Read
III-A. Dans la couche DAO
Dans PersonDao, on ajoute une méthode selectAll qui
retourne la liste de toutes les personnes depuis la
base de données:
@return
public List<Person> selectAll() {
List<Person> persons = getEntityManager().createQuery(
"select p from Person p").getResultList();
return persons;
}
|
 |
Notez que l'on accède à l'EntityManager via son
getter pour s'assurer qu'il soit crée.
|
Comme cité plus haut, on utilise l'EntityManager pour exécuter une requête sur la base de données.
Notez que cette requête est exprimée en JPA-QL (Java Persistence API Query Language) et non pas en SQL. JPA-SQL est
similaire à SQL mais est orienté Objet.
III-B. Dans la couche Control
On doit ensuite modifier le contrôleur pour offrir
aux pages JSF la possibilité de lister les entités
personnes. Dans la classe PersonCtrl, on ajoute une
liste de personnes (pour stocker les personnes récupérées de la base de données)
ainsi qu'une instance du DAO qu'on a crée (pour pouvoir exécuter les opérations de persistance sur la base de données):
private PersonDao pDao = new PersonDao();
private List<Person> persons;
|
Pour initialiser cette liste, mieux vaut éviter de
le faire dans le constructeur du managed-bean vu que
l'on n'est pas sûr de l'ordre d'initialisation des
différents modules, et qu'il se peut que ce
constructeur soit appelé alors que JPA ne soit pas
encore initialisé. On va donc différer
l'initialisation de cette liste dans son getter, de
cette façon:
public List<Person> getPersons() {
if(persons==null){
persons = pDao.findAll();
}
return persons;
}
|
 |
JSF accède toujours aux champs d'un managed-bean via les accesseurs et les mutateurs.
On est donc sûr de passer par getPersons en la référençant depuis JSF via "#{personCtrl.persons}".
|
III-C. Dans la couche View
On va maintenant afficher la liste des personnes
dans une page JSF dans un format tabulaire. On crée
une page list.jsp avec le contenu suivant:
<f:view>
<h:dataTable border="0" rules="all" value="#{personCtrl.persons}"
var="p">
<h:column>
<f:facet name="header">
<h:outputText value="Prénom" />
</f:facet>
<h:outputText value="#{p.firstName}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Nom" />
</f:facet>
<h:outputText value="#{p.lastName}" />
</h:column>
</h:dataTable>
</f:view>
|
On utilise le composant standard dataTable pour afficher la liste des personnes.
Ce composant prend entre autre les paramètres suivants:
- value: une EL désignant la liste à afficher.
Dans ce cas, on désire afficher la liste des personnes
définies dans le managed-bean PersonCtrl.
- var: dataTable va itérer sur la liste déclarée dans l'attribut value.
A chaque itération, l'élément courant est mis dans une variable
temporaire dont le nom est contrôlé par la valeur de l'attribut var.
- border et rules sont utilisées pour configurer l'affichage (pas de bordure, afficher des lignes autour de chaque cellule)
A l'inverse des autres taglibs (Struts, JSTL, etc.), le modèle de composants de JSF
introduit une abstraction lors de l'itération sur une liste de données en définissant les colonnes au lieu des lignes.
Ici, on définit 2 colonnes:
- Une colonne pour afficher les prénoms des personnes: Ceci est fait via le composant column.
Ce dernier répète son contenu à chaque itération sur la liste définie dans le dataTable parent.
Dans ce cas, le contenu de column qui est le composant outputText (qui permet d'afficher du texte) sera répété pour chaque personne de la liste
des personnes dans une ligne de la table. outputText prend un attribut value qui indique le texte à afficher.
Ici, on a mis l'EL #{p.firstName} où p est la variable d'itération (déclarée dans l'attribut var du dataTable parent) qui est une instance de Person.
Le sous composant facet permet de définir une sous partie du composant parent (column) et ne sera pas répété pour chaque itération. Ici, le facet définit le contenu
de l'entête de la colonne qui est le texte "Prénom".
- Une autre colonne pour afficher les noms des personnes. elle est définie de la même façon que la première colonne.
Il faut aussi créer une page index.jsp dont voici le
contenu:
<body>
<jsp:forward page="list.jsf" />
</body>
|
index.jsp est la page d'accueil vu qu'on l'a déclaré
dans web.xml.
Pour pouvoir accéder à une page JSF, il faut utiliser
le suffixe .jsf.
C'est pour cette raison qu'on passe par l'élément
forward qui permet de référence la page JSF avec le suffixe .jsf.
III-D. Test
En exécutant l'application, rien d'utile dans
l'affichage vu qu'on n'a pas encore de données, il
faut juste s'assurer qu'il n'y ait pas eu d'exceptions
au démarrage.
Mais en suivant le log de l'application (dans la
console d'Eclipse par exemple), on voit :
[TopLink Fine]: 2007.12.02 12:10:28.968
--ServerSession(3273383)
--Connection(15513215)--Thread(Thread[http-8080-2,5,main])
--CREATE TABLE PERSON (ID NUMERIC(19) NOT NULL, LASTNAME VARCHAR(255), FIRSTNAME VARCHAR(255), PRIMARY KEY (ID))
:
:
[TopLink Fine]: 2007.12.02 12:10:29.318--ServerSession(3273383)
--Connection(15513215)
--Thread(Thread[http-8080-2,5,main])
--SELECT ID, LASTNAME, FIRSTNAME FROM PERSON
|
Ce qui montre que Toplink a crée la table PERSON
dans la base de données et qu'il a ensuite effectué
un SELECT lors de l'affichage de la table.
IV. Implémenter l'opération Create
IV-A. Dans la couche DAO
Dans PersonDao, on ajoute la méthode suivante:
@param
@return
public Person insert(Person u) {
getEntityManager().getTransaction().begin();
getEntityManager().persist(u);
getEntityManager().getTransaction().commit();
return u;
}
|
Cette méthode fait appel à l'API de JPA pour
persister une personne dans la base de données.
 |
Comme vous le remarquez, j'ai entouré l'opération
persist par une ouverture d'une transaction et par
son commit.
C'est une très mauvaise idée de s'y prendre de cette
façon, i.e. gérer les transactions au niveau du DAO.
Dans une application réelle, on a en général
plusieurs opérations qui doivent se réaliser d'une
façon atomique.
Par exemple: on ajoute une entité dans la base de
données et on ajoute un lien vers cette entité dans
une autre table.
Ces deux opérations doivent se réaliser d'une façon
atomique. C'est justement la raison d'être des
transactions.
Or puisque les DAO offrent des fonctionnalités
unitaires (create, update, etc.), il faut plutôt
implémenter les transactions dans la couche Service
et optimalement d'une façon déclarative en utilisant Spring par exemple.
Cette remarque vaut aussi pour les opérations update
et delete.
|
IV-B. Dans la couche Control
Dans PersonCtrl, on ajoute un champ de type Person
qui servira à recueillir les informations de
l'utilisateur.
private Person newPerson = new Person();
|
On ajoute aussi l'action createPerson dans le
contrôleur PersonCtrl qui utilise le DAO et
l'instance newPerson pour ajouter une personne dans
la base de données.
Cette action doit être associée à un submit dans la
page JSF d'ajout d'utilisateur.
public String createPerson() {
pDao.insert(newPerson);
newPerson = new Person();
persons = pDao.selectAll();
return "list";
}
|
Cette méthode ne fait qu'appeler la méthode insert du DAO.
Ensuite, elle crée à nouveau newPerson pour la prochaine insertion.
Enfin, elle met à jour la liste des
personnes pour refléter l'ajout de la nouvelle
personne.
Pour retourner à la page de listing suite à la création d'une personne,
l'action doit retourner un littéral ("list" par exemple dans ce cas). On doit
ensuite ajouter une règle de navigation qui part de la
page d'ajout d'une personne add.jsp et mène vers
list.jsp suite à un résultat "list" dans
faces-config.xml:
<navigation-rule>
<display-name>add</display-name>
<from-view-id>/add.jsp</from-view-id>
<navigation-case>
<from-outcome>list</from-outcome>
<to-view-id>/list.jsp</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>
|
Une règle de navigation JSF se configure ainsi:
- l'élément from-view-id: le nom de la page source.
- navigation-case: cas de navigation:
- from-outcome: le littéral qui active ce cas de navigation
- to-view-id: où aller si ce cas de navigation est activé.
- redirect: un élément optionnel qui s'il est présent,
le cas de navigation effectue un redirect au lieu d'un forward.
 |
Il est possible de déclarer plusieurs cas de navigation dans la même règle de navigation.
On peut voir ça comme ceci: les cas de navigation sont groupés par la page de départ dans une règle de navigation.
|
IV-C. Dans la couche View
On crée une page add.jsp, qui permet de saisir les
données d'une nouvelle personne et d'invoquer
l'action createPerson:
<f:view>
<h:form>
<h:panelGrid border="0" columns="3" cellpadding="5">
<h:outputText value="Prénom" />
<h:inputText id="firstName" value="#{personCtrl.newPerson.firstName}"
required="true" requiredMessage="Prénom obligatoire" />
<h:message for="firstName" />
<h:outputText value="Nom" />
<h:inputText id="lastName" value="#{personCtrl.newPerson.lastName}"
required="true" requiredMessage="Nom obligatoire" />
<h:message for="firstName" />
<h:outputText />
<h:commandButton value="Ajouter" action="#{personCtrl.createPerson}" />
</h:panelGrid>
</h:form>
</f:view>
|
Quelques notes:
-
J'ai utilisé le composant panelGrid qui permet d'aligner ses fils en une grille pour la mise en page.
Ici, j'ai défini 3 colonnes.
-
J'ai utilisé le composant inputText qui permet de récupérer une valeur textuelle.
L'attribut value de ce composant permet d'associer sa valeur avec un champ d'un managed-bean
via une EL. Ici, je lie le premier champ textuel avec le champ firstName de personCtrl.newPerson et le
second avec le champ lastName.
-
J'ai ajouté des contraintes required=true pour
les deux champs texte (nom et prénom) pour indiquer à JSF que leurs valeurs ne peuvent pas être vides ainsi que des
éléments message pour afficher les messages
d'erreur en cas d'erreur de validation.
-
Enfin, un commandButton pour invoquer l'action
PersonCtrl.createPerson (spécifié via une EL dans l'attribut action).
Pour simplifier la navigation entre les deux pages
add.jsp et list.jsp, on va ajouter un menu dans les
deux pages dont voici le code:
<h:panelGrid columns="2" cellpadding="10">
<h:outputLink value="list.jsf">
<h:outputText value="Lister" />
</h:outputLink>
<h:outputLink value="add.jsf">
<h:outputText value="Ajouter" />
</h:outputLink>
</h:panelGrid>
|
Ce ne sont que deux liens hypertexte dans une
grille de 2 colonnes. Ce menu est à mettre dans
les deux pages add.jsp et list.jsp au tout début de
la page juste après le composant <f:view>.
IV-D. Test
On effectue un test en ajoutant quelques personnes:
Initialement la liste est vide:

Liste vide
En cliquant sur le lien "Ajouter", on passe à la
page d'ajout:

Page d'ajout d'une personne
Après avoir rempli et validé le formulaire, on
revient à la liste des personnes:

Liste avec la personne ajoutée
V. Implémenter l'opération Delete
V-A. Dans la couche DAO
On commence par ajouter une méthode delete à
PersonDao:
@param
public void delete(Person u) {
getEntityManager().getTransaction().begin();
u = getEntityManager().merge(u);
getEntityManager().remove(u);
getEntityManager().getTransaction().commit();
}
|
 |
L'instruction "u = em.merge(u)" est très importante, sans
elle, la méthode deletePerson risque de déclencher
une exception car l'instance qu'on lui aura passé
est détachée de la session de persistance.
La méthode merge de la classe EntityManager s'occupe de rattacher
une entité à la session de persistance en cours.
Cette étape est inutile dans un environnement
managé (où l'EntityManager est géré par le conteneur plutôt que par le développeur).
|
V-B. Dans la couche Control
Pour implémenter la fonction supprimer, on ajoute
généralement un lien ou un bouton supprimer dans
chaque ligne de la liste d'affichage.
Suite à un clic sur le bouton supprimer, on invoque
d'action delete qui doit récupérer la ligne
sélectionnée et la supprimer.
Pour implémenter facilement un fonctionnement pareil
dans JSF, on utilise un DataModel comme conteneur de la
liste des valeurs et non plus une liste simple (java.util.List).
En effet, dans le code d'une action donnée, DataModel permet de récupérer à tout
moment la ligne ayant déclenchée l'action.
On remplace donc dans PersonCtrl le champ persons de
type List et son accesseur par:
private DataModel persons;
public DataModel getPersons() {
if (persons == null) {
persons = new ListDataModel();
persons.setWrappedData(pDao.selectAll());
}
return persons;
}
|
DataModel est une interface tandis que ListDataModel est une implémentation (tout comme pour List et ArrayList).
Pour spécifier les éléments du DataModel, on passe une liste ordinaire (java.util.List, java.util.Set)
comme paramètre à la méthode setWrappedData.
Il faut aussi mettre à jour la méthode createPerson
pour refléter l'utilisation de DataModel au lieu de
List:
public String createPerson() {
pDao.insert(newPerson);
newPerson = new Person();
persons.setWrappedData(pDao.selectAll());
return "list";
}
|
Voici ensuite la méthode deletePerson du contrôleur:
@return
public String deletePerson() {
Person p = (Person) persons.getRowData();
pDao.delete(p);
persons.setWrappedData(pDao.selectAll());
return null;
}
|
La première ligne récupère la personne correspondant
à la ligne sélectionnée dans la table.
Reste plus qu'à invoquer la méthode delete du
DAO et à mettre à jour la liste des personnes pour
refléter la suppression.
V-C. Dans la couche View
Coté présentation, on ajoute une nouvelle colonne à
la table des personnes dans list.jsp qui contient un
bouton delete sur chaque ligne:
<h:column>
<f:facet name="header">
<h:outputText value="Opérations" />
</f:facet>
<h:commandButton value="Supprimer"
action="#{personCtrl.deletePerson}" />
</h:column>
|
 |
Il ne faut pas oublier d'englober le dataTable dans
un <h:form> vu que l'on a des commandButton.
|
V-D. Test

Liste des personnes avec les boutons supprimer
En cliquant sur supprimer dans une ligne de la table, la personne correspondante est
supprimée de la base de données et la liste est mise
à jour
VI. Implémenter l'opération Update
VI-A. Dans la couche DAO
Dans PersonDao, on ajoute la méthode update que
voici:
@param
@return
public Person update(Person u) {
getEntityManager().getTransaction().begin();
u = getEntityManager().merge(u);
getEntityManager().getTransaction().commit();
return u;
}
|
 |
Les remarques mentionnées
ici
et
ici
s'appliquent aussi ici.
|
Normalement, une mise à jour est réalisée de la même façon qu'une
insertion, c'est à dire avec la méthode persist de la classe EntityManager.
JPA s'occupe ensuite de décider s'il s'agit d'une nouvelle entité et
qu'il faut l'ajouter ou d'une entité déjà ajoutée et qu'il faut plutôt la
mettre à jour.
Mais pour les mêmes raisons que pour la méthode delete, c'est à dire pour éviter le cas
où l'entité passée est détachée, on utilise la méthode merge de la classe EntityManager
qui rattache une entité à la session de persistance en cours tout en intégrant les modificatio