Tp3 : ajout de relations dans le TP2

De $1

Introduction

Cette fois-ci nous allons jouer avec les relations. Nous allons ajouter au TP2 des relations entre des clients et des adresses.

Ajout d'une relation 1-1 unidirectionnelle pour lier utilisateur et adresse

Nous allons ajouter une relation liant un utilisateur et une adresse. Plusieurs utilisateurs peuvent habiter à la même adresse. 

Comme une adresse peut être partagée par deux utilisateurs (John Lennon et Ringo Starr peuvent habiter ensembles par exemple), on ne fera ni insertion, ni suppression en cascade.

Création de l'entité Adresse

Vous ajouterez au projet une classe entité Adresse, possèdant quelques propriétés. Dans cet exercice on se limite pour le moment au nom de la ville et au code postal. On utilisera une annotation de la "Bean Validation API" pour mettre des contraintes sur le code postal et sur le nom de la ville. Si jamais on essaie de créer une Adresse avec des attributs ne respectant pas ces contraintes, ou si on fait un setCity(...) ou setCodePostal(...) et que les paramètres ne sont pas valides, une exception sera levée.

@Entity
public class Adresse implements Serializable {
  
    
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    @NotNull @Size(min=1)
    private String ville;
    @Pattern(regexp="[0-9]{5}") // On accepte les codes postaux du type "06410"
    private String codePostal;

    public Adresse() {}
    
    public Adresse(String ville, String codePostal) {
        this.ville = ville;
        this.codePostal = codePostal;
    }
    ... compléter avec les getters et setters pour chaque propriété
}

N'oubliez pas de générer les getters et setters pour chacune des propriétés. 

Ajout de la propriété "adresse" correspondant à la relation, dans la classe Utilisateur

Dans le fichier Utilisateur.java, ajoutez manuellement un attribut de type Adresse :

private Adresse adresse;

Normalement une petite lampe jaune doit apparaitre sur la gauche, car NetBeans a détecté que Adresse était de type entité. En cliquant sur la lampe on va voir apparaitre un menu proposant d'ajouter une annotation pour déclarer une relation entre entités:

relation1.jpg

Choisissez la première option, nous allons bien créer une relation 1-1 unidirectionnelle entre Utilisateur et Personne. Normalement une annotation @OneToOne doit apparaître au-dessus de la propriété.

Pensez à générer des getters et setters pour la propriété "adresse".

Modification de la méthode "creerUtilisateursDeTest" pour créer des personnes avec des adresses

Nous allons commencer par modifier la méthode qui créée un utilisateur pour lui ajouter systématiquement une adresse (on suppose que l'adresse est en base avant que l'utilisateur soit créé) :

public Utilisateur creeUtilisateur(String prenom, String nom, String login, 
            Adresse a) {
        Utilisateur u = new Utilisateur(nom, prenom, login);
        // On met à jour la relation, elle est déjà en base
        u.setAdresse(a);
        
        // On persiste l'utilisateur, la relation est déjà en base, cela va donc
        // ajouter une ligne dans la table des utilisateur avec une clé étrangère
        // correspondant à l'adresse
        em.persist(u);
        
        return u; 
    }

Et donc voici la fonction qui créée des utilisateurs de test, modifiée pour qu'avant d'ajouter dans la base de données des utilisateurs on ait ajouté quelques adresses, que l'on utilisera pour construire les utilisateurs :

public void creerUtilisateursDeTest() {
        // On cree des adresses et on les insère dans la base
        Adresse biot = new Adresse("Biot", "06410");
        em.persist(biot);
        Adresse valbonne = new Adresse("Valbonne", "06560");
        em.persist(valbonne);
        Adresse nice = new Adresse("Nice", "06000");
        em.persist(nice);
        
        // Note : après un persist, les objets sont connectés
        
        // John et Paul habitent à Biot
        Utilisateur john = creeUtilisateur("John", "Lennon", "jlennon", biot);
        Utilisateur paul = creeUtilisateur("Paul", "Mac Cartney", "pmc", biot);
        
        
        Utilisateur ringo = creeUtilisateur("Ringo", "Starr", "rstarr", nice);
        Utilisateur georges = creeUtilisateur("Georges", "Harisson", "georgesH", valbonne);
    }

Voilà, donc ci-dessus, dans un premier temps on fait des em.persis() sur des adresses. Puis on passe les adresses à la méthode creeUtilisateur(). Rappel du cours : après un persist, un select les objets sont "connectés" (on dit "managés" dans le jargon JavaEE). DOnc quand on fait, dans la méthode creeUtilisateur, un u.setAdresse(a), a est bien un objet en base, connecté. Et quand in fait em.persist(u), à ce moment là la relation dans la table des Utilisateur (uné clé étrangère correspondant à une clé primaire dans la table des adresses, est ajoutée).

Exécutez votre projet, crééz des utilisateurs (soit c'est fait au déploiement si vous avez un ContextListener, soit il faut cliquer sur le bouton "crééer 4 utilisateurs de test"), puis regardez l'état de vos tables, et les données. Normalement vous devez avoir :

relation2.jpg

relations3.jpg

relation4.jpg 

Affichage des adresses dans la page JSP

Si vous vous souveznez du cours, les relations de type 1-1 sont toujours gérées par défaut en mode "agressif", c'est-à-dire que si on récupère en base un utilisateur (soit avec un em.find() pour le chercher par son id, soit par un select), le contenu de ses relations est également chargé en mémoire. En d'autres termes, pour notre exemple, un u.getAdresse().getVille() sur un utilisateur, si u n'est pas connecté, doit afficher la ville puisque celle-ci aura été lue en mémoire après un em.find() ou un select(). 

Dans le cas d'un chargement "lazy" (fainéant), les relations ne sont lues en mémoire que si l'objet est connecté et que après un get... sur la propriété de la relation. C'est le cas par défaut des relations 1-N et N-N.

Pour afficher la ville et le code postal où habitent les Beatles, rien de plus simple, on va utiliser le langage EL des pages JSP pour accèder à la propriété ville et à la propriété codePostal, de la propriété adresse des utilisateurs.

Nous allons odifier comme suit les lignes affichant la liste des utilisateurs :

<!-- Zone qui affiche les utilisateurs si le paramètre action vaut listerComptes -->
        <c:if test="${param.action == 'listerLesUtilisateurs'}" >
            <h2>Liste des utilisateurs</h2>

            <table border="10">
                <!-- La ligne de titre du tableau des comptes -->
                <tr>
                    <td><b>Login</b></td>
                    <td><b>Nom</b></td>
                    <td><b>Prénom</b></td>
                    <td><b>Ville</b></td>
                    <td><b>Code postal</b></td>
                </tr>

                <!-- Ici on affiche les lignes, une par utilisateur -->
                <!-- cette variable montre comment on peut utiliser JSTL et EL pour calculer -->
                <c:set var="total" value="0"/>

                <c:forEach var="u" items="${listeDesUsers}">
                    <tr>
                        <td>${u.login}</td>
                        <td>${u.firstname}</td>
                        <td>${u.lastname}</td>
                        <td>${u.adresse.ville}</td>
                        <td>${u.adresse.codePostal}</td>
                        <!-- On compte le nombre de users -->
                        <c:set var="total" value="${total+1}"/>
                    </tr>
                </c:forEach>

                <!-- Affichage du solde total dans la dernière ligne du tableau -->
                <tr><td><b>TOTAL</b></td><td></td><td><b>${total}</b></td><td></td></tr>
            </table>

        </c:if>

Notez qu'on a juste ajouté deux <TD> pour les en-têtes du tableau et qu'on a ajouté deux lignes dans la boucle pour afficher la ville et le code postal :

<td>${u.adresse.ville}</td>
<td>${u.adresse.codePostal}</td>

Ces lignes sont exécutent un u.getAdresse().getVille() et un u.getAdresse().getCodePostal(). C'est le principe des propriétés manipulées avec le langage EL.

Passage en bi-directionnel : on veut pourvoir afficher la liste des personnes habitant une même ville

Cette fois ci on va juste changer le type de la relation. Effacez dans la classe Utilisateur.java l'annotation existant pour la relation et choisissez dans le menu déroulant de l'ampoule jaune "ManyToOne bidirectional", acceptez les valeurs par défaut dans la fenêtre dialogue qui apparait.

Remarquez que la classe Utilisateur.java ne change pas beaucoup, le choix dans le menu a ajouté l'annotation @ManyToOne au lieu de @OneToOne :

  @ManyToOne   private Adresse adresse;

Dans la classe Adresse.java en revanche les choses ont changé, les lignes suivantes ont été rajoutées :

@OneToMany(mappedBy = "adresse")
private List<Utilisateur> utilisateurs;

Là, nous vous conseillons ici une bonne pratique concernant les relations du côté "Many" :

  1. Toujours initaliser la propriété avec une liste vide,
  2. Ajouter un getter sur la propriété : getUtilisateurs()
  3. Ajouter des méthodes addUtilisateur(...) et removeUtilisateur(...) dans la classe

Ces deux astuces vous simplifieront la vie et éviterons bien des erreurs. Voici donc les lignes qui sont réellement à ajouter dans la classe Adresse.java :

@OneToMany(mappedBy = "adresse")
   private List<Utilisateur> utilisateurs = new ArrayList<>();
   ...
   ...
   public void addUtilisateur(Utilisateur u) {
        utilisateurs.add(u);
    }

    public void removeUtilisateur(Utilisateur u) {
        utilisateurs.remove(u);
    }
    public List<Utilisateur> getUtilisateurs() {
        return utilisateurs;
    }

Il reste à modifier la création des utilisateurs de test dans GestionnaireUtilisateurs.java, pour bien affecter la relation des deux côtés. Voici la version modifée de la méthode creerUtilisateur(...) :

public Utilisateur creeUtilisateur(String prenom, String nom, String login, 
            Adresse a) {
        Utilisateur u = new Utilisateur(nom, prenom, login);
        // On met à jour la relation, elle est déjà en base
        u.setAdresse(a);
        
        // a est déjà en base et connectée, donc la ligne suivante modifie les 
        // données pour relier l'adresse à l'utilisateur
        a.addUtilisateur(u);
        
        // On persiste l'utilisateur, la relation est déjà en base, cela va donc
        // ajouter une ligne dans la table des utilisateur avec une clé étrangère
        // correspondant à l'adresse
        em.persist(u);
        
        return u; 
    }

La ligne importante est a.addUtilisateur(u) ici, que l'on a rajoutée !

Affichage des utilisateurs par Adresse dans la page JSP

On va commencer par ajouter un lien <a href=""...></a> dans le tableau, sur l'affichage du nom de la ville. En cliquant sur la ville, on affichera une nouvelle page qui affichera la liste des utilisateurs habitant une ville donnée :

On modifie donc index.jsp en conséquence :

   <td>
     <a href="ServletUsers?action=listerUtilisateursParVille&idville=${u.adresse.id}">
        ${u.adresse.ville}
     </a>
   </td>
                    

On aura donc sur chaque nom de ville un lien vers l'URL : ServletUsers?action=listerUtilisateursParVille&idville=${u.adresse.id}

Ce qui revient à appeler ServletUsers avec un GET, en passant les paramètres HTTTP suivants :

  • action = listerUtilisateursParVille
  • idVille = id de l'adresse (de la ville)

On ajoute dans la servlet un test pour traiter cette nouvelle action :

if (action.equals("listerLesUtilisateurs")) {
              ...
            } else if (action.equals("listerUtilisateursParVille")) {
                // on récupère le paramètre idVille
                int idVille = Integer.parseInt(request.getParameter("idVille"));

                // On récupère la liste des utilisateurs pour la ville
                Collection<Utilisateur> liste = gestionnaireUtilisateurs.getUsersParVille(idVille);

                // et on passe le résultat à la JSP pour affichage...
                request.setAttribute("listeDesUsers", liste);
                forwardTo = "index.jsp?action=listerLesUtilisateurs";
                message = "Liste des utilisateurs pour la ville No : " + idVille;
            } else if (action.equals("creerUtilisateursDeTest")) {
                ...

Ne manque qu'à voir l'implémentation de la méthode getUsersParVille(idVille) dans le gestionnaire d'utilisateurs :

GestionnaireUtilisateurs.java, la méthode fait deux lignes :

public Collection<Utilisateur> getUsersParVille(int idVille) {
        Adresse a = em.find(Adresse.class, idVille);

        // a est connecté, le get va déclencher un select
        return a.getUtilisateurs();
    }

Voilà.... !

A vous de jouer !

Complétez l'adresse en ajoutant un numéro de rue, complétez l'utilisateur en ajoutant une liste de numéros de téléphones sous forme de relations. On veut évidemment afficher le No de rue et la liste des numéros de téléphones dans le tableau figurant dans la page JSP. Vous ajoutez à chaque utilisateur au moins deux numéros de téléphones en précisant dans la classe Telephone.java une propriété indiquant si c'est un mobile un fixe, etc.

Si vous êtres arrivés là, essayez de rajouter un formulaire pour ajouter un numéro de téléphone à un utilisateur existant, ou bien pour en enlever un.

 

 

Mots clés:
 
Images (4)
Voir 1 - 4 sur 4 images | Voir tout
Aucune description
relations...  Actions
Aucune description
relation4...  Actions
Aucune description
relation2...  Actions
Aucune description
relation1...  Actions
Commentaires (0)
Vous devez être connecté pour poster un commentaire.