tete du loic

 Loïc YON [KIUX]

  • Enseignant-chercheur
  • Référent Formation Continue
  • Responsable des contrats pros ingénieur
  • Référent entrepreneuriat
  • Responsable de la filière F2 ingénieur
  • Secouriste Sauveteur du Travail
mail
loic.yon@isima.fr
phone
(+33 / 0) 4 73 40 50 42
location_on
ISIMA
  • twitter
  • linkedin
  • viadeo

[JEE] JSF & JPA

Date de première publication : 2012/06/04

Objectif et contexte

L'objectif de cet exercice est d'ajouter le support de JPA au carnet d'adresse.

Mise en place de la persistance

Création de la base

Nous avons besoin de préparer le serveur en déclarant une base de données. Dans l'onglet Services, s'assurer que le SGBD est bien démarré (par exemple Java DB).

Avec le clic bouton droit, créer une base de données. Il faut renseigner :

On voit alors une nouvelle connexion disponible. si l'on clique sur cette nouvelle connexion, il est possible de faire des opérations sur la base : création de tables, insertion de données ...

Entité : lien classe/BDD

Nous allons saupoudrer la classe Personne pour qu'elle devienne une entité et nous permettre ainsi de faire le lien avec la base de données

@Entity
public class Personne {

La classe Personne doit implémenter l'interface Serializable.

Il faut également s'occuper de la clé primaire. Deux propositions pour cette clé primaire :

  1. une clé primaire simple comme le mail
  2. une clé primaire composite comme par exemple (nom, prenom)

Pour la première proposition, c'est tout simple :

@Id private String mail;

Pour la seconde, c'est un tout petit plus compliqué... Deux écritures JAVA (pour un même résultat SQL) sont envisageables. Je choisis celui qui a le moins d'impact sur la classe Personne mais qui est aussi le moins documenté : l'IdClass

@Entity
@IdClass(Composite.class)
public class Personne {
   @Id private String nom;
   @Id private String prenom;
   private String tel;
   // ...
}

Il faut maintenant écrire la classe Composite :

public class Composite implements Serializable {
    public String nom;
    public String prenom;

    public Composite() {
        this(null, null);
    }
    
    public Composite(String n, String p) {
        nom = n;
        prenom = p;
    }
}

Il faut également redéfinir les méthodes equals() et hashCode(). Les attributs nom et prenom de la classe Composite sont bien publics

Le reste ? C'est automatique avec la configuration par exception

Unité de persistence

Il faut maintenant faire le lien entre la base que l'on vient de créer et notre application

Si vous avez commencé à taper du code spécifique (style EntityManager ou EntityManagerFactory), l'aide contextuelle va vous proposer de créer une nouvelle unité de persistance. Sinon, il suffit d'aller dans le menu :
(M) New > (O) Persistence > New Persistence Unit

Sélectionner New Data source, nous envoie sur une nouvelle boîte de dialogue

Lorsque l'opération est terminée, un fichier persistence.xml apparaît dans le répertoire des fichiers de configuration. Son édition nous montre que, par défaut, toutes les entités du projet vont être gérées automatiquement par l'unité de persistence.

En situation

Injection

Grâce à l'injection de dépendance, on peut directement utiliser l'unité de persistence que l'on vient de créer.

@PersistenceUnit(unitName="CarnetJPAPU")
EntityManagerFactory emf;

Pour tester, on peut ajouter un élément dans la base de données :

em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(personnes.get(0));
em.getTransaction().commit();

Vous pouvez maintenant ajouter tous les éléments précédents et mettre en commentaire le code : ajouter des éléments qui existent déjà lève une exception

Vous pouvez obtenir plusieurs erreurs

  1. l'injection ne se fait pas : emf est toujours null
  2. L'utilisation des transactions ne vous est pas permise

Résolution des erreurs

Si l'injection de dépendance ne se passe pas comme vous le voulez, il faut tout d'abord vérifier que vous avez autorisé l'injection de dépendance et de contexte lors de la création du projet. Cela peut se voir si un fichier de configuration : bean.xml est présent ou non.

Si ce n'est pas le cas, pas de panique, cela se répare comme cela

(M) File > New (O) Contexts & Dependancy injection > Beans.xml (CDI)

Une autre raison pour laquelle l'injection de dépendances ne se fait pas, c'est que vous cherchez à la faire au "mauvais moment". Ennéffé, elle n'a lieu qu'après la construction du bean managé.

Si vous avez placé votre code dans le constructeur, il faut le déplacer dans une méthode qui sera exécutée après la construction

@PostConstruct
public void methode() {  
    // et le tour est joué !
} 

Si l'utilisation des transactions ne vous est pas permise, c'est que vous avez activé JTA où la gestion des transactions est automatique.

Vérifier la présence d'un élément

La présence d'un élément se fait grâce à ma méthode find() de l'EntityManager

Personne p = em.find(Personne.class, "un_mail_dans_la_base");

ou

Personne p = em.find(Personne.class, new Composite("yon","loic");

Lire tous les enregistrements de la base

On va permettre la lecture des enregistrements de la base grâce à une requête nommée.

Coté entité :

@Entity
@NamedQuery(name = Personne.FIND_ALL, 
            query="select p from Personne p")
public class Personne implements Serializable {
   // ... 
   public static final String FIND_ALL = "Personne.findAll";
   // ...
} 

Coté bean managé :

Query query = em.createNamedQuery(Personne.FIND_ALL);
personnes.addAll(query.getResultList());

Sympa, non ?

Aller plus loin

Je vous propose de créer une nouvelle page Web qui affichera un formulaire pour insérer une nouvelle personne dans la base. Vous pouvez utiliser les balises panelGrid (avec l'attribut columns), outputLabel, inputText et buttonCommand

L'action à réaliser du formulaire va être de créer une nouvelle instance de Personne et de l'insérer dans la base en persistant l'entité.

Cependant, cette opération peut lever une exception, notamment si la personne est déjà présente dans la base. Dans ce cas, je vous propose d'afficher un message d'erreur dans la page de saisie elle-même.

Coté xhtml, il faut placer la zone de message :

<h:messages globalOnly="true" errorClass="error" />

On peut jouer avec les attributs infoXXX, errorXXX et warnXXX en fonction du type de message que l'on veut afficher et layout si on préfère un tableau aux puces.

Côté Bean, il faut générer le message comme suit :

FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(
	FacesMessage.SEVERITY_ERROR  , "Personne déjà présente dans la base", null));

Je vous propose d'essayer les règles de navigation :

La méthode du bean renvoie failure ou success au lien de renvoyer le nom de la page directement.

Pour faire le lien entre le nom et la page, il suffit d'utiliser l'éditeur graphique disponible sur le fichier de configuration faces-config.xml à créer ou à enrichir.

Il faut tracer un lien entre les pages correspondantes et cliquer sur le nom de la flèche pour préciser le message.

C'est terminé pour cette fois ! On pourrait encore