Mis à jour le vendredi 10 mars 2017
  • 40 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

TP Fil rouge - Étape 6

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Sixième étape du fil rouge, le temps est enfin venu pour vous d'intégrer une base de données à votre application ! Création de la base, des tables, des DAO associés et intégration dans l'application existante vous attendent.

Objectifs

Fonctionnalités

Vous allez mettre en place une base de données dans votre application. Ce changement va impliquer que :

  • lors de la création d'un client ou d'une commande par l'utilisateur, les données ne seront maintenant plus seulement mises en session, mais également enregistrées dans la base ;

  • vous allez rendre possible la création de clients portant le même nom, et de commandes passées à la même date ;

  • les pages listant les clients et commandes existants continueront à se baser uniquement sur les données présentes en session ;

  • la suppression d'un client ou d'une commande ne supprimera pas seulement les données de la session, mais également de la base.

C'est « tout » ce que je vous demande... :-°

Conseils

Création de la base de données

La première étape consiste à créer la base et les tables représentant les données de votre application. Ceci ne faisant pas directement l'objet de ce cours, je vais vous accompagner en vous détaillant chaque instruction. Suivez le guide !

Connexion au serveur MySQL

Connectez-vous à votre serveur via la commandemysql -h localhost -u root -p, puis entrez votre mot de passe le cas échéant.

Création d'une base

Mettez en place une nouvelle base de données nommée tp_sdzee à l'aide de l'instruction suivante :

CREATE DATABASE tp_sdzee DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
Mise en place des droits

Vous savez qu'utiliser le compte root est une mauvaise pratique, vous devez donc accorder des droits sur la nouvelle table à l'utilisateur java créé dans le cadre du cours, via l'instruction suivante :

GRANT ALL ON tp_sdzee.* TO 'java'@'localhost' IDENTIFIED BY 'SdZ_eE';

Vous devez ensuite vous déconnecter de l'utilisateur root via la commandeexit, puis vous reconnecter avec votre compte java via la commandemysql -h localhost -u java -p, entrer le mot de passe et enfin entrer la commandeuse tp_sdzeepour définir la base sur laquelle vous allez travailler.

Création des tables

Vous devez alors créer une table Client contenant les champs id, nom, prenom, adresse, telephone, email et image. Voici un exemple d'instruction que vous pouvez utiliser pour cela :

CREATE TABLE tp_sdzee.Client (
 id INT( 11 ) NOT NULL AUTO_INCREMENT ,
 nom VARCHAR( 20 ) NOT NULL ,
 prenom VARCHAR( 20 ) ,
 adresse VARCHAR( 200 ) NOT NULL ,
 telephone VARCHAR( 10 ) NOT NULL,
 email VARCHAR( 60 ) ,
 image VARCHAR( 200 ) ,
 PRIMARY KEY ( id )
) ENGINE = INNODB;

Rien de particulier ici, remarquez simplement la mise en place d'un champ id pour identifier chaque client.

De même, vous devez créer une table Commande contenant les champs id, id_client, date, montant, modePaiement, statutPaiement, modeLivraison et statutLivraison :

CREATE TABLE tp_sdzee.Commande (
 id INT( 11 ) NOT NULL AUTO_INCREMENT ,
 id_client INT( 11 ) ,
 date DATETIME NOT NULL ,
 montant DEC( 11 ) NOT NULL ,
 mode_paiement VARCHAR( 20 ) NOT NULL ,
 statut_paiement VARCHAR( 20 ) ,
 mode_livraison VARCHAR( 20 ) NOT NULL ,
 statut_livraison VARCHAR( 20 ) ,
 PRIMARY KEY ( id ) ,
 CONSTRAINT fk_id_client      -- On donne un nom à notre clé
    FOREIGN KEY (id_client)   -- Colonne sur laquelle on crée la clé
    REFERENCES Client(id)     -- Colonne de référence (celle de la table Client)
    ON DELETE SET NULL        -- Action à effectuer lors de la suppression d'une référence
) ENGINE = INNODB;

Première chose, vous remarquez ici que vous n'allez pas enregistrer les données des clients directement dans la table Commande, mais uniquement leur id dans le champ id_client. Les clients existant déjà dans la table Client, il serait idiot de dupliquer les données !

Deuxième chose, observez en fin d'instruction la mise en place d'une contrainte de clé étrangère sur le champ id_client. Grâce à cette clé, le SGBD sait que le contenu du champ id_client correspond à un id existant dans la table Client.

Enfin, ne manquez pas l'option de la clé étrangère ! Puisqu'une relation est définie par le SGBD entre une commande et son client, il devient impossible par défaut de supprimer un client s'il a déjà passé une commande. Dans le cadre de ce TP, ce n'est pas très ergonomique : vous allez donc devoir changer ce comportement, en précisant que lors de la suppression d'un client, les champs id_client de la table Commande qui y font référence devront passer automatiquement àNULL. C'est le rôle de la ligneON DELETE SET NULL.

En construisant ainsi vos tables, vous allez pouvoir vous assurer que les données écrites dans votre base sont cohérentes.

Mise en place de JDBC

Si ce n'est pas déjà fait, vous allez devoir placer le jar du driver JDBC pour MySQL dans le dossier /lib de Tomcat, afin que votre application puisse communiquer avec votre base de données.

Réutilisation de la structure DAO développée dans le cadre du cours

Votre base de données est en place. Vous pouvez maintenant attaquer le développement de votre application. Pour commencer, une bonne nouvelle : vous allez pouvoir réutiliser certaines classes du packagecom.sdzee.daoque vous avez développées dans le cadre du cours ! Les voici :

  • DAOConfigurationException.java

  • DAOException.java

  • DAOUtilitaire.java

En effet, inutile de réinventer la roue pour toutes celles-là : elles fonctionnent, vous avez passé du temps à les mettre en place, autant s'en resservir ! Vous pouvez donc créer un packagecom.sdzee.tp.daodans votre projet et y copier chacune d'elles.

De même, vous allez pouvoir réutiliser la classe InitialisationDaoFactory.java et la placer dans un nouveau packagecom.sdzee.tp.config.

Enfin, il reste deux fichiers que vous allez pouvoir réutiliser, moyennant quelques petits ajustements :

  • vous pouvez reprendre le fichier dao.properties, mais il faudra bien penser à changer le contenu de son entrée url, pour cibler la nouvelle base tp_sdzee que vous avez créée, et non plus bdd_sdzee comme c'était le cas dans le cadre du cours ;

  • vous pouvez de même reprendre la classe DAOFactory.java, mais vous allez devoir y changer le chemin vers le fichier dao.properties défini dans la constante FICHIER_PROPERTIES.

Prenez bien le temps de mettre cette structure en place, n'allez pas trop vite et vérifiez que tout est bien au bon endroit avant de poursuivre ! ;)

Création des interfaces et implémentations du DAO

Maintenant que le cadre est en place, vous allez devoir mettre la main à la pâte et coder les classes du DAO spécifiques à votre projet :

  • l'interface ClientDao et son implémentation ClientDaoImpl ;

  • l'interface CommandeDao et son implémentation CommandeDaoImpl.

Les interfaces

Dans le cadre de ce TP, vous n'avez pas développé de formulaires pour la modification des données existantes. Ainsi, vous n'avez pas besoin de définir toutes les méthodes CRUD dans vos DAO. Vous pouvez vous contenter des méthodescreer(),trouver(),lister()etsupprimer(). Inspirez-vous de ce que vous avez mis en place dans le chapitre précédent si vous ne vous souvenez plus comment procéder.

Les implémentations

Vous allez devoir définir les requêtes SQL à effectuer sur vos tables, créer et manipuler des requêtes préparées depuis vos différentes méthodes, tout cela bien entendu en réutilisant les méthodes développées dans la classe DAOUtilitaire. Vous pouvez, là encore, vous inspirer grandement de ce que vous avez développé dans le chapitre précédent, mais vous devrez prendre garde à modifier les méthodesmap(), à créer une méthodelister()et à bien ajuster les méthodestrouver()etcreer(), et enfinsupprimer()!

Afin de vous permettre de partir du bon pied, je vous donne ici les requêtes à utiliser respectivement dans les classes ClientDaoImpl et CommandeDaoImpl :

private static final String SQL_SELECT        = "SELECT id, nom, prenom, adresse, telephone, email, image FROM Client ORDER BY id";
private static final String SQL_SELECT_PAR_ID = "SELECT id, nom, prenom, adresse, telephone, email, image FROM Client WHERE id = ?";
private static final String SQL_INSERT        = "INSERT INTO Client (nom, prenom, adresse, telephone, email, image) VALUES (?, ?, ?, ?, ?, ?)";
private static final String SQL_DELETE_PAR_ID = "DELETE FROM Client WHERE id = ?";
private static final String SQL_SELECT        = "SELECT id, id_client, date, montant, mode_paiement, statut_paiement, mode_livraison, statut_livraison FROM Commande ORDER BY id";
private static final String SQL_SELECT_PAR_ID = "SELECT id, id_client, date, montant, mode_paiement, statut_paiement, mode_livraison, statut_livraison FROM Commande WHERE id = ?";
private static final String SQL_INSERT        = "INSERT INTO Commande (id_client, date, montant, mode_paiement, statut_paiement, mode_livraison, statut_livraison) VALUES (?, ?, ?, ?, ?, ?, ?)";
private static final String SQL_DELETE_PAR_ID = "DELETE FROM Commande WHERE id = ?";

Avec ceci en poche, vous n'avez plus à vous soucier de l'aspect SQL du développement, et vous pouvez vous concentrer sur l'aspect... Java EE ! ;)

Intégration dans le code existant

Afin d'intégrer correctement vos DAO dans l'application, vous allez devoir procéder à de nombreux changements dans le code existant...

Modification des beans

Vous allez devoir apporter deux changements :

  • ajouter un champ id de typeLongaux deux beans, pour refléter l'id auto-généré existant dans vos tables ;

  • modifier le type du champ date dans le bean Commande. Jusqu'à présent vous stockiez cette information sous forme d'une chaîne de caractères pour ne pas vous compliquer la tâche, mais cette fois vous allez devoir faire correspondre ce champ à celui créé dans la table Commande. Puisque nous utilisons la bibliothèque JodaTime, je vous conseille de changer le type du champ date deStringversDateTime.

Modification des servlets

Vous allez devoir adapter vos servlets CreationClient et CreationCommande, toujours sur le modèle de ce que vous avez développé dans le cours :

  • récupérer la ou les implémentations de DAO depuis la factory, dans la méthodeinit()de chaque servlet ;

  • passage du ou des DAO à l'objet métier lors de sa construction.

En outre, vous allez devoir apporter un autre changement, qui va avoir un impact considérable sur le reste de l’application. Jusqu'à présent, vous manipuliez desMapindexées sur les noms et dates des clients et commandes, ce qui rendait impossible la création de deux clients portant le même nom et la création de deux commandes au même instant.

Maintenant que vos données sont identifiables par un id dans vos tables, vous allez pouvoir modifier vosMappour qu'elles indexent les id des clients et commandes. VosMapvont donc changer deMap<String, Client>etMap<String, Commande>versMap<Long, Client>etMap<Long, Commande>. Vous allez donc devoir prendre garde, partout où ces maps sont manipulées, à ne plus travailler sur les noms et dates, mais bien sur les id. Ne prenez pas ce changement à la légère, il va impliquer pas mal de petites modifications ça et là... !

Vous allez également devoir modifier vos servlets de suppression :

  • pour qu'elles se basent sur l'id lors de la suppression d'une entrée dans uneMap, suite à la modification apportée précédemment ;

  • pour qu'elles suppriment également les données en base, ceci implique donc la récupération du DAO depuis la méthodeinit()et un appel à la méthodesupprimer()du DAO.

Modification des objets métier

Vous allez devoir reprendre vos deux objets métier CreationClientForm et CreationCommandeForm pour qu'ils interagissent avec la BDD. Là encore, vous pouvez vous inspirer grandement de ce que vous avez développé dans le chapitre précédent. Dans les grandes lignes, il va vous falloir :

  • récupérer la ou les instances de DAO nécessaires depuis le constructeur ;

  • effectuer un appel à la méthodecreer()du DAO une fois la validation des champs passée avec succès, et gérer les éventuelles erreurs ou exceptions retournées.

Attention dans le code de CreationCommandeForm ! Vous devrez prendre garde à bien adapter la manipulation de laMapdes clients, suite aux changements que vous avez apportés au maps définies dans vos servlets !

Modification des JSP

Premièrement, il va falloir adapter vos JSP aux changements effectués sur vos maps de clients et commandes. Dans les pages listerClients.jsp et listerCommandes.jsp, vous allez donc devoir modifier les liens de suppression pour qu'ils ne transmettent plus le nom d'un client ou la date d'une commande, mais leur id.

Deuxièmement, vous allez devoir modifier la manière dont vous affichez le champ date du bean Commande ! Eh oui, maintenant que vous l'avez remplacé par un objet Joda DateTime, vous ne pouvez plus vous contenter de l'afficher bêtement via la balise<c:out>... Pas de panique, les développeurs de Joda ont pensé à ce cas de figure et ont créé une bibliothèque de balises destinées à manipuler les objets Joda !

Vous devez donc télécharger son jar en cliquant sur ce lien, puis le placer dans le répertoire /WEB-INF/lib de votre projet. Ensuite, vous allez pouvoir utiliser les balises comme vous utilisez celles de la JSTL. Voici un exemple d'utilisation, qui se charge de formater un objet DateTime pour affichage à l'utilisateur selon un pattern défini :

<%@ taglib prefix="joda" uri="http://www.joda.org/joda/time/tags" %>

...

<joda:format value="${ commande.date }" pattern="dd/MM/yyyy HH:mm:ss"></joda:format>

Je vous invite bien entendu à parcourir la documentation du projet plus en profondeur pour découvrir les différentes balises et attributs disponibles.

Création d'un filtre

Enfin, il reste un aspect important à changer dans le mode de fonctionnement de votre application. Je vous ai demandé de faire en sorte que vos pages listant les commandes et clients existants continuent à se baser sur les infos présentes en session. Il va donc falloir réfléchir un petit peu à la manière de procéder ici. En effet, au tout premier lancement de l'application, aucun problème : aucune donnée n'existe, et donc rien n'existe en session.

Imaginez maintenant que l'utilisateur crée tout un tas de clients et commandes, puis quitte l'application et ne revient pas pendant une semaine. À son retour, la session n'existera plus depuis belle lurette sur le serveur, qui aura tôt fait de la supprimer. Ainsi, les pages listant les clients et commandes n'afficheront rien, alors qu'en réalité il existe des données, non pas dans la session, mais dans la base de données !

Ce qu'il vous faut gérer, c'est donc le préchargement des données existant en base dans la session de l'utilisateur lors de son arrivée, et uniquement lors de son arrivée, sur n'importe quelle page de l'application.

Comment réaliser une telle opération ?

En principe, vous avez déjà la réponse à cette question ! Réfléchissez bien. Quel composant est idéal pour ce type de fonctionnalité ? Eh oui, c'est le filtre ! Vous allez donc créer un filtre appliqué à toutes les pages de votre application, qui se chargera de vérifier si une session utilisateur existe, et qui la remplira avec les données présentes en base si elle n'existe pas. En somme, vous allez y récupérer une implémentation de DAO comme vous l'avez déjà fait pour les servlets, effectuer un appel à sa méthodelister(), et peupler les maps de clients et de commandes avec les données contenues dans les listes récupérées !

N'oubliez pas de bien déclarer le filtre dans le fichier web.xml !

Voilà tout ce dont vous avez besoin. Comme vous le voyez, le sujet était très court, mais en réalité le travail est conséquent !

Correction

Faites attention à bien reprendre les fichiers du cours qui restent inchangés, à corriger les quelques classes qui nécessitent des ajustements, et surtout ne baissez pas les bras devant la charge de travail impliquée par l'introduction d'une base de données dans votre programme ! Cet exercice est l'occasion parfaite pour vous familiariser avec ces concepts si importants. Ne la gâchez pas en abandonnant dès le moindre petit obstacle ! ;)

Comme toujours, ce n'est pas la seule manière de faire, le principal est que votre solution respecte les consignes que je vous ai données !

Code de la structure DAO

Voici les liens directs vers les fichiers que vous devez reprendre tels quels depuis le chapitre précédent :

Voilà le contenu du nouveau fichier dao.properties, à placer danscom.sdzee.tp.dao:

url = jdbc:mysql://localhost:3306/tp_sdzee
driver = com.mysql.jdbc.Driver
nomutilisateur = java
motdepasse = SdZ_eE

Et voilà le code de la classe DAOFactory adapté à ce nouveau fichier Properties et au nouveau projet :

package com.sdzee.tp.dao;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public class DAOFactory {

    private static final String FICHIER_PROPERTIES       = "/com/sdzee/tp/dao/dao.properties";
    private static final String PROPERTY_URL             = "url";
    private static final String PROPERTY_DRIVER          = "driver";
    private static final String PROPERTY_NOM_UTILISATEUR = "nomutilisateur";
    private static final String PROPERTY_MOT_DE_PASSE    = "motdepasse";

    private String              url;
    private String              username;
    private String              password;

    /* package */DAOFactory( String url, String username, String password ) {
        this.url = url;
        this.username = username;
        this.password = password;
    }

    /*
     * Méthode chargée de récupérer les informations de connexion à la base de
     * données, charger le driver JDBC et retourner une instance de la Factory
     */
    public static DAOFactory getInstance() throws DAOConfigurationException {
        Properties properties = new Properties();
        String url;
        String driver;
        String nomUtilisateur;
        String motDePasse;

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        InputStream fichierProperties = classLoader.getResourceAsStream( FICHIER_PROPERTIES );

        if ( fichierProperties == null ) {
            throw new DAOConfigurationException( "Le fichier properties " + FICHIER_PROPERTIES + " est introuvable." );
        }

        try {
            properties.load( fichierProperties );
            url = properties.getProperty( PROPERTY_URL );
            driver = properties.getProperty( PROPERTY_DRIVER );
            nomUtilisateur = properties.getProperty( PROPERTY_NOM_UTILISATEUR );
            motDePasse = properties.getProperty( PROPERTY_MOT_DE_PASSE );
        } catch ( FileNotFoundException e ) {
            throw new DAOConfigurationException( "Le fichier properties " + FICHIER_PROPERTIES + " est introuvable.", e );
        } catch ( IOException e ) {
            throw new DAOConfigurationException( "Impossible de charger le fichier properties " + FICHIER_PROPERTIES, e );
        }

        try {
            Class.forName( driver );
        } catch ( ClassNotFoundException e ) {
            throw new DAOConfigurationException( "Le driver est introuvable dans le classpath.", e );
        }

        DAOFactory instance = new DAOFactory( url, nomUtilisateur, motDePasse );
        return instance;
    }

    /* Méthode chargée de fournir une connexion à la base de données */
    /* package */Connection getConnection() throws SQLException {
        return DriverManager.getConnection( url, username, password );
    }

    /*
     * Méthodes de récupération de l'implémentation des différents DAO
     * (uniquement deux dans le cadre de ce TP)
     */
    public ClientDao getClientDao() {
        return new ClientDaoImpl( this );
    }

    public CommandeDao getCommandeDao() {
        return new CommandeDaoImpl( this );
    }
}

Code des interfaces DAO

L'interface ClientDao :

package com.sdzee.tp.dao;

import java.util.List;

import com.sdzee.tp.beans.Client;

public interface ClientDao {
    void creer( Client client ) throws DAOException;

    Client trouver( long id ) throws DAOException;

    List<Client> lister() throws DAOException;

    void supprimer( Client client ) throws DAOException;
}

L'interface CommandeDao :

package com.sdzee.tp.dao;

import java.util.List;

import com.sdzee.tp.beans.Commande;

public interface CommandeDao {
    void creer( Commande commande ) throws DAOException;

    Commande trouver( long id ) throws DAOException;

    List<Commande> lister() throws DAOException;

    void supprimer( Commande commande ) throws DAOException;
}

Code des implémentations DAO

Code de ClientDaoImpl :

package com.sdzee.tp.dao;

import static com.sdzee.tp.dao.DAOUtilitaire.fermeturesSilencieuses;
import static com.sdzee.tp.dao.DAOUtilitaire.initialisationRequetePreparee;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.sdzee.tp.beans.Client;

public class ClientDaoImpl implements ClientDao {

    private static final String SQL_SELECT        = "SELECT id, nom, prenom, adresse, telephone, email, image FROM Client ORDER BY id";
    private static final String SQL_SELECT_PAR_ID = "SELECT id, nom, prenom, adresse, telephone, email, image FROM Client WHERE id = ?";
    private static final String SQL_INSERT        = "INSERT INTO Client (nom, prenom, adresse, telephone, email, image) VALUES (?, ?, ?, ?, ?, ?)";
    private static final String SQL_DELETE_PAR_ID = "DELETE FROM Client WHERE id = ?";

    private DAOFactory          daoFactory;

    ClientDaoImpl( DAOFactory daoFactory ) {
        this.daoFactory = daoFactory;
    }

    /* Implémentation de la méthode définie dans l'interface ClientDao */
    @Override
    public Client trouver( long id ) throws DAOException {
        return trouver( SQL_SELECT_PAR_ID, id );
    }

    /* Implémentation de la méthode définie dans l'interface ClientDao */
    @Override
    public void creer( Client client ) throws DAOException {
        Connection connexion = null;
        PreparedStatement preparedStatement = null;
        ResultSet valeursAutoGenerees = null;

        try {
            connexion = daoFactory.getConnection();
            preparedStatement = initialisationRequetePreparee( connexion, SQL_INSERT, true,
                    client.getNom(), client.getPrenom(),
                    client.getAdresse(), client.getTelephone(),
                    client.getEmail(), client.getImage() );
            int statut = preparedStatement.executeUpdate();
            if ( statut == 0 ) {
                throw new DAOException( "Échec de la création du client, aucune ligne ajoutée dans la table." );
            }
            valeursAutoGenerees = preparedStatement.getGeneratedKeys();
            if ( valeursAutoGenerees.next() ) {
                client.setId( valeursAutoGenerees.getLong( 1 ) );
            } else {
                throw new DAOException( "Échec de la création du client en base, aucun ID auto-généré retourné." );
            }
        } catch ( SQLException e ) {
            throw new DAOException( e );
        } finally {
            fermeturesSilencieuses( valeursAutoGenerees, preparedStatement, connexion );
        }
    }

    /* Implémentation de la méthode définie dans l'interface ClientDao */
    @Override
    public List<Client> lister() throws DAOException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        List<Client> clients = new ArrayList<Client>();

        try {
            connection = daoFactory.getConnection();
            preparedStatement = connection.prepareStatement( SQL_SELECT );
            resultSet = preparedStatement.executeQuery();
            while ( resultSet.next() ) {
                clients.add( map( resultSet ) );
            }
        } catch ( SQLException e ) {
            throw new DAOException( e );
        } finally {
            fermeturesSilencieuses( resultSet, preparedStatement, connection );
        }

        return clients;
    }

    /* Implémentation de la méthode définie dans l'interface ClientDao */
    @Override
    public void supprimer( Client client ) throws DAOException {
        Connection connexion = null;
        PreparedStatement preparedStatement = null;

        try {
            connexion = daoFactory.getConnection();
            preparedStatement = initialisationRequetePreparee( connexion, SQL_DELETE_PAR_ID, true, client.getId() );
            int statut = preparedStatement.executeUpdate();
            if ( statut == 0 ) {
                throw new DAOException( "Échec de la suppression du client, aucune ligne supprimée de la table." );
            } else {
                client.setId( null );
            }
        } catch ( SQLException e ) {
            throw new DAOException( e );
        } finally {
            fermeturesSilencieuses( preparedStatement, connexion );
        }
    }

    /*
     * Méthode générique utilisée pour retourner un client depuis la base de
     * données, correspondant à la requête SQL donnée prenant en paramètres les
     * objets passés en argument.
     */
    private Client trouver( String sql, Object... objets ) throws DAOException {
        Connection connexion = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        Client client = null;

        try {
            /* Récupération d'une connexion depuis la Factory */
            connexion = daoFactory.getConnection();
            /*
             * Préparation de la requête avec les objets passés en arguments
             * (ici, uniquement un id) et exécution.
             */
            preparedStatement = initialisationRequetePreparee( connexion, sql, false, objets );
            resultSet = preparedStatement.executeQuery();
            /* Parcours de la ligne de données retournée dans le ResultSet */
            if ( resultSet.next() ) {
                client = map( resultSet );
            }
        } catch ( SQLException e ) {
            throw new DAOException( e );
        } finally {
            fermeturesSilencieuses( resultSet, preparedStatement, connexion );
        }

        return client;
    }

    /*
     * Simple méthode utilitaire permettant de faire la correspondance (le
     * mapping) entre une ligne issue de la table des clients (un ResultSet) et
     * un bean Client.
     */
    private static Client map( ResultSet resultSet ) throws SQLException {
        Client client = new Client();
        client.setId( resultSet.getLong( "id" ) );
        client.setNom( resultSet.getString( "nom" ) );
        client.setPrenom( resultSet.getString( "prenom" ) );
        client.setAdresse( resultSet.getString( "adresse" ) );
        client.setTelephone( resultSet.getString( "telephone" ) );
        client.setEmail( resultSet.getString( "email" ) );
        client.setImage( resultSet.getString( "image" ) );
        return client;
    }

}

Code de CommandeDaoImpl :

package com.sdzee.tp.dao;

import static com.sdzee.tp.dao.DAOUtilitaire.fermeturesSilencieuses;
import static com.sdzee.tp.dao.DAOUtilitaire.initialisationRequetePreparee;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import org.joda.time.DateTime;

import com.sdzee.tp.beans.Commande;

public class CommandeDaoImpl implements CommandeDao {

    private static final String SQL_SELECT        = "SELECT id, id_client, date, montant, mode_paiement, statut_paiement, mode_livraison, statut_livraison FROM Commande ORDER BY id";
    private static final String SQL_SELECT_PAR_ID = "SELECT id, id_client, date, montant, mode_paiement, statut_paiement, mode_livraison, statut_livraison FROM Commande WHERE id = ?";
    private static final String SQL_INSERT        = "INSERT INTO Commande (id_client, date, montant, mode_paiement, statut_paiement, mode_livraison, statut_livraison) VALUES (?, ?, ?, ?, ?, ?, ?)";
    private static final String SQL_DELETE_PAR_ID = "DELETE FROM Commande WHERE id = ?";

    private DAOFactory          daoFactory;

    CommandeDaoImpl( DAOFactory daoFactory ) {
        this.daoFactory = daoFactory;
    }

    /* Implémentation de la méthode définie dans l'interface CommandeDao */
    @Override
    public Commande trouver( long id ) throws DAOException {
        return trouver( SQL_SELECT_PAR_ID, id );
    }

    /* Implémentation de la méthode définie dans l'interface CommandeDao */
    @Override
    public void creer( Commande commande ) throws DAOException {
        Connection connexion = null;
        PreparedStatement preparedStatement = null;
        ResultSet valeursAutoGenerees = null;

        try {
            connexion = daoFactory.getConnection();
            preparedStatement = initialisationRequetePreparee( connexion, SQL_INSERT, true,
                    commande.getClient().getId(), new Timestamp( commande.getDate().getMillis() ),
                    commande.getMontant(),
                    commande.getModePaiement(), commande.getStatutPaiement(),
                    commande.getModeLivraison(), commande.getStatutLivraison() );
            int statut = preparedStatement.executeUpdate();
            if ( statut == 0 ) {
                throw new DAOException( "Échec de la création de la commande, aucune ligne ajoutée dans la table." );
            }
            valeursAutoGenerees = preparedStatement.getGeneratedKeys();
            if ( valeursAutoGenerees.next() ) {
                commande.setId( valeursAutoGenerees.getLong( 1 ) );
            } else {
                throw new DAOException( "Échec de la création de la commande en base, aucun ID auto-généré retourné." );
            }
        } catch ( SQLException e ) {
            throw new DAOException( e );
        } finally {
            fermeturesSilencieuses( valeursAutoGenerees, preparedStatement, connexion );
        }
    }

    /* Implémentation de la méthode définie dans l'interface ClientDao */
    @Override
    public List<Commande> lister() throws DAOException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        List<Commande> commandes = new ArrayList<Commande>();

        try {
            connection = daoFactory.getConnection();
            preparedStatement = connection.prepareStatement( SQL_SELECT );
            resultSet = preparedStatement.executeQuery();
            while ( resultSet.next() ) {
                commandes.add( map( resultSet ) );
            }
        } catch ( SQLException e ) {
            throw new DAOException( e );
        } finally {
            fermeturesSilencieuses( resultSet, preparedStatement, connection );
        }

        return commandes;
    }

    /* Implémentation de la méthode définie dans l'interface CommandeDao */
    @Override
    public void supprimer( Commande commande ) throws DAOException {
        Connection connexion = null;
        PreparedStatement preparedStatement = null;

        try {
            connexion = daoFactory.getConnection();
            preparedStatement = initialisationRequetePreparee( connexion, SQL_DELETE_PAR_ID, true, commande.getId() );
            int statut = preparedStatement.executeUpdate();
            if ( statut == 0 ) {
                throw new DAOException( "Échec de la suppression de la commande, aucune ligne supprimée de la table." );
            } else {
                commande.setId( null );
            }
        } catch ( SQLException e ) {
            throw new DAOException( e );
        } finally {
            fermeturesSilencieuses( preparedStatement, connexion );
        }
    }

    /*
     * Méthode générique utilisée pour retourner une commande depuis la base de
     * données, correspondant à la requête SQL donnée prenant en paramètres les
     * objets passés en argument.
     */
    private Commande trouver( String sql, Object... objets ) throws DAOException {
        Connection connexion = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        Commande commande = null;

        try {
            /* Récupération d'une connexion depuis la Factory */
            connexion = daoFactory.getConnection();
            /*
             * Préparation de la requête avec les objets passés en arguments
             * (ici, uniquement un id) et exécution.
             */
            preparedStatement = initialisationRequetePreparee( connexion, sql, false, objets );
            resultSet = preparedStatement.executeQuery();
            /* Parcours de la ligne de données retournée dans le ResultSet */
            if ( resultSet.next() ) {
                commande = map( resultSet );
            }
        } catch ( SQLException e ) {
            throw new DAOException( e );
        } finally {
            fermeturesSilencieuses( resultSet, preparedStatement, connexion );
        }

        return commande;
    }

    /*
     * Simple méthode utilitaire permettant de faire la correspondance (le
     * mapping) entre une ligne issue de la table des commandes (un ResultSet)
     * et un bean Commande.
     */
    private Commande map( ResultSet resultSet ) throws SQLException {
        Commande commande = new Commande();
        commande.setId( resultSet.getLong( "id" ) );

        /*
         * Petit changement ici : pour récupérer un client, il nous faut faire
         * appel à la méthode trouver() du DAO Client, afin de récupérer un bean
         * Client à partir de l'id présent dans la table Commande.
         */
        ClientDao clientDao = daoFactory.getClientDao();
        commande.setClient( clientDao.trouver( resultSet.getLong( "id_client" ) ) );

        commande.setDate( new DateTime( resultSet.getTimestamp( "date" ) ) );
        commande.setMontant( resultSet.getDouble( "montant" ) );
        commande.setModePaiement( resultSet.getString( "mode_paiement" ) );
        commande.setStatutPaiement( resultSet.getString( "statut_paiement" ) );
        commande.setModeLivraison( resultSet.getString( "mode_livraison" ) );
        commande.setStatutLivraison( resultSet.getString( "statut_livraison" ) );
        return commande;
    }

}

Code des beans

Client :

package com.sdzee.tp.beans;

import java.io.Serializable;

public class Client implements Serializable {

    private Long   id;
    private String nom;
    private String prenom;
    private String adresse;
    private String telephone;
    private String email;
    private String image;

    public void setId( Long id ) {
        this.id = id;
    }

    public Long getId() {
        return id;
    }

    public void setNom( String nom ) {
        this.nom = nom;
    }

    public String getNom() {
        return nom;
    }

    public void setPrenom( String prenom ) {
        this.prenom = prenom;
    }

    public String getPrenom() {
        return prenom;
    }

    public void setAdresse( String adresse ) {
        this.adresse = adresse;
    }

    public String getAdresse() {
        return adresse;
    }

    public void setTelephone( String telephone ) {
        this.telephone = telephone;
    }

    public String getTelephone() {
        return telephone;
    }

    public void setEmail( String email ) {
        this.email = email;
    }

    public String getEmail() {
        return email;
    }

    public void setImage( String image ) {
        this.image = image;
    }

    public String getImage() {
        return image;
    }
}

Commande :

package com.sdzee.tp.beans;

import java.io.Serializable;

import org.joda.time.DateTime;

public class Commande implements Serializable {
    private Long     id;
    private Client   client;
    private DateTime date;
    private Double   montant;
    private String   modePaiement;
    private String   statutPaiement;
    private String   modeLivraison;
    private String   statutLivraison;

    public Long getId() {
        return id;
    }

    public void setId( Long id ) {
        this.id = id;
    }

    public Client getClient() {
        return client;
    }

    public void setClient( Client client ) {
        this.client = client;
    }

    public DateTime getDate() {
        return date;
    }

    public void setDate( DateTime date ) {
        this.date = date;
    }

    public Double getMontant() {
        return montant;
    }

    public void setMontant( Double montant ) {
        this.montant = montant;
    }

    public String getModePaiement() {
        return modePaiement;
    }

    public void setModePaiement( String modePaiement ) {
        this.modePaiement = modePaiement;
    }

    public String getStatutPaiement() {
        return statutPaiement;
    }

    public void setStatutPaiement( String statutPaiement ) {
        this.statutPaiement = statutPaiement;
    }

    public String getModeLivraison() {
        return modeLivraison;
    }

    public void setModeLivraison( String modeLivraison ) {
        this.modeLivraison = modeLivraison;
    }

    public String getStatutLivraison() {
        return statutLivraison;
    }

    public void setStatutLivraison( String statutLivraison ) {
        this.statutLivraison = statutLivraison;
    }
}

Code des objets métier

Code de CreationClientForm :

package com.sdzee.tp.forms;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;

import com.sdzee.tp.beans.Client;
import com.sdzee.tp.dao.ClientDao;
import com.sdzee.tp.dao.DAOException;

import eu.medsea.mimeutil.MimeUtil;

public final class CreationClientForm {
    private static final String CHAMP_NOM       = "nomClient";
    private static final String CHAMP_PRENOM    = "prenomClient";
    private static final String CHAMP_ADRESSE   = "adresseClient";
    private static final String CHAMP_TELEPHONE = "telephoneClient";
    private static final String CHAMP_EMAIL     = "emailClient";
    private static final String CHAMP_IMAGE     = "imageClient";

    private static final int    TAILLE_TAMPON   = 10240;                        // 10ko

    private String              resultat;
    private Map<String, String> erreurs         = new HashMap<String, String>();
    private ClientDao           clientDao;

    public CreationClientForm( ClientDao clientDao ) {
        this.clientDao = clientDao;
    }

    public Map<String, String> getErreurs() {
        return erreurs;
    }

    public String getResultat() {
        return resultat;
    }

    public Client creerClient( HttpServletRequest request, String chemin ) {
        String nom = getValeurChamp( request, CHAMP_NOM );
        String prenom = getValeurChamp( request, CHAMP_PRENOM );
        String adresse = getValeurChamp( request, CHAMP_ADRESSE );
        String telephone = getValeurChamp( request, CHAMP_TELEPHONE );
        String email = getValeurChamp( request, CHAMP_EMAIL );

        Client client = new Client();

        traiterNom( nom, client );
        traiterPrenom( prenom, client );
        traiterAdresse( adresse, client );
        traiterTelephone( telephone, client );
        traiterEmail( email, client );
        traiterImage( client, request, chemin );

        try {
            if ( erreurs.isEmpty() ) {
                clientDao.creer( client );
                resultat = "Succès de la création du client.";
            } else {
                resultat = "Échec de la création du client.";
            }
        } catch ( DAOException e ) {
            setErreur( "imprévu", "Erreur imprévue lors de la création." );
            resultat = "Échec de la création du client : une erreur imprévue est survenue, merci de réessayer dans quelques instants.";
            e.printStackTrace();
        }

        return client;
    }

    private void traiterNom( String nom, Client client ) {
        try {
            validationNom( nom );
        } catch ( FormValidationException e ) {
            setErreur( CHAMP_NOM, e.getMessage() );
        }
        client.setNom( nom );
    }

    private void traiterPrenom( String prenom, Client client ) {
        try {
            validationPrenom( prenom );
        } catch ( FormValidationException e ) {
            setErreur( CHAMP_PRENOM, e.getMessage() );
        }
        client.setPrenom( prenom );
    }

    private void traiterAdresse( String adresse, Client client ) {
        try {
            validationAdresse( adresse );
        } catch ( FormValidationException e ) {
            setErreur( CHAMP_ADRESSE, e.getMessage() );
        }
        client.setAdresse( adresse );
    }

    private void traiterTelephone( String telephone, Client client ) {
        try {
            validationTelephone( telephone );
        } catch ( FormValidationException e ) {
            setErreur( CHAMP_TELEPHONE, e.getMessage() );
        }
        client.setTelephone( telephone );
    }

    private void traiterEmail( String email, Client client ) {
        try {
            validationEmail( email );
        } catch ( FormValidationException e ) {
            setErreur( CHAMP_EMAIL, e.getMessage() );
        }
        client.setEmail( email );
    }

    private void traiterImage( Client client, HttpServletRequest request, String chemin ) {
        String image = null;
        try {
            image = validationImage( request, chemin );
        } catch ( FormValidationException e ) {
            setErreur( CHAMP_IMAGE, e.getMessage() );
        }
        client.setImage( image );
    }

    private void validationNom( String nom ) throws FormValidationException {
        if ( nom != null ) {
            if ( nom.length() < 2 ) {
                throw new FormValidationException( "Le nom d'utilisateur doit contenir au moins 2 caractères." );
            }
        } else {
            throw new FormValidationException( "Merci d'entrer un nom d'utilisateur." );
        }
    }

    private void validationPrenom( String prenom ) throws FormValidationException {
        if ( prenom != null && prenom.length() < 2 ) {
            throw new FormValidationException( "Le prénom d'utilisateur doit contenir au moins 2 caractères." );
        }
    }

    private void validationAdresse( String adresse ) throws FormValidationException {
        if ( adresse != null ) {
            if ( adresse.length() < 10 ) {
                throw new FormValidationException( "L'adresse de livraison doit contenir au moins 10 caractères." );
            }
        } else {
            throw new FormValidationException( "Merci d'entrer une adresse de livraison." );
        }
    }

    private void validationTelephone( String telephone ) throws FormValidationException {
        if ( telephone != null ) {
            if ( !telephone.matches( "^\\d+$" ) ) {
                throw new FormValidationException( "Le numéro de téléphone doit uniquement contenir des chiffres." );
            } else if ( telephone.length() < 4 ) {
                throw new FormValidationException( "Le numéro de téléphone doit contenir au moins 4 chiffres." );
            }
        } else {
            throw new FormValidationException( "Merci d'entrer un numéro de téléphone." );
        }
    }

    private void validationEmail( String email ) throws FormValidationException {
        if ( email != null && !email.matches( "([^.@]+)(\\.[^.@]+)*@([^.@]+\\.)+([^.@]+)" ) ) {
            throw new FormValidationException( "Merci de saisir une adresse mail valide." );
        }
    }

    private String validationImage( HttpServletRequest request, String chemin ) throws FormValidationException {
        /*
         * Récupération du contenu du champ image du formulaire. Il faut ici
         * utiliser la méthode getPart().
         */
        String nomFichier = null;
        InputStream contenuFichier = null;
        try {
            Part part = request.getPart( CHAMP_IMAGE );
            nomFichier = getNomFichier( part );

            /*
             * Si la méthode getNomFichier() a renvoyé quelque chose, il s'agit
             * donc d'un champ de type fichier (input type="file").
             */
            if ( nomFichier != null && !nomFichier.isEmpty() ) {
                /*
                 * Antibug pour Internet Explorer, qui transmet pour une raison
                 * mystique le chemin du fichier local à la machine du client...
                 * 
                 * Ex : C:/dossier/sous-dossier/fichier.ext
                 * 
                 * On doit donc faire en sorte de ne sélectionner que le nom et
                 * l'extension du fichier, et de se débarrasser du superflu.
                 */
                nomFichier = nomFichier.substring( nomFichier.lastIndexOf( '/' ) + 1 )
                        .substring( nomFichier.lastIndexOf( '\\' ) + 1 );

                /* Récupération du contenu du fichier */
                contenuFichier = part.getInputStream();

                /* Extraction du type MIME du fichier depuis l'InputStream */
                MimeUtil.registerMimeDetector( "eu.medsea.mimeutil.detector.MagicMimeMimeDetector" );
                Collection<?> mimeTypes = MimeUtil.getMimeTypes( contenuFichier );

                /*
                 * Si le fichier est bien une image, alors son en-tête MIME
                 * commence par la chaîne "image"
                 */
                if ( mimeTypes.toString().startsWith( "image" ) ) {
                    /* Écriture du fichier sur le disque */
                    ecrireFichier( contenuFichier, nomFichier, chemin );
                } else {
                    throw new FormValidationException( "Le fichier envoyé doit être une image." );
                }
            }
        } catch ( IllegalStateException e ) {
            /*
             * Exception retournée si la taille des données dépasse les limites
             * définies dans la section <multipart-config> de la déclaration de
             * notre servlet d'upload dans le fichier web.xml
             */
            e.printStackTrace();
            throw new FormValidationException( "Le fichier envoyé ne doit pas dépasser 1Mo." );
        } catch ( IOException e ) {
            /*
             * Exception retournée si une erreur au niveau des répertoires de
             * stockage survient (répertoire inexistant, droits d'accès
             * insuffisants, etc.)
             */
            e.printStackTrace();
            throw new FormValidationException( "Erreur de configuration du serveur." );
        } catch ( ServletException e ) {
            /*
             * Exception retournée si la requête n'est pas de type
             * multipart/form-data.
             */
            e.printStackTrace();
            throw new FormValidationException(
                    "Ce type de requête n'est pas supporté, merci d'utiliser le formulaire prévu pour envoyer votre fichier." );
        }

        return nomFichier;
    }

    /*
     * Ajoute un message correspondant au champ spécifié à la map des erreurs.
     */
    private void setErreur( String champ, String message ) {
        erreurs.put( champ, message );
    }

    /*
     * Méthode utilitaire qui retourne null si un champ est vide, et son contenu
     * sinon.
     */
    private static String getValeurChamp( HttpServletRequest request, String nomChamp ) {
        String valeur = request.getParameter( nomChamp );
        if ( valeur == null || valeur.trim().length() == 0 ) {
            return null;
        } else {
            return valeur;
        }
    }

    /*
     * Méthode utilitaire qui a pour unique but d'analyser l'en-tête
     * "content-disposition", et de vérifier si le paramètre "filename" y est
     * présent. Si oui, alors le champ traité est de type File et la méthode
     * retourne son nom, sinon il s'agit d'un champ de formulaire classique et
     * la méthode retourne null.
     */
    private static String getNomFichier( Part part ) {
        /* Boucle sur chacun des paramètres de l'en-tête "content-disposition". */
        for ( String contentDisposition : part.getHeader( "content-disposition" ).split( ";" ) ) {
            /* Recherche de l'éventuelle présence du paramètre "filename". */
            if ( contentDisposition.trim().startsWith( "filename" ) ) {
                /*
                 * Si "filename" est présent, alors renvoi de sa valeur,
                 * c'est-à-dire du nom de fichier sans guillemets.
                 */
                return contentDisposition.substring( contentDisposition.indexOf( '=' ) + 1 ).trim().replace( "\"", "" );
            }
        }
        /* Et pour terminer, si rien n'a été trouvé... */
        return null;
    }

    /*
     * Méthode utilitaire qui a pour but d'écrire le fichier passé en paramètre
     * sur le disque, dans le répertoire donné et avec le nom donné.
     */
    private void ecrireFichier( InputStream contenuFichier, String nomFichier, String chemin )
            throws FormValidationException {
        /* Prépare les flux. */
        BufferedInputStream entree = null;
        BufferedOutputStream sortie = null;
        try {
            /* Ouvre les flux. */
            entree = new BufferedInputStream( contenuFichier, TAILLE_TAMPON );
            sortie = new BufferedOutputStream( new FileOutputStream( new File( chemin + nomFichier ) ),
                    TAILLE_TAMPON );

            /*
             * Lit le fichier reçu et écrit son contenu dans un fichier sur le
             * disque.
             */
            byte[] tampon = new byte[TAILLE_TAMPON];
            int longueur = 0;
            while ( ( longueur = entree.read( tampon ) ) > 0 ) {
                sortie.write( tampon, 0, longueur );
            }
        } catch ( Exception e ) {
            throw new FormValidationException( "Erreur lors de l'écriture du fichier sur le disque." );
        } finally {
            try {
                sortie.close();
            } catch ( IOException ignore ) {
            }
            try {
                entree.close();
            } catch ( IOException ignore ) {
            }
        }
    }
}

Code de CreationCommandeForm :

package com.sdzee.tp.forms;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.joda.time.DateTime;

import com.sdzee.tp.beans.Client;
import com.sdzee.tp.beans.Commande;
import com.sdzee.tp.dao.ClientDao;
import com.sdzee.tp.dao.CommandeDao;
import com.sdzee.tp.dao.DAOException;

public final class CreationCommandeForm {
    private static final String CHAMP_CHOIX_CLIENT     = "choixNouveauClient";
    private static final String CHAMP_LISTE_CLIENTS    = "listeClients";
    private static final String CHAMP_DATE             = "dateCommande";
    private static final String CHAMP_MONTANT          = "montantCommande";
    private static final String CHAMP_MODE_PAIEMENT    = "modePaiementCommande";
    private static final String CHAMP_STATUT_PAIEMENT  = "statutPaiementCommande";
    private static final String CHAMP_MODE_LIVRAISON   = "modeLivraisonCommande";
    private static final String CHAMP_STATUT_LIVRAISON = "statutLivraisonCommande";

    private static final String ANCIEN_CLIENT          = "ancienClient";
    private static final String SESSION_CLIENTS        = "clients";
    private static final String FORMAT_DATE            = "dd/MM/yyyy HH:mm:ss";

    private String              resultat;
    private Map<String, String> erreurs                = new HashMap<String, String>();
    private ClientDao           clientDao;
    private CommandeDao         commandeDao;

    public CreationCommandeForm( ClientDao clientDao, CommandeDao commandeDao ) {
        this.clientDao = clientDao;
        this.commandeDao = commandeDao;
    }

    public Map<String, String> getErreurs() {
        return erreurs;
    }

    public String getResultat() {
        return resultat;
    }

    public Commande creerCommande( HttpServletRequest request, String chemin ) {
        Client client;
        /*
         * Si l'utilisateur choisit un client déjà existant, pas de validation à
         * effectuer
         */
        String choixNouveauClient = getValeurChamp( request, CHAMP_CHOIX_CLIENT );
        if ( ANCIEN_CLIENT.equals( choixNouveauClient ) ) {
            /* Récupération de l'id du client choisi */
            String idAncienClient = getValeurChamp( request, CHAMP_LISTE_CLIENTS );
            Long id = null;
            try {
                id = Long.parseLong( idAncienClient );
            } catch ( NumberFormatException e ) {
                setErreur( CHAMP_CHOIX_CLIENT, "Client inconnu, merci d'utiliser le formulaire prévu à cet effet." );
                id = 0L;
            }
            /* Récupération de l'objet client correspondant dans la session */
            HttpSession session = request.getSession();
            client = ( (Map<Long, Client>) session.getAttribute( SESSION_CLIENTS ) ).get( id );
        } else {
            /*
             * Sinon on garde l'ancien mode, pour la validation des champs.
             * 
             * L'objet métier pour valider la création d'un client existe déjà,
             * il est donc déconseillé de dupliquer ici son contenu ! À la
             * place, il suffit de passer la requête courante à l'objet métier
             * existant et de récupérer l'objet Client créé.
             */
            CreationClientForm clientForm = new CreationClientForm( clientDao );
            client = clientForm.creerClient( request, chemin );

            /*
             * Et très important, il ne faut pas oublier de récupérer le contenu
             * de la map d'erreur créée par l'objet métier CreationClientForm
             * dans la map d'erreurs courante, actuellement vide.
             */
            erreurs = clientForm.getErreurs();
        }

        /*
         * Ensuite, il suffit de procéder normalement avec le reste des champs
         * spécifiques à une commande.
         */

        /* Récupération de la date dans un DateTime Joda. */
        DateTime dt = new DateTime();

        String montant = getValeurChamp( request, CHAMP_MONTANT );
        String modePaiement = getValeurChamp( request, CHAMP_MODE_PAIEMENT );
        String statutPaiement = getValeurChamp( request, CHAMP_STATUT_PAIEMENT );
        String modeLivraison = getValeurChamp( request, CHAMP_MODE_LIVRAISON );
        String statutLivraison = getValeurChamp( request, CHAMP_STATUT_LIVRAISON );

        Commande commande = new Commande();

        try {
            traiterClient( client, commande );

            commande.setDate( dt );

            traiterMontant( montant, commande );
            traiterModePaiement( modePaiement, commande );
            traiterStatutPaiement( statutPaiement, commande );
            traiterModeLivraison( modeLivraison, commande );
            traiterStatutLivraison( statutLivraison, commande );

            if ( erreurs.isEmpty() ) {
                commandeDao.creer( commande );
                resultat = "Succès de la création de la commande.";
            } else {
                resultat = "Échec de la création de la commande.";
            }
        } catch ( DAOException e ) {
            setErreur( "imprévu", "Erreur imprévue lors de la création." );
            resultat = "Échec de la création de la commande : une erreur imprévue est survenue, merci de réessayer dans quelques instants.";
            e.printStackTrace();
        }

        return commande;
    }

    private void traiterClient( Client client, Commande commande ) {
        if ( client == null ) {
            setErreur( CHAMP_CHOIX_CLIENT, "Client inconnu, merci d'utiliser le formulaire prévu à cet effet." );
        }
        commande.setClient( client );
    }

    private void traiterMontant( String montant, Commande commande ) {
        double valeurMontant = -1;
        try {
            valeurMontant = validationMontant( montant );
        } catch ( FormValidationException e ) {
            setErreur( CHAMP_MONTANT, e.getMessage() );
        }
        commande.setMontant( valeurMontant );
    }

    private void traiterModePaiement( String modePaiement, Commande commande ) {
        try {
            validationModePaiement( modePaiement );
        } catch ( FormValidationException e ) {
            setErreur( CHAMP_MODE_PAIEMENT, e.getMessage() );
        }
        commande.setModePaiement( modePaiement );
    }

    private void traiterStatutPaiement( String statutPaiement, Commande commande ) {
        try {
            validationStatutPaiement( statutPaiement );
        } catch ( FormValidationException e ) {
            setErreur( CHAMP_STATUT_PAIEMENT, e.getMessage() );
        }
        commande.setStatutPaiement( statutPaiement );
    }

    private void traiterModeLivraison( String modeLivraison, Commande commande ) {
        try {
            validationModeLivraison( modeLivraison );
        } catch ( FormValidationException e ) {
            setErreur( CHAMP_MODE_LIVRAISON, e.getMessage() );
        }
        commande.setModeLivraison( modeLivraison );
    }

    private void traiterStatutLivraison( String statutLivraison, Commande commande ) {
        try {
            validationStatutLivraison( statutLivraison );
        } catch ( FormValidationException e ) {
            setErreur( CHAMP_STATUT_LIVRAISON, e.getMessage() );
        }
        commande.setStatutLivraison( statutLivraison );
    }

    private double validationMontant( String montant ) throws FormValidationException {
        double temp;
        if ( montant != null ) {
            try {
                temp = Double.parseDouble( montant );
                if ( temp < 0 ) {
                    throw new FormValidationException( "Le montant doit être un nombre positif." );
                }
            } catch ( NumberFormatException e ) {
                temp = -1;
                throw new FormValidationException( "Le montant doit être un nombre." );
            }
        } else {
            temp = -1;
            throw new FormValidationException( "Merci d'entrer un montant." );
        }
        return temp;
    }

    private void validationModePaiement( String modePaiement ) throws FormValidationException {
        if ( modePaiement != null ) {
            if ( modePaiement.length() < 2 ) {
                throw new FormValidationException( "Le mode de paiement doit contenir au moins 2 caractères." );
            }
        } else {
            throw new FormValidationException( "Merci d'entrer un mode de paiement." );
        }
    }

    private void validationStatutPaiement( String statutPaiement ) throws FormValidationException {
        if ( statutPaiement != null && statutPaiement.length() < 2 ) {
            throw new FormValidationException( "Le statut de paiement doit contenir au moins 2 caractères." );
        }
    }

    private void validationModeLivraison( String modeLivraison ) throws FormValidationException {
        if ( modeLivraison != null ) {
            if ( modeLivraison.length() < 2 ) {
                throw new FormValidationException( "Le mode de livraison doit contenir au moins 2 caractères." );
            }
        } else {
            throw new FormValidationException( "Merci d'entrer un mode de livraison." );
        }
    }

    private void validationStatutLivraison( String statutLivraison ) throws FormValidationException {
        if ( statutLivraison != null && statutLivraison.length() < 2 ) {
            throw new FormValidationException( "Le statut de livraison doit contenir au moins 2 caractères." );
        }
    }

    /*
     * Ajoute un message correspondant au champ spécifié à la map des erreurs.
     */
    private void setErreur( String champ, String message ) {
        erreurs.put( champ, message );
    }

    /*
     * Méthode utilitaire qui retourne null si un champ est vide, et son contenu
     * sinon.
     */
    private static String getValeurChamp( HttpServletRequest request, String nomChamp ) {
        String valeur = request.getParameter( nomChamp );
        if ( valeur == null || valeur.trim().length() == 0 ) {
            return null;
        } else {
            return valeur;
        }
    }
}

Code des servlets

Contenu du fichier web.xml :

<?xml version="1.0" encoding="UTF-8"?>
<web-app>
    <filter>
        <filter-name>Set Character Encoding</filter-name>
        <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>ignore</param-name>
            <param-value>false</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>Set Character Encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <filter>
        <filter-name>PrechargementFilter</filter-name>
        <filter-class>com.sdzee.tp.filters.PrechargementFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>PrechargementFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <listener>
	<listener-class>com.sdzee.tp.config.InitialisationDaoFactory</listener-class>
    </listener>
    
	<servlet>
		<servlet-name>CreationClient</servlet-name>
		<servlet-class>com.sdzee.tp.servlets.CreationClient</servlet-class>
		<init-param>
			<param-name>chemin</param-name> 
			<param-value>/fichiers/images/</param-value> 
		</init-param>
		<multipart-config>
			<location>c:/fichiers/images</location>
			<max-file-size>2097152</max-file-size> <!-- 2 Mo -->
			<max-request-size>10485760</max-request-size> <!-- 5 x 2Mo -->
			<file-size-threshold>1048576</file-size-threshold> <!-- 1 Mo -->
		</multipart-config>
	</servlet>
	<servlet>
		<servlet-name>ListeClients</servlet-name>
		<servlet-class>com.sdzee.tp.servlets.ListeClients</servlet-class>
	</servlet>
	<servlet>
		<servlet-name>SuppressionClient</servlet-name>
		<servlet-class>com.sdzee.tp.servlets.SuppressionClient</servlet-class>
	</servlet>
	<servlet>
		<servlet-name>CreationCommande</servlet-name>
		<servlet-class>com.sdzee.tp.servlets.CreationCommande</servlet-class>
		<init-param>
			<param-name>chemin</param-name> 
			<param-value>/fichiers/images/</param-value> 
		</init-param>
		<multipart-config>
			<location>c:/fichiers/images</location>
			<max-file-size>2097152</max-file-size> <!-- 2 Mo -->
			<max-request-size>10485760</max-request-size> <!-- 5 x 2Mo -->
			<file-size-threshold>1048576</file-size-threshold> <!-- 1 Mo -->
		</multipart-config>
	</servlet>
	<servlet>
		<servlet-name>ListeCommandes</servlet-name>
		<servlet-class>com.sdzee.tp.servlets.ListeCommandes</servlet-class>
	</servlet>
	<servlet>
		<servlet-name>SuppressionCommande</servlet-name>
		<servlet-class>com.sdzee.tp.servlets.SuppressionCommande</servlet-class>
	</servlet>
	<servlet>
		<servlet-name>Image</servlet-name>
		<servlet-class>com.sdzee.tp.servlets.Image</servlet-class>
		<init-param>
			<param-name>chemin</param-name> 
			<param-value>/fichiers/images/</param-value> 
		</init-param>
	</servlet>

	
	<servlet-mapping>
		<servlet-name>CreationClient</servlet-name>
		<url-pattern>/creationClient</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>ListeClients</servlet-name>
		<url-pattern>/listeClients</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>SuppressionClient</servlet-name>
		<url-pattern>/suppressionClient</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>CreationCommande</servlet-name>
		<url-pattern>/creationCommande</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>ListeCommandes</servlet-name>
		<url-pattern>/listeCommandes</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>SuppressionCommande</servlet-name>
		<url-pattern>/suppressionCommande</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>Image</servlet-name>
		<url-pattern>/images/*</url-pattern>
	</servlet-mapping>
</web-app>

Code de la servlet CreationClient :

package com.sdzee.tp.servlets;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.sdzee.tp.beans.Client;
import com.sdzee.tp.dao.ClientDao;
import com.sdzee.tp.dao.DAOFactory;
import com.sdzee.tp.forms.CreationClientForm;

public class CreationClient extends HttpServlet {
    public static final String CONF_DAO_FACTORY = "daofactory";
    public static final String CHEMIN           = "chemin";
    public static final String ATT_CLIENT       = "client";
    public static final String ATT_FORM         = "form";
    public static final String SESSION_CLIENTS  = "clients";

    public static final String VUE_SUCCES       = "/WEB-INF/afficherClient.jsp";
    public static final String VUE_FORM         = "/WEB-INF/creerClient.jsp";

    private ClientDao          clientDao;

    public void init() throws ServletException {
        /* Récupération d'une instance de notre DAO Utilisateur */
        this.clientDao = ( (DAOFactory) getServletContext().getAttribute( CONF_DAO_FACTORY ) ).getClientDao();
    }

    public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException {
        /* À la réception d'une requête GET, simple affichage du formulaire */
        this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response );
    }

    public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException {
        /*
         * Lecture du paramètre 'chemin' passé à la servlet via la déclaration
         * dans le web.xml
         */
        String chemin = this.getServletConfig().getInitParameter( CHEMIN );

        /* Préparation de l'objet formulaire */
        CreationClientForm form = new CreationClientForm( clientDao );

        /* Traitement de la requête et récupération du bean en résultant */
        Client client = form.creerClient( request, chemin );

        /* Ajout du bean et de l'objet métier à l'objet requête */
        request.setAttribute( ATT_CLIENT, client );
        request.setAttribute( ATT_FORM, form );

        /* Si aucune erreur */
        if ( form.getErreurs().isEmpty() ) {
            /* Alors récupération de la map des clients dans la session */
            HttpSession session = request.getSession();
            Map<Long, Client> clients = (HashMap<Long, Client>) session.getAttribute( SESSION_CLIENTS );
            /* Si aucune map n'existe, alors initialisation d'une nouvelle map */
            if ( clients == null ) {
                clients = new HashMap<Long, Client>();
            }
            /* Puis ajout du client courant dans la map */
            clients.put( client.getId(), client );
            /* Et enfin (ré)enregistrement de la map en session */
            session.setAttribute( SESSION_CLIENTS, clients );

            /* Affichage de la fiche récapitulative */
            this.getServletContext().getRequestDispatcher( VUE_SUCCES ).forward( request, response );
        } else {
            /* Sinon, ré-affichage du formulaire de création avec les erreurs */
            this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response );
        }
    }
}

Code de la servlet CreationCommande :

package com.sdzee.tp.servlets;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.sdzee.tp.beans.Client;
import com.sdzee.tp.beans.Commande;
import com.sdzee.tp.dao.ClientDao;
import com.sdzee.tp.dao.CommandeDao;
import com.sdzee.tp.dao.DAOFactory;
import com.sdzee.tp.forms.CreationCommandeForm;

public class CreationCommande extends HttpServlet {
    public static final String CONF_DAO_FACTORY      = "daofactory";
    public static final String CHEMIN                = "chemin";
    public static final String ATT_COMMANDE          = "commande";
    public static final String ATT_FORM              = "form";
    public static final String SESSION_CLIENTS       = "clients";
    public static final String APPLICATION_CLIENTS   = "initClients";
    public static final String SESSION_COMMANDES     = "commandes";
    public static final String APPLICATION_COMMANDES = "initCommandes";

    public static final String VUE_SUCCES            = "/WEB-INF/afficherCommande.jsp";
    public static final String VUE_FORM              = "/WEB-INF/creerCommande.jsp";

    private ClientDao          clientDao;
    private CommandeDao        commandeDao;

    public void init() throws ServletException {
        /* Récupération d'une instance de nos DAO Client et Commande */
        this.clientDao = ( (DAOFactory) getServletContext().getAttribute( CONF_DAO_FACTORY ) ).getClientDao();
        this.commandeDao = ( (DAOFactory) getServletContext().getAttribute( CONF_DAO_FACTORY ) ).getCommandeDao();
    }

    public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException {
        /* À la réception d'une requête GET, simple affichage du formulaire */
        this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response );
    }

    public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException {
        /*
         * Lecture du paramètre 'chemin' passé à la servlet via la déclaration
         * dans le web.xml
         */
        String chemin = this.getServletConfig().getInitParameter( CHEMIN );

        /* Préparation de l'objet formulaire */
        CreationCommandeForm form = new CreationCommandeForm( clientDao, commandeDao );

        /* Traitement de la requête et récupération du bean en résultant */
        Commande commande = form.creerCommande( request, chemin );

        /* Ajout du bean et de l'objet métier à l'objet requête */
        request.setAttribute( ATT_COMMANDE, commande );
        request.setAttribute( ATT_FORM, form );

        /* Si aucune erreur */
        if ( form.getErreurs().isEmpty() ) {
            /* Alors récupération de la map des clients dans la session */
            HttpSession session = request.getSession();
            Map<Long, Client> clients = (HashMap<Long, Client>) session.getAttribute( SESSION_CLIENTS );
            /* Si aucune map n'existe, alors initialisation d'une nouvelle map */
            if ( clients == null ) {
                clients = new HashMap<Long, Client>();
            }
            /* Puis ajout du client de la commande courante dans la map */
            clients.put( commande.getClient().getId(), commande.getClient() );
            /* Et enfin (ré)enregistrement de la map en session */
            session.setAttribute( SESSION_CLIENTS, clients );

            /* Ensuite récupération de la map des commandes dans la session */
            Map<Long, Commande> commandes = (HashMap<Long, Commande>) session.getAttribute( SESSION_COMMANDES );
            /* Si aucune map n'existe, alors initialisation d'une nouvelle map */
            if ( commandes == null ) {
                commandes = new HashMap<Long, Commande>();
            }
            /* Puis ajout de la commande courante dans la map */
            commandes.put( commande.getId(), commande );
            /* Et enfin (ré)enregistrement de la map en session */
            session.setAttribute( SESSION_COMMANDES, commandes );

            /* Affichage de la fiche récapitulative */
            this.getServletContext().getRequestDispatcher( VUE_SUCCES ).forward( request, response );
        } else {
            /* Sinon, ré-affichage du formulaire de création avec les erreurs */
            this.getServletContext().getRequestDispatcher( VUE_FORM ).forward( request, response );
        }
    }
}

Code de la servlet SuppressionClient :

package com.sdzee.tp.servlets;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.sdzee.tp.beans.Client;
import com.sdzee.tp.dao.ClientDao;
import com.sdzee.tp.dao.DAOException;
import com.sdzee.tp.dao.DAOFactory;

public class SuppressionClient extends HttpServlet {
    public static final String CONF_DAO_FACTORY = "daofactory";
    public static final String PARAM_ID_CLIENT  = "idClient";
    public static final String SESSION_CLIENTS  = "clients";

    public static final String VUE              = "/listeClients";

    private ClientDao          clientDao;

    public void init() throws ServletException {
        /* Récupération d'une instance de notre DAO Utilisateur */
        this.clientDao = ( (DAOFactory) getServletContext().getAttribute( CONF_DAO_FACTORY ) ).getClientDao();
    }

    public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException {
        /* Récupération du paramètre */
        String idClient = getValeurParametre( request, PARAM_ID_CLIENT );
        Long id = Long.parseLong( idClient );

        /* Récupération de la Map des clients enregistrés en session */
        HttpSession session = request.getSession();
        Map<Long, Client> clients = (HashMap<Long, Client>) session.getAttribute( SESSION_CLIENTS );

        /* Si l'id du client et la Map des clients ne sont pas vides */
        if ( id != null && clients != null ) {
            try {
                /* Alors suppression du client de la BDD */
                clientDao.supprimer( clients.get( id ) );
                /* Puis suppression du client de la Map */
                clients.remove( id );
            } catch ( DAOException e ) {
                e.printStackTrace();
            }
            /* Et remplacement de l'ancienne Map en session par la nouvelle */
            session.setAttribute( SESSION_CLIENTS, clients );
        }

        /* Redirection vers la fiche récapitulative */
        response.sendRedirect( request.getContextPath() + VUE );
    }

    /*
     * Méthode utilitaire qui retourne null si un paramètre est vide, et son
     * contenu sinon.
     */
    private static String getValeurParametre( HttpServletRequest request, String nomChamp ) {
        String valeur = request.getParameter( nomChamp );
        if ( valeur == null || valeur.trim().length() == 0 ) {
            return null;
        } else {
            return valeur;
        }
    }
}

Code de la servlet SuppressionCommande :

package com.sdzee.tp.servlets;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.sdzee.tp.beans.Commande;
import com.sdzee.tp.dao.CommandeDao;
import com.sdzee.tp.dao.DAOException;
import com.sdzee.tp.dao.DAOFactory;

public class SuppressionCommande extends HttpServlet {
    public static final String CONF_DAO_FACTORY  = "daofactory";
    public static final String PARAM_ID_COMMANDE = "idCommande";
    public static final String SESSION_COMMANDES = "commandes";

    public static final String VUE               = "/listeCommandes";

    private CommandeDao        commandeDao;

    public void init() throws ServletException {
        /* Récupération d'une instance de notre DAO Utilisateur */
        this.commandeDao = ( (DAOFactory) getServletContext().getAttribute( CONF_DAO_FACTORY ) ).getCommandeDao();
    }

    public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException {
        /* Récupération du paramètre */
        String idCommande = getValeurParametre( request, PARAM_ID_COMMANDE );
        Long id = Long.parseLong( idCommande );

        /* Récupération de la Map des commandes enregistrées en session */
        HttpSession session = request.getSession();
        Map<Long, Commande> commandes = (HashMap<Long, Commande>) session.getAttribute( SESSION_COMMANDES );

        /* Si l'id de la commande et la Map des commandes ne sont pas vides */
        if ( id != null && commandes != null ) {
            try {
                /* Alors suppression de la commande de la BDD */
                commandeDao.supprimer( commandes.get( id ) );
                /* Puis suppression de la commande de la Map */
                commandes.remove( id );
            } catch ( DAOException e ) {
                e.printStackTrace();
            }
            /* Et remplacement de l'ancienne Map en session par la nouvelle */
            session.setAttribute( SESSION_COMMANDES, commandes );
        }

        /* Redirection vers la fiche récapitulative */
        response.sendRedirect( request.getContextPath() + VUE );
    }

    /*
     * Méthode utilitaire qui retourne null si un paramètre est vide, et son
     * contenu sinon.
     */
    private static String getValeurParametre( HttpServletRequest request, String nomChamp ) {
        String valeur = request.getParameter( nomChamp );
        if ( valeur == null || valeur.trim().length() == 0 ) {
            return null;
        } else {
            return valeur;
        }
    }
}

Code du filtre

Code du filtre de préchargement :

package com.sdzee.tp.filters;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import com.sdzee.tp.beans.Client;
import com.sdzee.tp.beans.Commande;
import com.sdzee.tp.dao.ClientDao;
import com.sdzee.tp.dao.CommandeDao;
import com.sdzee.tp.dao.DAOFactory;

public class PrechargementFilter implements Filter {
    public static final String CONF_DAO_FACTORY      = "daofactory";
    public static final String ATT_SESSION_CLIENTS   = "clients";
    public static final String ATT_SESSION_COMMANDES = "commandes";

    private ClientDao          clientDao;
    private CommandeDao        commandeDao;

    public void init( FilterConfig config ) throws ServletException {
        /* Récupération d'une instance de nos DAO Client et Commande */
        this.clientDao = ( (DAOFactory) config.getServletContext().getAttribute( CONF_DAO_FACTORY ) ).getClientDao();
        this.commandeDao = ( (DAOFactory) config.getServletContext().getAttribute( CONF_DAO_FACTORY ) )
                .getCommandeDao();
    }

    public void doFilter( ServletRequest req, ServletResponse res, FilterChain chain ) throws IOException,
            ServletException {
        /* Cast de l'objet request */
        HttpServletRequest request = (HttpServletRequest) req;

        /* Récupération de la session depuis la requête */
        HttpSession session = request.getSession();

        /*
         * Si la map des clients n'existe pas en session, alors l'utilisateur se
         * connecte pour la première fois et nous devons précharger en session
         * les infos contenues dans la BDD.
         */
        if ( session.getAttribute( ATT_SESSION_CLIENTS ) == null ) {
            /*
             * Récupération de la liste des clients existants, et enregistrement
             * en session
             */
            List<Client> listeClients = clientDao.lister();
            Map<Long, Client> mapClients = new HashMap<Long, Client>();
            for ( Client client : listeClients ) {
                mapClients.put( client.getId(), client );
            }
            session.setAttribute( ATT_SESSION_CLIENTS, mapClients );
        }

        /*
         * De même pour la map des commandes
         */
        if ( session.getAttribute( ATT_SESSION_COMMANDES ) == null ) {
            /*
             * Récupération de la liste des commandes existantes, et
             * enregistrement en session
             */
            List<Commande> listeCommandes = commandeDao.lister();
            Map<Long, Commande> mapCommandes = new HashMap<Long, Commande>();
            for ( Commande commande : listeCommandes ) {
                mapCommandes.put( commande.getId(), commande );
            }
            session.setAttribute( ATT_SESSION_COMMANDES, mapCommandes );
        }

        /* Pour terminer, poursuite de la requête en cours */
        chain.doFilter( request, res );
    }

    public void destroy() {
    }
}

Code des JSP

Code de listerClients.jsp :

<%@ page pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Liste des clients existants</title>
        <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"></c:url>" />
    </head>
    <body>
        <c:import url="/inc/menu.jsp" ></c:import>
        <div id="corps">
        <c:choose>
            <%-- Si aucun client n'existe en session, affichage d'un message par défaut. --%>
            <c:when test="${ empty sessionScope.clients }">
                <p class="erreur">Aucun client enregistré.</p>
            </c:when>
            <%-- Sinon, affichage du tableau. --%>
            <c:otherwise>
            <table>
                <tr>
                    <th>Nom</th>
                    <th>Prénom</th>
                    <th>Adresse</th>
                    <th>Téléphone</th>
                    <th>Email</th>
                    <th>Image</th>
                    <th class="action">Action</th>                    
                </tr>
                <%-- Parcours de la Map des clients en session, et utilisation de l'objet varStatus. --%>
                <c:forEach items="${ sessionScope.clients }" var="mapClients" varStatus="boucle">
                <%-- Simple test de parité sur l'index de parcours, pour alterner la couleur de fond de chaque ligne du tableau. --%>
                <tr class="${boucle.index % 2 == 0 ? 'pair' : 'impair'}">
                    <%-- Affichage des propriétés du bean Client, qui est stocké en tant que valeur de l'entrée courante de la map --%>
                    <td><c:out value="${ mapClients.value.nom }"></c:out></td>
                    <td><c:out value="${ mapClients.value.prenom }"></c:out></td>
                    <td><c:out value="${ mapClients.value.adresse }"></c:out></td>
                    <td><c:out value="${ mapClients.value.telephone }"></c:out></td>
                    <td><c:out value="${ mapClients.value.email }"></c:out></td>
                    <td>
                        <%-- On ne construit et affiche un lien vers l'image que si elle existe. --%>
                        <c:if test="${ !empty mapClients.value.image }">
                            <c:set var="image"><c:out value="${ mapClients.value.image }"></c:out></c:set>
                            <a href="<c:url value="/images/${ image }"></a>">Voir</a>
                        </c:if>
                    </td>
                    <%-- Lien vers la servlet de suppression, avec passage du nom du client - c'est-à-dire la clé de la Map - en paramètre grâce à la balise <c:param></c:param>. --%>
                    <td class="action">
                        <a href="<c:url value="/suppressionClient"><c:param name="idClient" value="${ mapClients.key }" ></c:param></c:url>">
                            <img src="<c:url value="/inc/supprimer.png"></c:url>" alt="Supprimer" />
                        </a>
                    </td>
                </tr>
                </c:forEach>
            </table>
            </c:otherwise>
        </c:choose>
        </div>
    </body>
</html>

Code de listerCommandes.jsp :

<%@ page pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="joda" uri="http://www.joda.org/joda/time/tags" %>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Liste des commandes existantes</title>
        <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"></c:url>" />
    </head>
    <body>
        <c:import url="/inc/menu.jsp" ></c:import>
        <div id="corps">
        <c:choose>
            <%-- Si aucune commande n'existe en session, affichage d'un message par défaut. --%>
            <c:when test="${ empty sessionScope.commandes }">
                <p class="erreur">Aucune commande enregistrée.</p>
            </c:when>
            <%-- Sinon, affichage du tableau. --%>
            <c:otherwise>
            <table>
                <tr>
                    <th>Client</th>
                    <th>Date</th>
                    <th>Montant</th>
                    <th>Mode de paiement</th>
                    <th>Statut de paiement</th>
                    <th>Mode de livraison</th>
                    <th>Statut de livraison</th>
                    <th class="action">Action</th>                    
                </tr>
                <%-- Parcours de la Map des commandes en session, et utilisation de l'objet varStatus. --%>
                <c:forEach items="${ sessionScope.commandes }" var="mapCommandes" varStatus="boucle">
                <%-- Simple test de parité sur l'index de parcours, pour alterner la couleur de fond de chaque ligne du tableau. --%>
                <tr class="${boucle.index % 2 == 0 ? 'pair' : 'impair'}">
                    <%-- Affichage des propriétés du bean Commande, qui est stocké en tant que valeur de l'entrée courante de la map --%>
                    <td><c:out value="${ mapCommandes.value.client.prenom } ${ mapCommandes.value.client.nom }"></c:out></td>
                    <td><joda:format value="${ mapCommandes.value.date }" pattern="dd/MM/yyyy HH:mm:ss"></joda:format></td>
                    <td><c:out value="${ mapCommandes.value.montant }"></c:out></td>
                    <td><c:out value="${ mapCommandes.value.modePaiement }"></c:out></td>
                    <td><c:out value="${ mapCommandes.value.statutPaiement }"></c:out></td>
                    <td><c:out value="${ mapCommandes.value.modeLivraison }"></c:out></td>
                    <td><c:out value="${ mapCommandes.value.statutLivraison }"></c:out></td>
                    <%-- Lien vers la servlet de suppression, avec passage de la date de la commande - c'est-à-dire la clé de la Map - en paramètre grâce à la balise <c:param></c:param>. --%>
                    <td class="action">
                        <a href="<c:url value="/suppressionCommande"><c:param name="idCommande" value="${ mapCommandes.key }" ></c:param></c:url>">
                            <img src="<c:url value="/inc/supprimer.png"></c:url>" alt="Supprimer" />
                        </a>
                    </td>
                </tr>
                </c:forEach>
            </table>
            </c:otherwise>
        </c:choose>
        </div>
    </body>
</html>

Code de creerCommande.jsp :

<%@ page pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="joda" uri="http://www.joda.org/joda/time/tags" %>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Création d'une commande</title>
        <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"></c:url>" />
    </head>
    <body>
        <c:import url="/inc/menu.jsp" ></c:import>
        <div>
            <form method="post" action="<c:url value="/creationCommande"></form>" enctype="multipart/form-data">
                <fieldset>
                    <legend>Informations client</legend>
                    <%-- Si et seulement si la Map des clients en session n'est pas vide, alors on propose un choix à l'utilisateur --%>
                    <c:if test="${ !empty sessionScope.clients }">
                        <label for="choixNouveauClient">Nouveau client ? <span class="requis">*</span></label>
                        <input type="radio" id="choixNouveauClient" name="choixNouveauClient" value="nouveauClient" checked /> Oui
                        <input type="radio" id="choixNouveauClient" name="choixNouveauClient" value="ancienClient" /> Non
                        <br/><br />
                    </c:if>
                    
                    <c:set var="client" value="${ commande.client }" scope="request" ></c:set>
                    <div id="nouveauClient">
                        <c:import url="/inc/inc_client_form.jsp" ></c:import>
                    </div>
                    
                    <%-- Si et seulement si la Map des clients en session n'est pas vide, alors on crée la liste déroulante --%>
                    <c:if test="${ !empty sessionScope.clients }">
                    <div id="ancienClient">
                        <select name="listeClients" id="listeClients">
                            <option value="">Choisissez un client...</option>
                            <%-- Boucle sur la map des clients --%>
                            <c:forEach items="${ sessionScope.clients }" var="mapClients">
                            <%--  L'expression EL ${mapClients.value} permet de cibler l'objet Client stocké en tant que valeur dans la Map, 
                                  et on cible ensuite simplement ses propriétés nom et prenom comme on le ferait avec n'importe quel bean. --%>
                            <option value="${ mapClients.key }">${ mapClients.value.prenom } ${ mapClients.value.nom }</option>
                            </c:forEach>
                        </select>
                    </div>
                    </c:if>
                </fieldset>
                <fieldset>
                    <legend>Informations commande</legend>
                    
                    <label for="dateCommande">Date <span class="requis">*</span></label>
                    <input type="text" id="v" name="dateCommande" value="<joda:format value="${ commande.date }" pattern="dd/MM/yyyy HH:mm:ss"></joda:format>" size="30" maxlength="30" disabled />
                    <span class="erreur">${form.erreurs['dateCommande']}</span>
                    <br />
                    
                    <label for="montantCommande">Montant <span class="requis">*</span></label>
                    <input type="text" id="montantCommande" name="montantCommande" value="<c:out value="${commande.montant}"></c:out>" size="30" maxlength="30" />
                    <span class="erreur">${form.erreurs['montantCommande']}</span>
                    <br />
                    
                    <label for="modePaiementCommande">Mode de paiement <span class="requis">*</span></label>
                    <input type="text" id="modePaiementCommande" name="modePaiementCommande" value="<c:out value="${commande.modePaiement}"></c:out>" size="30" maxlength="30" />
                    <span class="erreur">${form.erreurs['modePaiementCommande']}</span>
                    <br />
                    
                    <label for="statutPaiementCommande">Statut du paiement</label>
                    <input type="text" id="statutPaiementCommande" name="statutPaiementCommande" value="<c:out value="${commande.statutPaiement}"></c:out>" size="30" maxlength="30" />
                    <span class="erreur">${form.erreurs['statutPaiementCommande']}</span>
                    <br />
                    
                    <label for="modeLivraisonCommande">Mode de livraison <span class="requis">*</span></label>
                    <input type="text" id="modeLivraisonCommande" name="modeLivraisonCommande" value="<c:out value="${commande.modeLivraison}"></c:out>" size="30" maxlength="30" />
                    <span class="erreur">${form.erreurs['modeLivraisonCommande']}</span>
                    <br />
                    
                    <label for="statutLivraisonCommande">Statut de la livraison</label>
                    <input type="text" id="statutLivraisonCommande" name="statutLivraisonCommande" value="<c:out value="${commande.statutLivraison}"></c:out>" size="30" maxlength="30" />
                    <span class="erreur">${form.erreurs['statutLivraisonCommande']}</span>
                    <br />
                    
                    <p class="info">${ form.resultat }</p>
                </fieldset>
                <input type="submit" value="Valider"  />
                <input type="reset" value="Remettre à zéro" /> <br />
            </form>
        </div>
        
        <%-- Inclusion de la bibliothèque jQuery. Vous trouverez des cours sur JavaScript et jQuery aux adresses suivantes :
               - http://www.siteduzero.com/tutoriel-3-309961-dynamisez-vos-sites-web-avec-javascript.html 
               - http://www.siteduzero.com/tutoriel-3-659477-un-site-web-dynamique-avec-jquery.html 
               
             Si vous ne souhaitez pas télécharger et ajouter jQuery à votre projet, vous pouvez utiliser la version fournie directement en ligne par Google :
             <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script> 
        --%>
        <script src="<c:url value="/inc/jquery.js"></script>"></script>
        
        <%-- Petite fonction jQuery permettant le remplacement de la première partie du formulaire par la liste déroulante, au clic sur le bouton radio. --%>
        <script>
        	jQuery(document).ready(function(){
        		/* 1 - Au lancement de la page, on cache le bloc d'éléments du formulaire correspondant aux clients existants */
        		$("div#ancienClient").hide();
        		/* 2 - Au clic sur un des deux boutons radio "choixNouveauClient", on affiche le bloc d'éléments correspondant (nouveau ou ancien client) */
                jQuery('input[name=choixNouveauClient]:radio').click(function(){
                	$("div#nouveauClient").hide();
                	$("div#ancienClient").hide();
                    var divId = jQuery(this).val();
                    $("div#"+divId).show();
                });
            });
        </script>
    </body>
</html>

Code de afficherCommande.jsp :

<%@ page pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="joda" uri="http://www.joda.org/joda/time/tags" %>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Affichage d'une commande</title>
        <link type="text/css" rel="stylesheet" href="<c:url value="/inc/style.css"></c:url>" />
    </head>
    <body>
        <c:import url="/inc/menu.jsp" ></c:import>
        <div id="corps">
            <p class="info">${ form.resultat }</p>
            <p>Client</p>
            <p>Nom : <c:out value="${ commande.client.nom }"></c:out></p>
            <p>Prénom : <c:out value="${ commande.client.prenom }"></c:out></p>
            <p>Adresse : <c:out value="${ commande.client.adresse }"></c:out></p>
            <p>Numéro de téléphone : <c:out value="${ commande.client.telephone }"></c:out></p>
            <p>Email : <c:out value="${ commande.client.email }"></c:out></p>
            <p>Image : <c:out value="${ commande.client.image }"></c:out></p>
            <p>Commande</p>
            <p>Date  : <joda:format value="${ commande.date }" pattern="dd/MM/yyyy HH:mm:ss"></joda:format></p> 
            <p>Montant  : <c:out value="${ commande.montant }"></c:out></p> 
            <p>Mode de paiement  : <c:out value="${ commande.modePaiement }"></c:out></p> 
            <p>Statut du paiement  : <c:out value="${ commande.statutPaiement }"></c:out></p> 
            <p>Mode de livraison  : <c:out value="${ commande.modeLivraison }"></c:out></p> 
            <p>Statut de la livraison  : <c:out value="${ commande.statutLivraison }"></c:out></p> 
        </div>
    </body>
</html>
Exemple de certificat de réussite
Exemple de certificat de réussite