Chapitre 8 - Entreprise Java Bean (EJB3 Entity) PDF

Document Details

ArtisticBernoulli

Uploaded by ArtisticBernoulli

Université des Sciences et de la Technologie Houari Boumediène

Tags

Java Persistence API Enterprise JavaBeans Entity Beans Programming

Summary

Cet exemple d'étude sur les Enterprise Java Beans Entity décrit les avantages, propriétés, relations entre les Beans entités, la création d'un Bean Entity, et un exemple d'application lié à la programmation orientée objet. Le document explore les concepts de persistance d'objets en Java, en expliquant les liens avec le modèle relationnel et l'API Java Persistence.

Full Transcript

# Chapitre 8 - Entreprise Java Bean (EJB3 Entity) ## Contents - 8.1 Introduction - 8.2 La correspondance entre le modèle relationnel et objet - 8.2.1 La persistance des objets - 8.2.2 Outil de mappping Objet/Relationnel - 8.3 Les Entreprise Java Bean Entity - 8.3.1 Avantage des Entity...

# Chapitre 8 - Entreprise Java Bean (EJB3 Entity) ## Contents - 8.1 Introduction - 8.2 La correspondance entre le modèle relationnel et objet - 8.2.1 La persistance des objets - 8.2.2 Outil de mappping Objet/Relationnel - 8.3 Les Entreprise Java Bean Entity - 8.3.1 Avantage des Entity Beans - 8.3.2 Propriétés d'un Bean entité - 8.3.3 Relations entre les Beans entités - 8.3.4 La création d'un Bean Entity - 8.3.5 Exemple d'application - 8.4 Conclusion ## 8.1 Introduction Contrairement aux EJB de type Session Beans, les données d'un Entity Bean sont conservées, même après l'arrêt de l'application. Les données des Entity Beans ont généralement une durée de vie longue, elles sont enregistrées et stockées dans des systèmes de persistance (base de données). Dans la programmation orientée objet, on utilise des classes pour représenter les caractéristiques des objets. La liaison entre les données et l'application par un objet s'appelle le mapping (relier). Le mapping objet/relationnel est le lien entre une base de données relationnelle avec une application objet [13] [16]. Les entités dans les spécifications de l'API Java Persistence permettent d'encapsuler les données d'une occurrence d'une ou plusieurs tables. Un objet Java mappé vers une table de la base de données grâce à des métas data via l'API Java Persistence est nommé Bean entité (Entity Bean). La version 3.0 propose une refonte complète des EJB entités afin de simplifier leur développement. Cette simplification est assurée en grande partie par la mise en œuvre de l'API JPA qui permet [16]: - La standardisation du mapping O/R. - L'utilisation des annotations avec support de l'héritage et du polymorphisme. - La possibilité d'utiliser les EJB entités en dehors du conteneur d'EJB. ## 8.2 La correspondance entre le modèle relationnel et objet La correspondance des données entre le modèle relationnel et le modèle objet pose quelques problèmes [17]: - Le modèle objet propose plus de fonctionnalités: héritage, polymorphisme. - Les relations entre les entités des deux modèles sont différentes. - Les objets ne possèdent pas d'identifiant en standard (un objet a une adresse mémoire qui varie d'une exécution à l'autre). Dans le modèle relationnel, chaque occurrence devrait posséder un identifiant unique. ## 8.2.1 La persistance des objets La persistance des objets en Java possède quelques inconvénients [17]: - De multiples choix dans les solutions et les outils (standard, commercial, open source). - De multiples choix dans les API et leurs implémentations. - De nombreuses évolutions dans les API standards et les frameworks open source. Différentes solutions sont apparues [17]: - Des frameworks open source: le plus populaire est Hibernate. - Des frameworks commerciaux (Toplink). - Des API Standards: JDO, EJB entity, JPA. Le mapping Objet/Relationnel qui consiste à réaliser la correspondance entre le modèle de données relationnel et le modèle objets. ## 8.2.2 Outil de mappping Objet/Relationnel Un outil de mappping Objet/Relationnel doit proposer un certain nombre de fonctionnalités [17]: - Assurer le mapping des tables avec les classes. - Assurer le mapping des champs avec les attributs. - Assurer le mapping des relations et des cardinalités. - Proposer une interface qui permette de mettre en œuvre des actions de type CRUD éventuellement permettre l'héritage des mappings. - Proposer un langage de requêtes indépendant de la base de données cible et assurer une traduction en SQL selon la base utilisée. - Supporter différentes formes d'identifiants générés automatiquement par les bases de données (identity, sequence,...). - Proposer un support des transactions. - Assurer une gestion des accès concurrents. - Fournir des fonctionnalités pour améliorer les performances. Les solutions de mapping O/R permettent de réduire la quantité de code à produire mais impliquent une partie configuration (généralement sous la forme d'un ou plusieurs fichiers XML ou d'annotations). ## 8.3 Les Entreprise Java Bean Entity L'objet à rendre persistant via un mapping objet/relationnel correspond généralement à une table. Chaque propriété de cet objet est liée à un champ de la table. Chaque instance de cet objet représente généralement un enregistrement de la table. Il est possible qu'un Entity Bean soit réparti sur plusieurs tables. Les Entity Beans doivent tous avoir un identifiant unique (clé primaire) pour permettre à l'application de retrouver les données de l'Entity Bean associé. Comme une table, un Entity Bean possède des champs relationnels. Toutefois, une grande différence existe. Un champ relationnel est représenté, dans un Entity Bean, par une propriété dont le type est un autre Entity Bean. On parle d'agrégation, en programmation objet. A l'opposé, une table est liée à une autre table par une clé étrangère [16] [17]. La persistance d'objets avec JPA repose sur plusieurs fonctionnalités [16]: - Un cycle de vie pour les entités. - Un ensemble d'entités annotées qui représentent le modèle objet du domaine. Le Bean entity doit respecter les règles de création des EJB: - Être déclaré avec l'annotation `@Entity`. - Posséder au moins une propriété déclarée comme clé primaire avec l'annotation `@Id`. - Avoir un constructeur sans argument. L'annotation `@Entity` possède un attribut optionnel nommé name qui permet de préciser le nom de l'entité dans les requêtes. Par défaut, ce nom est celui de la classe de l'entité. Le Bean entity est composé de propriétés qui seront mappés sur les champs de la table de la base de données. Chaque propriété encapsule les données d'un champ d'une table. Ces propriétés sont utilisables au travers les méthodes (getter/setter). Une propriété particulière est la clé primaire qui sert d'identifiant unique dans la base de données mais aussi dans l'objet. Elle peut être de type primitif ou de type objet. La déclaration de cette clé primaire est obligatoire. La description du mapping entre le Bean entité et la table peut être fait de deux façons [16]: - Utiliser des annotations. - Utiliser un fichier XML de mapping. Note: Les informations de mapping entre une table et un objet peuvent être définies grâce aux annotations mais aussi via un fichier de mapping (le fichier est prioritaire par rapport aux annotations si les deux sont utilisés). ## 8.3.1 Avantage des Entity Beans Le principal service est la gestion de la persistance qui gère l'ensemble des accès aux données dans la mémoire ou dans les sources de données. L'utilisation des Beans entités possède plusieurs avantages comparés à l'accès direct à la base de données. Cette solution apporte une façon simple pour l'accès et la modification des données; il est plus facile de modifier le nom d'un utilisateur au niveau de l'application, d'appeler la méthode Utilisateur.setNom() que d'exécuter une requête SQL [16]. Les Entity Beans sont exécutés dans le conteneur EJB qui apporte des services comme: - La gestion de la persistance (pas besoin de créer le code SQL ou JDBC). - la gestion des transactions. - la gestion de la sécurité. - etc.... ## 8.3.2 Propriétés d'un Bean entité L'objet à rendre persistant, via un mapping Objet/Relationnel, correspond à: - Une table de la base de données. - Chaque propriété de cet objet est liée à un champ de la table. - Chaque instance de cet objet représente généralement une ligne de la table. Il est possible qu'un Bean entité soit réparti sur plusieurs tables. Parmi les propriétés d'un Bean entité: - Les Beans entités doivent tous posséder un identifiant unique (clé primaire). - Un Bean entité est caractérisé par des propriétés telles que: nom, prénom, email, téléphone, etc. Ces propriétés sont mappées (liées) aux champs de la table associée. - Comme une table, un Bean entité possède des champs relationnels. Un champ relationnel est représenté, dans un Bean entité, par une propriété dont le type est un autre Bean entité ce qui correspond à l'agrégation dans la programmation objet. A l'opposé, une table est liée à une autre table par une clé étrangère. ## 8.3.3 Relations entre les Beans entités Il existe quatre relations possibles entre les Beans entités [16]: - **One To One** (un à un): si un utilisateur ne peut avoir qu'un seul et unique compte alors la relation entre l'utilisateur et son compte est de type One To One. - **One To Many** (un à plusieurs) et **Many To One** (plusieurs à un): un utilisateur peut avoir plusieurs portefeuilles alors qu'un portefeuille est détenu par un seul utilisateur. La relation entre Portefeuille et Utilisateur est de type Many To One et la relation entre Utilisateur et Portefeuille est de type One To Many. - **Many To Many** (plusieurs à plusieurs) : un utilisateur a plusieurs loisirs et un loisir peut être partagé avec plusieurs utilisateurs. Dans ce cas, la relation est de type **Many To Many** entre Utilisateur et Utilisateur. ## 8.3.4 La création d'un Bean Entity La création d'un Bean Entity doit respecter les points suivants: - Le Bean entity doit être annoté avec l'annotation `@Entity`. - L'annotation `@Table` permet de préciser le nom de la table vers laquelle le Bean sera mappé. L'utilisation de cette annotation est facultative (par défaut le nom de la table correspond au nom de la classe). - Pour mapper un champ de la table avec une propriété du Bean, il faut utiliser l'annotation `@Column`. L'utilisation de cette annotation est facultative. - Le champ correspondant à la clé primaire de la table doit être annoté avec l'annotation `@Id.` L'utilisation de cette annotation est obligatoire. - Le Bean de type entity doit généralement implémenter l'interface Serializable: le Bean pourra être utilisé dans les paramètres et la valeur de retour des méthodes métiers d'un EJB. Le Bean peut ainsi être utilisé pour la persistence et le transfert de données. ## 8.3.5 Exemple d'application ### 8.3.5.1 Exemple avec une seule entité Nous considérons l'entité Produit et nous donnons dans ce qui suit les étapes à suivre à fin de créer l'EJB Entity. #### Création de l'entité Produit Nous commençons toujours par l'entité (ou les entités) pour assurer la persistance. Ici on donne l'implémentation de l'entité Produit avec l'utilisation des annotations nécessaires. L'entité Produit est utilisée pour le mapping. ```java package Pack; import java.io.Serializable; import javax.persistence. Column; import javax.persistence. Entity; import javax.persistence. Id; import javax.persistence. Table; @Entity @Table (name="table_Produit") public class Produit implements Serializable { private static final long serialVersionUID = 1L; @Id private String id; @Column(name = "libelle_Column") private String libelle; private int quantiteEnStock; public Produit () { super();} public Produit (String id) { this.id = id;} public Produit (String id, String libelle, int quantiteEnStock) { this.id = id; this. libelle = libelle; this.quantiteEnStock = quantiteEnStock;} public String getLibelle () { return libelle;} public void setLibelle (String libelle) { this.libelle = libelle;} public int getQuantiteEnStock () { return quantiteEnStock; } public void setQuantiteEnStock (int quantiteEnStock) { this. quantiteEnStock = quantiteEnStock; } public String getId() { return id;} public String toString() { return "Produit_num_" + id + "/" + libelle + "/_quantite_disponible:" + quantiteEnStock; } } ``` #### Création de l'interface Il faut toujours créer une interface qui contient les signatures des méthodes métiers de l'EJB. Il faut également préciser le type de l'interface (Remote ou Local). ```java package Pack; import java.util.List; import javax.ejb. Remote; @Remote public interface GestionDeStock { public void ajouter (Produit produit); public Produit rechercherProduit (String id); public List<Produit> listerTousLesProduits(); public int supprimerTousLesProduits(); } ``` #### La création de la classe qui implémente l'interface Nous utilisons ici la classe EntityManager qui est responsable de la gestion des opérations sur une entité grâce à plusieurs méthodes: - `persist(entité)`: sauvegarder l'entité. - `remove(entité)`: suppression d'une entité. - `find(entité.class, clé)`: rechercher une entité par une clé - `refresh(entité)`: annulation modifications - `flush()`: sauvegarde immédiate modifications - `createQuery(req)`: création d'une requête. -...etc ```java package Pack; import java.util.List; import javax.ejb. Stateless; import javax.persistence. EntityManager; import javax.persistence. PersistenceContext; @Stateless public class GestionDeStockBean implements GestionDeStock { @PersistenceContext EntityManager em; public void ajouter (Produit produit) {em.persist (produit);} public Produit rechercherProduit (String id) {return em.find (Produit.class, id);} public List<Produit> listerTousLesProduits () { return em.createQuery ("SELECT_P_FROM_ Produit_p_ORDER_BY_p.quantiteEnStock").getResultList();} public int supprimerTousLesProduits(){return em.createQuery("Delete FROM_Produit").executeUpdate();}} ``` L'annotation `@PersistenceContext` demande au conteneur d'injecter une instance de la classe EntityManager. #### Création de l'unité de persistance La spécification JPA nécessite l'utilisation d'un fichier `persistence.xml` pour le déploiement. Ce fichier définit le nom de l'unité de persistance et les options de la base de données. Toute application JPA doit comporter au moins un fichier persistence.xml. Ci-dessous un exemple de fichier persistence.xml que nous avons utilisé dans la création de notre EJB3 Entité. ```xml <persistence > <persistence-unit name="EJB3Entity"> <jta-data-source>java:/MySqlDS</jta-data-source> <!-- Pour h2 database -> <!-- java:jboss/datasources/ExampleDS <properties> <property name="hibernate.hbm2ddl.auto" value="update"/> </properties > </persistence-unit> </persistence > ``` #### Création de la classe Client pour tester l'EJB Entity Il faut créer le programme client dans un nouveau projet java standard (pas un projet EJB3). Il faut mettre en place aussi la dépendance entre le projet client et le projet EJB3. ```java import java.util. Iterator; import java.util.List; import java.util. Properties; import javax.naming.Context; import javax.naming. Initial Context; import javax.naming. NamingException; import Pack. Gestion DeStock; import Pack. Produit; import Pack.*; public class GestionDeStockClient { public static void main(String[] args) { try { Properties props = System.getProperties(); props.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory"); props.put("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces"); props.put("java.naming.provider.url", "localhost:1099"); Context context = new Initial Context (props); GestionDeStock stock = (GestionDeStock) context.lookup("GestionDeStockBean/remote"); // Ne pas faire l'ajout plusieurs fois stock.ajouter (new Produit ("1401", "Tomate", 100)); stock.ajouter (new Produit ("1402", "Pomme_de terre", 5680)); stock.ajouter (new Produit ("1403", "Orange", 23)); stock.ajouter (new Produit ("1404", "Carotte", 115)); stock.ajouter (new Produit ("1405", "Pomme", 48)); //stock.supprimerTousLesProduits (); List<Produit> produits = stock.listerTousLesProduits (); for (Iterator iter = produits.iterator(); iter.hasNext();) { Produit eachProduit = (Produit) iter.next(); System.out.println(eachProduit);} } catch (NamingException e) { e.printStackTrace(); }}} ``` Pour éviter de préciser dans le code les paramètres de connexion JNDI, on peut créer un fichier de ressources jndi.properties. #### Synchronisation de l'accès aux EJB Pour une instance donnée d'un EJB, les accès aux méthodes sont automatiquement synchronisés par le serveur d'applications. Cette limitation garantit qu'une action métier ne puisse pas entrer en conflit avec une autre action métier du même EJB. #### Visualisation des résultats Les outils utilisés dans cette partie sont: - OS: Windows 10, v.10.0, x86_64 / win32. - Eclipse IDE for Enterprise Java Developers. Version: 2020-03 (4.15.0). Build id: 20200313-1211 - Java version: 1.8.0_271 - Application server: JBoss Application Server 4.2. Les Figures 8.1, 8.2 et 8.3 montrent respectivement: La configuration de projet et serveur, Le déploiement de l'EJB et les résultats d'exécution et d'insertion des produits. ### 8.3.5.2 Exemple avec deux seule entités Nous considérons dans cet exemple deux entités: Auteur et livre. On suppose qu'un Auteur peut écrire plusieurs Livres et qu'un livre n'est écrit que par un seul Auteur. Nous donnons dans ce qui suit l'implémentation des entités Auteur et Livre par l'utilisation des EJB3 et les annotation `@OneToMany` et `@OneToOne`. ```java package pack; import java.io. Serializable; import java.util.ArrayList; import java.util. Collection; import javax.persistence. CascadeType; import javax.persistence. Entity; import javax.persistence. FetchType; import javax.persistence. GeneratedValue; import javax.persistence. GenerationType; import javax.persistence. Id; import javax.persistence. NamedQuery; import javax.persistence. OneToMany; @Entity @NamedQuery(name = "tousLesAuteurs", query = "select o FROM Auteur o") public class Auteur implements Serializable { private static final long serialVersionUID = 8279536554568120695L; private long id; private String nom = null; private Collection <Livre> livres; public Auteur() { livres = new ArrayList<Livre >(); } public Auteur (final String nom) {this(); setNom (nom); } @OneToMany (mappedBy = "auteur", fetch = FetchType.EAGER, cascade = CascadeType.ALL) public Collection<Livre> getLivres () { return livres;} public void ajoutLivre (final String titre) { Livre livre = new Livre(); livre.setTitre (titre); livre.setAuteur(this); livres.add(livre);} public void setLivres (final Collection <Livre > livres) { this.livres = livres;} public String getNom() { return nom;} public void setNom (final String nom) {this.nom = nom;} @Id @GeneratedValue (strategy = GenerationType.AUTO) public long getId() { return this.id;} public void setId (final long id) {this.id = id;}} package pack; import java.io. Serializable; import javax.persistence. Entity; import javax.persistence. GeneratedValue; import javax.persistence. GenerationType; import javax.persistence. Id; import javax.persistence. JoinColumn; import javax.persistence. ManyToOne; import javax.persistence. NamedQuery; @Entity @NamedQuery (name = "tousLesLivres", query = "select o FROM Livre o") public class Livre implements Serializable { private static final long serialVersionUID = 7870634228111717036L; private long id; private Auteur auteur; private String titre; public Livre () { } public Livre (final String titre, final Auteur auteur) { setTitre (titre); setAuteur(auteur);} @ManyToOne @JoinColumn (name = "Auteur_id") public Auteur getAuteur() { return auteur;} public void setAuteur (final Auteur auteur) { this.auteur = auteur;} public String getTitre () { return titre;} public void setTitre (final String titre) { this.titre = titre; } @Id @GeneratedValue (strategy = GenerationType.AUTO) public long getId() { return this.id;} public void setId (final long id) {this.id = id;}} ``` ```java package pack; import java.util.List; import javax.ejb. Remote; @Remote public interface InterfaceAuteurLivre { void init(); void listeDesAuteurs(); void listeDesLivres (); } package pack; import java.util. Collection; import java.util.List; import javax.ejb.Stateless; import javax.persistence. EntityManager; import javax.persistence. PersistenceContext; import javax.persistence. Query; @Stateless public class AuteurLivreBean implements InterfaceAuteurLivre { @PersistenceContext Entity Manager entityManager; public void init() { Auteur al = new Auteur("Auteur_al"); Livre 11 = new Livre ("Titre_1", al); al.getLivres().add(11); Livre 12 = new Livre("Titre 2", al); al.getLivres ().add(12); entityManager.persist (al); entityManager.joinTransaction(); } public void listeDesAuteurs() { List<Auteur> auteurs = entityManager.createNamedQuery("tousLesAuteurs").getResultList(); if (auteurs != null) { for (Auteur auteur : auteurs) { System.out.println("Liste_de_livres_pour_l'auteur_dont_le_nom.est'" + auteur.getNom () + "':"); Collection<Livre> livres = auteur.getLivres(); if (livres = null) { System.out.println("-_Aucun_livre.");} else {for (Livre livre: livres) { System.out.println("-_Livre ayant pour titre'" + livre.getTitre () + "'.");}}}} else { System.out.println("Aucun auteur.");}} public void listeDesLivres () { List<Livre> livres = entityManager.createNamedQuery("tousLesLivres").getResultList(); System.out.println("Liste des livres:"); if (livres != null) { for (Livre livre: livres) { System.out.println("--Livre avec titre'" + livre.getTitre () + "' ayant pour auteur'" + livre.getAuteur().getNom().getNom () + "'.");}}}} ``` ```java import java.util. Collection; import java.util.List; import java.util. Properties; import javax.naming.Context; import javax.naming. InitialContext; import pack. Auteur; import pack. InterfaceAuteurLivre; import pack. Livre; public final class ClientRelationManyOne { private ClientRelationManyOne() {} public static void main(final String[] args) throws Exception { System.out.println("Client_App_Started"); Properties props = new Properties(); props.put("java.naming.factory.url.pkgs","org.jboss.ejb.client.naming"); InitialContext context = new InitialContext (props); InterfaceAuteurLivre i = (InterfaceAuteurLivre) context.lookup("ejb:/AuteurLivre/AuteurLivreBean!pack.InterfaceAuteurLivre"); i.init(); i.listeDesAuteurs(); i.listeDesLivres ();}} ``` #### Visualisation des résultats Les outils utilisés dans cette partie sont: - OS: Windows 10, v.10.0, x86_64 / win32. - Eclipse IDE for Enterprise Java Developers. Version: 2020-03 (4.15.0). Build id: 20200313-1211 - Java version: 1.8.0_271 - Application server: WildFly 22.0 Runtime. Les Figures 8.4, 8.5 et 8.6 montrent respectivement: La configuration de projet et serveur et le déploiement de l'EJB, les résultats d'exécution coté client et serveur. ## 8.4 Conclusion Dans ce chapitre nous avons vu comment créer et utiliser les Enterprise JavaBean Entity. Le principal service esst la gestion de la persistance qui gère l'ensemble des accès aux données dans la mémoire ou dans les sources de données. L'utilisation des Beans entités, procure de multiples avantages comparés à l'accès direct à la base de données. Cette solution apporte un mécanisme simple pour l'accès et la modification des données.

Use Quizgecko on...
Browser
Browser