Bonjour ! Dans la leçon précédente, nous avons exploré la théorie et la mise en place de la connexion entre les Servlets (contrôleur), les JSP (vue) et la couche DAO (modèle). Vous avez appris comment initialiser et rendre accessible le DAO depuis une servlet.
Aujourd'hui, nous passons à l'action. Cette leçon est un projet de synthèse qui vous guidera dans la construction de votre première application web Java complète, de A à Z. L'objectif est de mettre en œuvre les quatre opérations fondamentales de la gestion de données — Create, Read, Update, Delete (CRUD) — pour une entité "Produit". Ce projet consolidera tout ce que vous avez appris sur JDBC, le pattern DAO, les Servlets et les JSP. C'est le point d'orgue de notre étude de l'écosystème Java EE "classique", avant d'aborder des frameworks plus modernes comme Spring Boot.
1. Architecture et Préparation du Projet
Avant d'écrire la moindre ligne de code, ayons une vision claire de l'architecture et de la structure de notre projet. Nous allons suivre scrupuleusement le modèle MVC, en y intégrant notre couche DAO.
Structure des Fichiers
Pour une application bien organisée, la structure des répertoires est primordiale. Elle reflète la séparation des responsabilités.
Plan d'action initial :
- Créez un nouveau projet Maven (
war). - Ajoutez les dépendances nécessaires dans votre
pom.xml:jakarta.servlet-api,mysql-connector-java, etjstl-apiet son implémentation. - Base de données : Créez une table
produitsdans votre base de données MySQL avec les colonnes :id(INT, PRIMARY KEY, AUTO_INCREMENT),nom(VARCHAR),description(TEXT),prix(DECIMAL). - Le Modèle : Créez le bean
Produit.javadans le packagecom.myapp.modelavec les attributs correspondants (id,nom,description,prix) ainsi que les getters et setters. - Le DAO : Mettez en place la couche DAO comme vu dans les leçons précédentes :
- L'interface
ProduitDAO.javaavec les signatures des méthodes :creer(Produit p),trouverParId(int id),lister(),modifier(Produit p),supprimer(int id). - La classe d'implémentation
ProduitDAOImpl.javaqui implémente cette interface. - La
DAOFactoryet sonServletContextListenerpour l'initialisation au démarrage de l'application. Vous pouvez réutiliser et adapter le code du tutoriel de Zeste de Savoir (ressource LINK).
- L'interface
Pour ce projet, nous allons utiliser une seule servlet, ProduitServlet, qui agira comme un contrôleur frontal pour toutes les opérations CRUD sur les produits. Nous la "routerons" en interne à l'aide d'un paramètre d'URL action.
2. Opération "Read" : Afficher la liste des produits
Commençons par la fonctionnalité la plus simple : afficher tous les produits de la base de données.
a. Implémentation du DAO (lister())
Dans votre ProduitDAOImpl, concentrez-vous sur la méthode lister(). Elle doit :
- Obtenir une connexion via la
DAOFactory. - Créer et exécuter une requête
SELECT * FROM produits. - Parcourir le
ResultSet. - Pour chaque ligne, créer un objet
Produit(en utilisant une méthode de mapping comme vu dans la ressource LINK, section 5). - Ajouter chaque objet
Produità uneList<Produit>. - Retourner la liste.
- Gérer la fermeture des ressources (
ResultSet,Statement,Connection) dans un blocfinally.
b. Le Contrôleur (ProduitServlet)
Créez ProduitServlet.java dans le package com.myapp.web.
Dans la méthode doGet(), nous allons lire le paramètre action pour décider quoi faire.
// ProduitServlet.java
@WebServlet("/produits")
public class ProduitServlet extends HttpServlet {
private ProduitDAO produitDao;
public void init() throws ServletException {
DAOFactory daoFactory = (DAOFactory) getServletContext().getAttribute("daoFactory");
this.produitDao = daoFactory.getProduitDao(); // Assurez-vous d'avoir cette méthode dans votre factory
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String action = request.getParameter("action");
if (action == null) {
action = "list"; // Action par défaut
}
switch (action) {
case "list":
listerProduits(request, response);
break;
// D'autres cas pour "new", "edit", "delete" viendront ici...
default:
listerProduits(request, response);
break;
}
}
private void listerProduits(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<Produit> listeProduits = produitDao.lister();
request.setAttribute("listeProduits", listeProduits);
RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/views/produit-list.jsp");
dispatcher.forward(request, response);
}
// La méthode doPost viendra plus tard
}
Note : Placer les JSP dans /WEB-INF/ est une bonne pratique de sécurité pour empêcher l'accès direct via l'URL.
c. La Vue (produit-list.jsp)
Créez produit-list.jsp dans /WEB-INF/views/. Cette page utilise la JSTL pour afficher les données.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<title>Liste des Produits</title>
</head>
<body>
<h1>Liste des Produits</h1>
<p><a href="produits?action=new">Ajouter un produit</a></p>
<table border="1">
<tr>
<th>ID</th>
<th>Nom</th>
<th>Prix</th>
<th>Actions</th>
</tr>
<c:forEach var="produit" items="${listeProduits}">
<tr>
<td><c:out value="${produit.id}" /></td>
<td><c:out value="${produit.nom}" /></td>
<td><c:out value="${produit.prix}" /> €</td>
<td>
<a href="produits?action=edit&id=${produit.id}">Modifier</a>
<a href="produits?action=delete&id=${produit.id}" onclick="return confirm('Êtes-vous sûr de vouloir supprimer ce produit ?')">Supprimer</a>
</td>
</tr>
</c:forEach>
</table>
</body>
</html>
À ce stade, en accédant à http://localhost:8080/votre-app/produits, vous devriez voir la liste des produits de votre base de données.
3. Opération "Create" : Ajouter un produit
L'ajout d'un produit se fait en deux temps : afficher le formulaire, puis le traiter.
a. Afficher le formulaire
- Contrôleur : Dans
ProduitServlet, ajoutez un nouveau cas dans leswitchdedoGet():// Dans doGet() de ProduitServlet case "new": afficherFormulaireCreation(request, response); break;// Nouvelle méthode dans ProduitServlet private void afficherFormulaireCreation(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/views/produit-form.jsp"); dispatcher.forward(request, response); } - Vue : Créez le fichier
produit-form.jsp. Pour l'instant, c'est un simple formulaire HTML qui pointe vers notre servlet.<!-- produit-form.jsp --> <form action="produits?action=insert" method="post"> <h2>Ajouter un Produit</h2> <label>Nom:</label> <input type="text" name="nom" required /> <br/> <label>Description:</label> <textarea name="description" required></textarea> <br/> <label>Prix:</label> <input type="text" name="prix" required /> <br/> <button type="submit">Enregistrer</button> </form>
b. Traiter la soumission
Le traitement se fait dans la méthode doPost() de la servlet. La vidéo ci-dessous montre bien ce mécanisme de récupération des données d'un formulaire.
Développez des sites web avec Java EE: Les formulaires avec JAVA EE
Cette vidéo montre comment traiter un formulaire simple. Concentrez-vous sur la manière dont les données sont récupérées dans la méthode doPost et comment elles sont utilisées.
Regardez comment la servlet récupère les paramètres d'un formulaire soumis en POST après la soumission. C'est exactement ce que nous allons faire.
- DAO : Implémentez la méthode
creer(Produit p)dansProduitDAOImpl. Elle utilisera une requêteINSERTavec unPreparedStatement. - Contrôleur : Implémentez
doPost()et une méthode privée pour l'insertion.// Dans ProduitServlet protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String action = request.getParameter("action"); if (action == null) { action = "list"; } switch (action) { case "insert": insererProduit(request, response); break; // Le cas "update" viendra ici default: listerProduits(request, response); break; } } private void insererProduit(HttpServletRequest request, HttpServletResponse response) throws IOException { String nom = request.getParameter("nom"); String description = request.getParameter("description"); double prix = Double.parseDouble(request.getParameter("prix")); Produit nouveauProduit = new Produit(); nouveauProduit.setNom(nom); nouveauProduit.setDescription(description); nouveauProduit.setPrix(prix); produitDao.creer(nouveauProduit); // Redirection pour éviter la re-soumission du formulaire (Pattern Post-Redirect-Get) response.sendRedirect("produits"); }
4. Opération "Update" : Modifier un produit
La mise à jour est un mélange entre Read (pour pré-remplir le formulaire) et Create (pour traiter la soumission).
- DAO : Implémentez
trouverParId(int id)etmodifier(Produit p). - Contrôleur (
doGet) : Gérez l'actionedit.// Dans le switch de doGet() case "edit": afficherFormulaireModification(request, response); break;// Nouvelle méthode dans ProduitServlet private void afficherFormulaireModification(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int id = Integer.parseInt(request.getParameter("id")); Produit produitExistant = produitDao.trouverParId(id); request.setAttribute("produit", produitExistant); RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/views/produit-form.jsp"); dispatcher.forward(request, response); } - Vue (
produit-form.jsp) : Modifiez le formulaire pour qu'il puisse gérer à la fois la création et la modification.
L'EL (<!-- produit-form.jsp (version améliorée) --> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <c:if test="${produit != null}"> <form action="produits?action=update" method="post"> <input type="hidden" name="id" value="<c:out value='${produit.id}' />" /> </c:if> <c:if test="${produit == null}"> <form action="produits?action=insert" method="post"> </c:if> <h2> <c:if test="${produit != null}">Modifier le Produit</c:if> <c:if test="${produit == null}">Ajouter un Produit</c:if> </h2> <label>Nom:</label> <input type="text" name="nom" value="<c:out value='${produit.nom}' />" required /> <br/> <label>Description:</label> <textarea name="description" required><c:out value='${produit.description}' /></textarea> <br/> <label>Prix:</label> <input type="text" name="prix" value="<c:out value='${produit.prix}' />" required /> <br/> <button type="submit">Enregistrer</button> </form>${produit.nom}) et la JSTL (<c:if>) permettent d'adapter dynamiquement la vue. - Contrôleur (
doPost) : Gérez l'actionupdate.// Dans le switch de doPost() case "update": modifierProduit(request, response); break;// Nouvelle méthode dans ProduitServlet private void modifierProduit(HttpServletRequest request, HttpServletResponse response) throws IOException { int id = Integer.parseInt(request.getParameter("id")); String nom = request.getParameter("nom"); String description = request.getParameter("description"); double prix = Double.parseDouble(request.getParameter("prix")); Produit produit = new Produit(); produit.setId(id); produit.setNom(nom); produit.setDescription(description); produit.setPrix(prix); produitDao.modifier(produit); response.sendRedirect("produits"); }
5. Opération "Delete" : Supprimer un produit
C'est l'opération la plus directe.
- DAO : Implémentez la méthode
supprimer(int id)avec une requêteDELETE. - Contrôleur (
doGet) : Gérez l'actiondelete.// Dans le switch de doGet() case "delete": supprimerProduit(request, response); break;
Bonne pratique : Les opérations qui modifient l'état du serveur (comme// Nouvelle méthode dans ProduitServlet private void supprimerProduit(HttpServletRequest request, HttpServletResponse response) throws IOException { int id = Integer.parseInt(request.getParameter("id")); produitDao.supprimer(id); response.sendRedirect("produits"); }DELETE) devraient idéalement utiliser la méthode HTTPPOSTpour des raisons de sécurité et de sémantique. Pour un projet d'apprentissage, l'utilisation deGETest acceptable, mais gardez cette distinction en tête pour des projets professionnels. L'ajout d'une confirmation JavaScript (onclick) est également une précaution simple et efficace.
Conclusion
Félicitations ! Vous venez de construire une application web Java complète et fonctionnelle qui respecte les standards de l'architecture MVC. Vous avez mis en pratique l'ensemble des technologies vues jusqu'à présent pour créer une application réaliste.
Points clés à retenir :
- Une servlet unique peut servir de contrôleur pour toutes les actions CRUD liées à une entité, en utilisant un paramètre
actionpour le routage interne. - Le pattern Post-Redirect-Get (PRG) est essentiel après une opération de création, mise à jour ou suppression pour éviter les problèmes de re-soumission de formulaire.
- Les JSP, combinées à la JSTL et à l'EL, permettent de créer des vues dynamiques, capables de s'adapter pour afficher des listes, des formulaires vides ou des formulaires pré-remplis.
- La structure du projet et la séparation claire des couches (Modèle, Vue, Contrôleur, DAO) sont la clé d'une application maintenable et évolutive.
Ce projet conclut notre exploration approfondie de la pile technologique "classique" de Java EE. Dans le prochain module, nous allons découvrir Spring Boot, un framework moderne qui va considérablement simplifier et accélérer le développement de telles applications, notamment en automatisant une grande partie de la configuration et de la plomberie que nous avons dû mettre en place manuellement ici. Vous apprécierez d'autant plus sa puissance après avoir maîtrisé ces fondations.