Différences
Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
d3:tp04:jpa [2025/10/12 19:59] – [L'EJB] dthevenot | d3:tp04:jpa [2025/10/12 20:25] (Version actuelle) – supprimée dthevenot | ||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
- | ====== D3-TP04 : Projet gestion de clients - Persistence des données avec un ORM - JPA de Payara ====== | ||
- | < | ||
- | ===== ORM ===== | ||
- | Un ORM est un ensemble de bibliothèques permettant de mapper (faire correspondre) les structures de données orientées objet d'un langage de programmation avec les tables d'une base de données relationnelle. L' | ||
- | |||
- | L'ORM offre plusieurs avantages significatifs : | ||
- | |||
- | * Abstraction des détails de la base de données : Le développeur n'a pas besoin de connaître le langage SQL spécifique au SGBDR utilisé. | ||
- | * Productivité accrue : Les opérations CRUD (Create, Read, Update, Delete) sont simplifiées, | ||
- | * Maintenabilité : Le code est plus lisible et plus facile à maintenir, car il reflète directement les structures de données utilisées par l' | ||
- | * Sécurité : Beaucoup d'ORMs intègrent des mécanismes de prévention des injections SQL. | ||
- | ===== JPA ===== | ||
- | JPA est un acronyme qui signifie **Java Persistence API**. C'est évolution de l' | ||
- | |||
- | ==== 1-La datasource ==== | ||
- | Dans Payara il faut une **DataSource JDBC jdbc/ | ||
- | |||
- | Définir la ressource JNDI sur ton serveur | ||
- | - Aller sur : http:// | ||
- | - Menu : Resources → JDBC → JDBC Connection Pools | ||
- | - Créer un pool de connexions (ex. : MonPoolMariaDB) | ||
- | - Pool Name : MonPoolMariaDB | ||
- | - Resource Type : javax.sql.DataSource | ||
- | - Classname : org.mariadb.jdbc.MariaDbDataSource | ||
- | - Database Vendor : MySQL (MariaDB est compatible MySQL) | ||
- | - Next | ||
- | - Propriétés additionnelles : | ||
- | - Finish | ||
- | < | ||
- | < | ||
- | * d’accéder à différents services de nommage ou de répertoire de façon uniforme ; | ||
- | * d' | ||
- | * de faire des opérations sur des annuaires (java naming and directory interface) | ||
- | </ | ||
- | Créer la JDBC Resource (source de données JDBC) : | ||
- | - Menu : Resources → JDBC → JDBC Resources | ||
- | - Clique sur New... | ||
- | - Configure : | ||
- | - JNDI Name : **jdbc/ | ||
- | - Pool Name : monPoolMariaDB | ||
- | - Clique sur OK | ||
- | |||
- | |||
- | ==== 2-Configurer la persistence avec JPA ==== | ||
- | === a) Les dépendances JPA === | ||
- | Le projet va inclure : | ||
- | * Jakarta Persistence (jakarta.persistence-api) | ||
- | * Une implémentation JPA (ex. : EclipseLink, | ||
- | * Le driver MariaDB (mariadb-java-client) | ||
- | <code ruby pom.xml> | ||
- | < | ||
- | < | ||
- | < | ||
- | < | ||
- | < | ||
- | </ | ||
- | < | ||
- | < | ||
- | < | ||
- | < | ||
- | </ | ||
- | </ | ||
- | === b) persistence.xml === | ||
- | Ce fichier indique à JPA comment se connecter à la base de données. | ||
- | |||
- | Dans WEB-INF/ | ||
- | <code ruby persistence.xml> | ||
- | < | ||
- | < | ||
- | < | ||
- | < | ||
- | < | ||
- | < | ||
- | < | ||
- | < | ||
- | <!-- Optionnel : génération du schéma --> | ||
- | < | ||
- | </ | ||
- | </ | ||
- | </ | ||
- | ==== Les transactions ==== | ||
- | __Rappel__ :Une transaction est une série d’actions qui doivent toutes se terminer avec succès, sinon toutes les modifications apportées à chaque action sont annulées. Les transactions se terminent par une validation ou une annulation. | ||
- | |||
- | Deux modes de gestion de transactions dans JPA : | ||
- | * JTA (Java Transaction API) – mode container-managed | ||
- | Utilisé dans les serveurs d’application comme WildFly, Payara, GlassFish, TomEE, etc. Les transactions sont gérées par le conteneur. | ||
- | |||
- | Les transactions gérées par conteneur simplifient le développement, | ||
- | |||
- | En règle générale, le conteneur commence une transaction immédiatement avant le démarrage d’une méthode et valide la transaction juste avant la fermeture de la méthode. Chaque méthode peut être associée à une seule transaction. Les transactions imbriquées ou multiples ne sont pas autorisées dans une méthode. | ||
- | |||
- | * RESOURCE_LOCAL – mode application-managed | ||
- | Utilisé dans les applis standalone (Java SE, Spring Boot sans conteneur JEE). Les transactions sont gérées par le code (ex : getTransaction(), | ||
- | |||
- | < | ||
- | < | ||
- | < | ||
- | </ | ||
- | ==== L'EJB ==== | ||
- | EJB est une spécification Java pour créer des composants métier côté serveur, qui permettent de gérer automatiquement: | ||
- | * La logique métier (ex : traitement de commandes, calculs…) | ||
- | * La sécurité | ||
- | * La transaction | ||
- | * La concurrence | ||
- | |||
- | Types principaux d’EJB | ||
- | - **Stateless (@Stateless)** : | ||
- | - Pas d’état entre les appels. | ||
- | - Idéal pour des services comme “calculer le prix d’une commande”. | ||
- | - Exemple : un service qui ne se soucie pas de qui l’a appelé avant. | ||
- | - Stateful (@Stateful) : | ||
- | - Garde un état pour chaque client. | ||
- | - Exemple : panier d’achat en session. | ||
- | - Singleton (@Singleton) : | ||
- | - Une seule instance partagée pour tous les clients. | ||
- | - Exemple : compteur global ou cache partagé. | ||
- | ==== 3-Créer l’entité Client utilisable par la JPA ==== | ||
- | Une entité JPA est très proche d’une classe java Bean (Client), mais avec quelques annotations supplémentaires pour que JPA sache comment la relier à la base de données. | ||
- | < | ||
- | Au lieu d’une classe java Client, on fait une **Entity** : | ||
- | * "New > Entity Class" : création de l' | ||
- | * "New > Entity Classes from Database" | ||
- | |||
- | Nous allons créer l' | ||
- | <code ruby entité Client> | ||
- | package com.Test.entities; | ||
- | import jakarta.persistence.*; | ||
- | |||
- | // | ||
- | @Entity | ||
- | @Table(name = " | ||
- | |||
- | public class Client { | ||
- | |||
- | // | ||
- | @Id | ||
- | @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
- | private int id; | ||
- | |||
- | private String nom; | ||
- | private String prenom; | ||
- | private String mail; | ||
- | |||
- | public Client() {} // obligatoire pour JPA (constructeur vide) | ||
- | |||
- | public Client(int id, String nom, String prenom, String mail) { | ||
- | this.id=id; | ||
- | this.nom = nom; | ||
- | this.prenom = prenom; | ||
- | this.mail = mail; | ||
- | } | ||
- | | ||
- | public Client(String nom, String prenom, String mail) { | ||
- | this.nom = nom; | ||
- | this.prenom = prenom; | ||
- | this.mail = mail; | ||
- | } | ||
- | |||
- | // Getters, setters, toString() identiques | ||
- | public int getId() { return id; } | ||
- | public void setId(int id) { this.id = id; } | ||
- | public String getNom() { return nom; } | ||
- | public void setNom(String nom) { this.nom = nom; } | ||
- | public String getPrenom() { return prenom; } | ||
- | public void setPrenom(String prenom) { this.prenom = prenom; } | ||
- | public String getMail() { return mail; } | ||
- | public void setMail(String mail) { this.mail = mail; } | ||
- | |||
- | @Override | ||
- | public String toString() { | ||
- | return " | ||
- | } | ||
- | } | ||
- | </ | ||
- | < | ||
- | ^Élément ^Classe Java simple ^Classe JPA Entity^ | ||
- | |Utilisation |Objet métier (logique applicative) |Objet persistant (lié à une table SQL)| | ||
- | |Base de données|Aucune relation directe|Mappée à une table| | ||
- | |Annotations |Aucune |@Entity, | ||
- | |Constructeur vide|Optionnel|Obligatoire| | ||
- | |Gestion SQL |Fait à la main (JDBC)|Automatique (via EntityManager)| | ||
- | ==== 3. Créer un DAO ==== | ||
- | La classe java **ClientMysql** est un DAO (Data Access Object) en mode **JDBC classique** (Connection, | ||
- | |||
- | Passer à JPA va permettre de simplifier le code, tout en gardant la même logique métier (lire tous les clients, créer un client, etc.). | ||
- | |||
- | < | ||
- | <code ruby ClientJpa> | ||
- | @Stateless //type EJB, logique métier, à injecter dans les autres classes avec @EJB | ||
- | public class ClientJPA { | ||
- | | ||
- | @PersistenceContext(unitName = " | ||
- | private EntityManager em; | ||
- | | ||
- | // Lecture de tous les clients | ||
- | public List< | ||
- | List< | ||
- | return lesClients; | ||
- | } | ||
- | |||
- | // Création d’un client | ||
- | public int create(Client unClient) { | ||
- | em.persist(unClient); | ||
- | // Force la synchro avec la base pour récupérer l’ID tout de suite | ||
- | em.flush(); | ||
- | return unClient.getId(); | ||
- | } | ||
- | } | ||
- | </ | ||
- | < | ||
- | ^Action ^JDBC (ClientMysql) ^JPA (ClientJpa)^ | ||
- | |Connexion à la base |Connection, | ||
- | |Lecture |Boucle while(resultSet.next()) |SELECT c FROM Client c (JPQL)| | ||
- | |Insertion |INSERT INTO ... VALUES (...) |em.persist(client)| | ||
- | |Gestion des transactions |Manuelle (implémentée ou pas) |Automatique via em.getTransaction()| | ||
- | |Fermeture |stmt.close(), | ||
- | |||
- | < | ||
- | ^Notion ^Explication^ | ||
- | |EntityManagerFactory |Point d’entrée JPA (selon le fichier persistence.xml)| | ||
- | |EntityManager |Objet qui gère les entités (sélection, | ||
- | |persist() |Dit à JPA d’insérer l’objet dans la table correspondante| | ||
- | |JPQL |Langage de requête orienté objets au lieu de SQL| | ||
- | |||
- | | |exemple : SELECT c FROM Client c |Client est l’entité.| | | ||
- | | | |c est un alias qu’on utilise pour faire référence à chaque client.| | ||
- | | | |SELECT c signifie : « prends tous les clients ».| | ||
- | | | |Client.class indique le type de retour attendu : ici, chaque résultat sera un objet Client.| | ||
- | |||
- | ==== 4-Adaptation de l' | ||
- | === Modification de la classe NouveauClientForm === | ||
- | Prise en compte du conteneur : modification de la déclaration de la dao qui devient une propriété de la classe | ||
- | < | ||
- | @Stateless //type EJB | ||
- | public class NouveauClientForm { | ||
- | @EJB //pour utiliser la classe ClientJPA définie en @Stateless | ||
- | private ClientJPA dao; | ||
- | | ||
- | private String resultat; | ||
- | |||
- | public String getResultat() { | ||
- | return resultat; | ||
- | } | ||
- | |||
- | public void setResultat(String resultat) { | ||
- | this.resultat = resultat; | ||
- | } | ||
- | | ||
- | public int verifierClient(HttpServletRequest request){ | ||
- | int id=-1; | ||
- | | ||
- | String leNom = request.getParameter(" | ||
- | String lePrenom =request.getParameter(" | ||
- | | ||
- | if ((lePrenom.matches(" | ||
- | Client cliSaisi = new Client(request.getParameter(" | ||
- | int idCree = dao.create(cliSaisi); | ||
- | System.out.println(" | ||
- | } | ||
- | return id; | ||
- | } | ||
- | } | ||
- | |||
- | < | ||
- | === Modification de la servlet NouveauServlet.java === | ||
- | Prise en compte du conteneur : modification de la déclaration de l' | ||
- | < | ||
- | @EJB | ||
- | | ||
- | </ | ||
- | |||
- | |||