Aller au contenu

Exercice 3 - Utilisation de JPA⚓︎

Nous allons modifier le projet Web Dynamique, appelé GestionNotes, afin d'utiliser l'API JPA.

1. Utilisation du runtime WildFly⚓︎

Tout d'abord, il faut modifier la runtime du projet. Comme indiqué plus tôt, Tomcat est très léger, et ne permet pas d'utiliser l'API JPA.

Pour cela, il suffit de faire un clic droit sur le projet, puis :material-mouse:Properties, puis :material-file-tree:Project Facets. Dans l'onglet Runtimes, déselectionner Tomcat et sélectionner la runtime WildFly à la place :

Choix runtime

2. Activation de JPA⚓︎

Pour pouvoir utiliser JPA, il faut l'indiquer dans les "facettes" du projet.

Pour cela, au même endroit (il faut faire un clic droit sur le projet GestionNotes, cliquer sur :material-mouse:Properties et aller dans :material-file-tree:Project Facets), cocher la case JPA et enfin cliquer sur Apply and Close :

Activation JPA

Dans la vue Explorer, le fichier 📄persistence.xml apparaît. Il va nous permettre de gérer la persistance des beans Java :

Activation JPA

3. Configuration de JPA⚓︎

Le fichier 📄persistence.xml permet de configurer toutes la partie persistance.

Ouvrir ce fichier. Dans Eclipse, il y a plusieurs onglets. Le dernier, Source, permet de modifier le fichier XML directement. Tous les autres sont une "interface graphique" qui permet d'effectuer ces modifications plus simplement. Ici, nous allons utiliser cette "interface graphique". Il est indiqué ci-dessous, dans le bloc "Si besoin, modification manuelle du fichier 📄persistence.xml", comment faire ces mêmes modifications directement dans le XML.

  • Dans l'onglet Connection :
    • Dans Transaction Type, indiquer JTA.
    • Dans JTA data source, précier le nom de la source créées précédemment. Normalement, il s'agit de java:/MySqlGestionNotesJPA.
  • Dans l'onglet Schema Generation :
    • Dans Database action, sélectionner Drop and Create.

      :warning::warning: Attention, cela signifie qu'à chaque redémarrage de l'application, toutes les tables du schéma sont supprimées et recréées. C'est très pratique en mode développement, puisque des champs peuvent être ajoutés et/ou supprimés régulièrement, mais une fois que les développements sont terminés, il faut bien sûr repasser cette valeur à None. :warning::warning:

Si besoin, modification manuelle du fichier 📄persistence.xml

Dans ce fichier 📄persistence.xml :

  1. Le nom de l'unité de persistance (attribut name de la balise persistence-unit) est très important, il faut l'indiquer dans les classes DAO pour indiquer sur quelle base de données les requêtes doivent être effectuées.
    Par défaut, il n'y a qu'une seule unité de persistance, dont le nom est le nom du projet Java (GestionNotes ici).
  2. Nous allons indiquer que nous utilisons l'API JTA pour gérer les transactions. Pour cela, il faut ajouter l'attribut transaction-type="JTA" dans la balise <persistence-unit>.
  3. À l'intérieur de cette balise <persistence-unit>, nous allons ajouter la balise fille <jta-data-source>java:/MySqlGestionNotesJPA</jta-data-source> pour faire le lien avec la datasource créée précédemment (on indique le nom JNDI).
  4. Enfin, on indique la stratégie utilisée au redémarrage du serveur. Ici, nous sommes en mode "développement", nous allons donc supprimer et re-créer la table correspondante à chaque redémarrage. Pour cela, à l'intérieur de <persistence-unit>, ajouter une balise <properties>. Cette balise va elle même contenir la balise fille suivante :

    📄 persistence.xml
    1
    2
    3
    <properties>
        <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
    </properties>
    

4. Gestion du modèle - Persistance du bean⚓︎

Le bean étant déjà créé, il va falloir faire des modifications "à la main" pour gérer la persistance. Lorsqu'on en créé un nouveau, ce que vous ferez par exemple dans le TP noté, c'est plus facile (Cf. bloc "Pour la prochaine fois, création d'un nouveau bean JPA" ci-dessous).

  1. Nous allons commencer par indiquer que le bean fr.univtours.polytech.gestionnotes.model.NoteBean doit être "persisté". Pour cela, il suffit de faire un clic droit sur la classe, puis :material-mouse:JPA Tools > Add to Persistence Unit.

    Effet dans 📄persistence.xml

    Dans le fichier 📄persistence.xml, la ligne ci-dessous a été automatiquement ajoutée :

    📄 persistence.xml
    1
    <class>fr.univtours.polytech.gestionnotes.model.NoteBean</class>
    

  2. Une erreur apparaît alors, qui indique que le bean est référencé, mais pas annoté. Il faut donc annoter ce bean. Pour cela, on ajoute l'annotation @Entity :

    ☕ Code Java - Entité NoteBean
    1
    2
    @Entity//(1)!
    public class NoteBean implements Serializable {//(2)!
    
    1. Indique qu'il s'agit d'un bean JPA.
    2. Et oui, c'est un bean, il ne faut donc pas oublier que la classe doit implémenter java.io.Serializable !

    Comme la nouvelle erreur l'indique, il faut préciser quelle est la clef primaire. Sur la propriété id, on indique donc cela. On en profite également pour préciser que la valeur est auto incrémentée (ce qui n'est pas une obligation) :

    ☕ Code Java - Entité NoteBean
    1
    2
    3
    @Id //(1)!
    @GeneratedValue(strategy = GenerationType.IDENTITY) //(2)!
    private Integer id;
    
    1. Indique qu'il s'agit de la clef primaire.
    2. Cela correspond exactement au auto_increment que nous avions mis sur le champ id de la table NOTE dans l'exercice 1 de ce TD.
    Changement du nom de la table

    Par défaut, le nom du bean doit être le même que le nom de la table en base. De même, les noms des propriétés de la classe doivent être les mêmes que les noms de champs de la table.

    Si ce n'est pas le cas, il est bien sûr possible de le préciser dans les annotations. Ici par exemple, la table s'appelle NOTE, alors que le bean s'appelle NoteBean. Pour que le lien (le mapping) puisse se faire correctement, il faut le préciser dans l'annotation :

    ☕ Code Java - Entité NoteBean
    1
    2
    3
    @Entity
    @Table(name = "NOTE")//(1)!
    public class NoteBean implements Serializable {
    
    1. Il n'y a que cette ligne à rajouter ici.

    Ce site liste les différentes options possibles.

Pour la prochaine fois, création d'un nouveau bean JPA

Il est possible de créer directement un bean, en précisant qu'il est persisté avec JPA.

Pour cela, il suffit de faire un clic droit sur le projet, puis :material-mouse:New > JPA Entity. Indiquer fr.univtours.polytech.gestionnotes.model comme package, et NoteBean comme classe, puis cliquer sur Next >.

Création GestionNotes-JPA
Ajouter ensuite les trois champs listés ci-dessus en utilisant la bouton Add... (attention à bien cocher le champ id comme key), puis clique sur Finish
Création GestionNotes-JPA
Nous allons également préciser la stratégie de gestion du champ id. On souhaite qu'il soit automatiquement incrémenté (d'un en un). On ajoute donc, juste en dessous de l'annotation @id, l'annotation @GeneratedValue(strategy = GenerationType.IDENTITY). On a donc :
☕ Code Java - Entité NoteBean
1
2
3
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) //(1)!
private Integer id;

  1. Cela correspond exactement au auto_increment que nous avions mis sur le champ id de la table NOTE dans l'exercice 1 de ce TD.
Le fichier 📄persistence.xml

Le fichier doit maintenant ressembler à cela :

📄 persistence.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
    <persistence-unit name="GestionNotes" transaction-type="JTA"><!--(1)!-->
        <jta-data-source>java:/MySqlGestionNotesJPA</jta-data-source><!--(2)!-->
        <class>fr.univtours.polytech.gestionnotes.model.NoteBean</class><!--(3)!-->
        <properties>
            <property
                name="javax.persistence.schema-generation.database.action"
                value="drop-and-create" /><!--(4)!-->
        </properties>
    </persistence-unit>
</persistence>

  1. Dans les DAOs, on aura besoin du nom de l'unité de persistance pour savoir sur quelle base de données envoyer les requêtes.

    Plus d'infos sur transaction-type ici.
  2. Ici, il faut indiquer le même nom JNDI que celui précisé à la création de la Data Source précédemment.
  3. Liste tous les beans qui sont persistés avec JPA.
  4. Cela signifie qu'à chaque démarrage du serveur, les tables correspondantes sont supprimées et recréées. On évitera bien sûr de laisser cette option en environnement de production ...
Vérification en base de données

Il est maintenant temps de tester. Pour cela, on démarre le serveur (si ce n'est pas déjà fait), puis on publie le projet (GestionNotes) sur le serveur. Pour cela, on fait un clic droit sur le serveur, :material-mouse:Add and Remove..., et on déploie GestionNotes.

On vérifie que la table Note est bien créée en BDD (et vide), avec les quatre champs indiqués.

3. Création de la couche DAO⚓︎

Nous allons créer une nouvelle implémentation de cette couche (c'est-à-dire une nouvelle classe implémentant l'interface NotesDAO) :

  1. On crée un objet EntityManager, avec le nom de l'unité de persistance en paramètre (celle indiquée dans l'attribut name de la balise persistence-unit dans le 📄persistence.xml).
  2. Cet objet gère pour nous les requêtes en BDD, avec le langage HQL (pour Hibernate Query Language) - appelé également JPAQL (pour Java Persistence API Query Language) - et l'utilisation des méthodes find, persist, createQuery, ...

Voici maintenant à quoi ressemble la classe NotesDAOImplJPA :

☕ Code Java - Classe NotesDAOImplJPA
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class NotesDAOImplJPA implements NotesDAO {

    // L'objet EntityManager qui va permettre d'effectuer les requêtes en BDD.
    private EntityManager em;

    public NotesDAOImplJPA() {
        // Le constructeur public sans argument a pour rôle d'instancier l'objet EntityManager.
        // Pour cela, on utilise une "Factory" qui a besoin du nom de l'unité de persistance.
        // On la retrouve dans le fichier persistence.xml, dans l'attribute "name"
        // de la balise "persistence-unit".
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("GestionNotes");
        this.em = emf.createEntityManager();
    }

    @Override
    public List<NoteBean> getNotesList() {
        // Exemple de requête HQL (ou JPAQL).
        Query requete = em.createQuery("select n from NoteBean n");
        return requete.getResultList();
    }

    @Override
    public NoteBean getNote(Integer id) {
        // Récupération d'un enregistrement avec sa clef.
        NoteBean note = (NoteBean) em.find(NoteBean.class, id);
        return note;
    }

    @Override
    public void insertNote(NoteBean note) {
        // Insertion d'un enregistrement en BDD.
        em.getTransaction().begin();
        em.persist(note);
        em.getTransaction().commit();
    }
}

4. Modification de la couche métier⚓︎

Il n'y a qu'une seule modification à effectuer : modifier l'implémentation du DAO utilisée. Pour cela, dans le constructeur de la classe NotesBusinessImpl, remplacer la ligne existante par :

☕ Code Java - Classe NotesBusinessImpl - Contructeur
1
2
3
public NotesBusinessImpl() {
    this.dao = new NotesDAOImplJPA();
}

5. Récupération de la couche présentation⚓︎

Il n'y a aucune modification à apporter sur cette couche pour le moment.

Vérification

Vérifier maintenant que cette nouvelle version de l'application est bien fonctionnelle.

Contrairement à Tomcat où il fallait redémarrer le serveur à chaque modification, avec WildFly, il suffit de déployer l'application, en faisant un clic droit, puis :material-mouse:Full Publish.

Il est bien sûr possible de faire à chaque fois un redémarrage du serveur, mais cette méthode est plus rapide, surtout lorsqu'il y a plusieurs applications déployées sur le serveur.