Vous êtes sur la page 1sur 439
Résumé Les EJB 3 3 applications détaillées Celinio FERNANDES Ce livre sur les EJB 3
Résumé Les EJB 3 3 applications détaillées Celinio FERNANDES Ce livre sur les EJB 3

RésuméLes EJB 3 3 applications détaillées Celinio FERNANDES Ce livre sur les EJB 3 s’adresse

Résumé Les EJB 3 3 applications détaillées Celinio FERNANDES Ce livre sur les EJB 3 s’adresse

Les EJB 3

3 applications détaillées

Celinio FERNANDES

Les EJB 3 3 applications détaillées Celinio FERNANDES Ce livre sur les EJB 3 s’adresse aux

Ce livre sur les EJB 3 s’adresse aux développeurs Java d’applications web travaillant sur les frameworks Struts 2, JSF 2 ou Flex 3. Le débutant comme l’expert trouveront les informations qui leur conviennent sur l'utilisation des EJB (Enterprise JavaBeans) de manière générale et les gains de productivité apportés par la version 3. L’auteur propose le développement avec les EJB de trois applications web de vente en ligne aux fonctionnalités quasi identiques et qui sont basées sur des couches métier et persistance communes. A l'aide de l'IDE Eclipse et du serveur d'application JBoss 6, il exploite les fonctionnalités d'un container EJB pour :

- mettre en place une couche de persistance basée sur les Entity beans, le langage JPQL et la Java Persistence API,

- créer des objets métiers à l'aide des Session beans et des Message-driven beans,

- définir une politique de sécurité avec une gestion des rôles et des permissions définie dans un fichier de propriétés, une base ou un annuaire LDAP,

- exposer des EJB 3 en tant que web services,

- mettre en place des traitements différés et ponctuels à l'aide des EJB Timers,

- faire de la programmation par aspect grâce aux Interceptors.

Tout au long des chapitres, l’auteur : - décrit et met en place les nouveautés incluses dans les dernières versions des frameworks Struts 2 et JSF

2.

- détaille l'utilisation du framework GraniteDS pour réaliser la communication entre les objets Java et Flex 3 et créer une interface RIA.

met en avant le framework open-source de reporting JasperReports et montre son utilisation avec les EJB, Struts 2 et JSF 2 pour créer des rapports graphiques. Enfin, l’auteur décrit les apports de la toute dernière version des EJB, la version 3.1, qui a été finalisée en décembre 2009. Les sources des applications sont en téléchargement sur le site www.editions-eni.fr et l’auteur continuera de les faire évoluer sur son site.

Les chapitres du livre :

Avant-propos – Introduction – L’application VenteEnLigne – Les entity beans et l’API de persistance (JPA) – Les session beans – Traitement des commandes avec les Message-Driven Beans – Utilisation des Web Services – Les EJB Timers – Les interceptors– Sécurité – Struts 2 – Développement d’un client avec JSF 2 - Génération de rapports avec JasperReports – Développement d’un client avec Flex 3 – Mise en place de l’environnement – EJB 3.1 : les nouveautés avec Java EE 6

– EJB 3.1 : les nouveautés avec Java EE 6 L'auteur Celinio Fernandes est architecte spécialisé

L'auteur– EJB 3.1 : les nouveautés avec Java EE 6 Celinio Fernandes est architecte spécialisé dans

– EJB 3.1 : les nouveautés avec Java EE 6 L'auteur Celinio Fernandes est architecte spécialisé

Celinio Fernandes est architecte spécialisé dans les technologies Java EE dans une SSII et intervient sur des projets en tant qu'architecte, référent technique et développeur. Il est titulaire de 4 certifications Java EE : SCBCD 5 (EJB 3.0), SCBCD 1.3 (EJB 2.0), SCWCD 1.4 et SCJP 1.4. Il est par ailleurs membre de l'équipe Java de Developpez.com et modérateur des forums.

Ce livre numérique a été conçu et est diffusé dans le respect des droits d’auteur. Toutes les marques citées ont été déposées par leur éditeur respectif. La loi du 11 Mars 1957 n’autorisant aux termes des alinéas 2 et 3 de l’article 41, d’une part, que les “copies ou reproductions strictement réservées à l’usage privé du copiste et non destinées à une utilisation collective”, et, d’autre part, que les analyses et les courtes citations dans un but d’exemple et d’illustration, “toute représentation ou reproduction intégrale, ou partielle, faite sans le consentement de l’auteur ou de ses ayants droit ou ayant cause, est illicite” (alinéa 1er de l’article 40). Cette représentation ou reproduction, par quelque procédé que ce soit, constituerait donc une contrefaçon sanctionnée par les articles 425 et suivants du Code Pénal. Copyright Editions ENI

Ce livre numérique intègre plusieurs mesures de protection dont un marquage lié à votre identifiant visible sur les principales images.

© ENI Editions - All rigths reserved - Moha Anisa

-

1 -

Objectifs

Dans cet ouvrage, j’ai voulu montrer que les Enterprise JavaBeans (EJB) 3, souvent décriées dans les premières versions pour leur lourdeur (tant dans leur apprentissage que dans leur mise en place), représentent aujourd’hui une solution à la fois viable, simple et performante pour le développement d’une application client/serveur.

À la fin des années 90, début des années 2000, les EJB représentaient l’une des rares solutions permettant au développeur de déléguer la gestion de la persistance (au container EJB). Puis des frameworks tels qu’Hibernate et Spring ont émergé et proposé des alternatives plus riches et plus simples, notamment au niveau de la gestion de la persistance ou des ressources. Fort heureusement la version 3 des EJB a su prendre en compte certaines des avancées introduites par ces frameworks.

De fait, aujourd’hui, les EJB 3 continuent à s’inscrire de façon logique dans la liste des technologies pérennes qui peuvent :

être utilisées dans les solutions élaborées pour la création de prototypes pour des POC (Proof Of Concept),

être intégrées dans des réponses à des appels d’offre,

servir à l’élaboration d’une solution complète dans un environnement complexe par la diversité et la multitude des besoins techniques et fonctionnels à satisfaire.

© ENI Editions - All rigths reserved - Moha Anisa

-

1 -

À qui s’adresse ce livre ?

Ce livre s’adresse aux étudiants et aux développeurs (débutants, intermédiaires ou confirmés) qui souhaitent découvrir ou mieux connaître les EJB mais aussi Struts 2, JSF 2, Flex 3 et JasperReports.

Il s’adresse également aux chefs de projets et architectes ainsi qu’aux responsables techniques qui désirent mettre en place une architecture Java EE qui réponde aux besoins classiques d’une application web d’entreprise : interaction avec une base de données, sécurisation des accès, reporting, gestion des utilisateurs, traitements différés, développement en couches, adoption de design patterns…

Concernant les pré­requis, des connaissances des bases du langage Java sont nécessaires pour aborder ce livre. Une connaissance de JBoss est utile.

Les sources de l’application sont téléchargeables sur le site des Éditions ENI à l’adresse http://www.editions­eni.fr

© ENI Editions - All rigths reserved - Moha Anisa

-

1 -

Introduction

Les Enterprise JavaBeans sont une des API qui font partie du standard de développement d’applications d’entreprises Java EE. L’un des plus importants changements depuis Java EE 5 est l’introduction des EJB 3 qui sont devenus des POJO (Plain Old Java Object) basés sur des annotations. Ceci élimine le besoin d’étendre certaines interfaces et de créer des fichiers de configuration XML.

Les EJB 3 intègrent la JPA (Java Persistance API) qui est l’interface standard de mapping orienté objet de la plate­forme Java EE 5. L’API JPA est utilisée au niveau de la couche de persistance pour créer une séparation entre l’application et la base de données.

La JPA est une spécification qui est implémentée par divers vendeurs tels que :

Hibernate, l’implémentation la plus populaire (et qui existait en fait avant la spécification JPA) et également utilisée par JBoss.

Oracle Toplink

EclipseLink

Apache OpenJPA

Kodo

1. Historique des spécifications

Les EJB sont apparus dans leur première version (1.0) en 1997 à la conférence JavaOne de San Francisco.

En 1999, la version 1.1 (J2EE 1.2) rend le support des entity beans obligatoire.

En 2001, la version 2.0 (J2EE 1.3) introduit les Message­Driven Beans, les entity beans 2.x et EJB QL, les interfaces locales et distantes.

La version 2.1 (J2EE 1.4) naît deux ans plus tard, en 2003 : apparition des EJB Timer Service, support pour les Web services et améliorations mineures d’EJB QL.

En 2006, des changements majeurs sont apportés dans la version 3.0 (Java EE 5) pour faciliter le développement :

annotations, mécanisme d’injection, spécifications JPA. Ces changements constituent un grand pas en avant et vont être examinés en détails dans ce livre.

Enfin, en décembre 2009, la version 3.1 est finalisée. L’idée est d’aller vers encore plus de simplification.

Au moment de la rédaction de cet ouvrage, la version 3.1 (Java EE 6) n’est implémentée que par Glassfish qui est le serveur d’applications Open Source de Sun et donc forcément le premier serveur à implémenter les nouvelles normes.

À titre d’exemple, voici les dates de sortie de quelques serveurs EJB compatibles et implémentant Java EE 5 :

JBoss 5 : septembre 2008

Oracle Weblogic Server 10.0 : avril 2007

IBM Websphere 7 : septembre 2008

Jonas 5.1 : mars 2009

La barre de planning suivante montre l’historique des EJB :

barre de planning suivante montre l’historique des EJB : Le serveur JBoss 6.0 est disponible depuis

Le serveur JBoss 6.0 est disponible depuis décembre 2009 mais n’est pas encore certifié Java EE 6. La version des EJB utilisée dans cet ouvrage est la version 3.0. Cependant le dernier chapitre aborde en détails les quelques améliorations et nouveautés que les EJB 3.1 apportent.

La plate­forme Java est passée de J2EE 4 à Java EE 5. Le 2 a

La plate­forme Java est passée de J2EE 4 à Java EE 5. Le 2 a disparu au profit d’une appellation plus simple :

Java Enterprise Edition (Java EE).

2. Comparaison EJB 2 ­ EJB 3

Les spécifications des EJB 3 (JSR 220 (Java Specification Request) est le numéro des spécifications des EJB 3) ont été divisées en trois sous­spécifications :

l’API EJB 3 simplifiée (59 pages) ;

l’API Core Contracts and Requirements (562 pages) ;

la Java Persistence API (version 1.0, 256 pages).

Le modèle de programmation proposé par la version 2.1 des spécifications EJB, comportait une série de contraintes qui limitait l’usage de cette spécification et a entraîné l’apparition de solutions Open Source destinées à combler les carences que présentaient les EJB 2.1.

L’objectif des EJB 3 est de simplifier le développement d’applications Java et de standardiser l’API de persistance pour la plate­forme Java.

Une différence majeure apparaît dans la création des entity beans puisqu’ils sont maintenant basés sur la JPA.

Les spécifications des EJB 3 ont apporté une nouvelle API qui est plus simple pour implémenter les beans de sessions et y accéder. EJB 3 élimine l’obligation d’étendre :

les interfaces Home (javax.ejb.EJBHome, javax.ejb.EJBLocalHome),

les interfaces Remote component (javax.ejb.EJBObject, javax.ejb.EJBLocalObject),

l’interface javax.ejb.SessionBean dans le cas des session beans,

l’interface javax.ejb.EntityBean dans le cas des entity beans,

ainsi que d’avoir à écrire un fichier XML de déploiement très verbeux (ejb­jar.xml).

Les interfaces Remote component sont devenues des business interfaces. Les cycles de vie des beans sont les mêmes que dans la version 2.1 mais les méthodes callbacks sont plus simples.

Il n’est plus nécessaire de faire de lookup JNDI, l’injection de dépendance est utilisée à la place à l’aide des annotations.

La configuration par défaut simplifie également le développement puisqu’elle permet la mise en place de valeurs par défaut.

Le langage de requêtes EJB QL (Query Language) laisse la place au langage JPQL (JPA Query Language) qui donne enfin la possibilité au développeur d’écrire des requêtes avec des sous­requêtes, d’utiliser les clauses GROUP BY et HAVING, etc.

3. Avantages des EJB 3

Les avantages que représentent les EJB 3 par rapport aux versions précédentes et de manière générale sont importants. Ils sont bien évidemment examinés de plus près tout au long de l’ouvrage.

Le premier avantage est la simplicité du développement depuis la version 3, comme décrit dans le paragraphe précédent. Si bien que les EJB sont devenus une solution intéressante même pour des petites applications. De plus, grâce à cette simplification, de nouveaux collaborateurs peuvent être rapidement formés aux EJB pour monter en compétence et intégrer une équipe.

Un deuxième avantage est bien évidemment la multitude de services qu’offre le container du serveur d’application :

service de nommage JNDI, scheduling avec les EJB Timers, gestion des transactions, concurrence, authentification, sécurité, pools de ressources, persistance De plus les serveurs d’application peuvent facilement être mis en cluster pour faire face à des montées en charge.

-

2 -

© ENI Editions - All rigths reserved - Moha Anisa

Un autre avantage est la flexibilité qu’apportent les EJB puisqu’ils contribuent à une meilleure répartition des rôles dans une équipe de développement ainsi qu’au respect des règles métiers.

De manière générale, les EJB apportent de la haute disponibilité et de la scalabilité grâce au load­balancing ainsi qu’au clustering.

© ENI Editions - All rigths reserved - Moha Anisa

-

3 -

Présentation de l’application VenteEnligne

L’application développée dans cet ouvrage s’appelle VenteEnLigne. C’est, comme son nom l’indique, une application classique de commerce électronique proposant à la vente en ligne des produits provenant de catalogues variés (sport, informatique, bricolage, etc). Au niveau juridique, il est bon de savoir que la CNIL (Commission Nationale de l’Informatique et des Libertés) a mis à disposition sur son site un guide pratique qui explique les obligations des créateurs de sites marchands vis­à­vis de la CNIL.

1. Description fonctionnelle de l’application

Un client se connecte sur le site marchand pour visualiser les produits vendus et passer des commandes. Il peut mettre à jour son profil (nom, prénom, adresse etc.), consulter le détail d’un article, modifier les quantités des articles souhaités, consulter l’historique de ses commandes ainsi que donner son avis sur des produits en leur attribuant des notes. Pour pouvoir consulter le magasin en ligne et pour simplifier les choses, l’utilisateur doit obligatoirement être connecté. Une alternative possible et de plus en plus courante est de permettre à tous les utilisateurs, qu’ils soient connectés ou non connectés (anonymes), de consulter le magasin et d’ajouter des articles dans leurs paniers. Dans ce cas, ils devront se connecter ou s’enregistrer au moment du paiement.

Le paiement en ligne est effectué et la commande au format PDF est envoyée par courrier électronique au client.

Les fonctionnalités de l’application sont les suivantes :

une page de connexion et d’inscription ;

une page principale qui offre l’accès aux principales fonctionnalités ;

la présentation des catalogues, des produits et des articles disponibles en base (quantité, nom, prix…) ;

un profil ainsi qu’un panier, et la recherche de produits par mots­clefs ;

l’ajout d’un article dans le panier ;

la suppression d’un article du panier ;

le passage de commande avec la visualisation du contenu du panier ;

la connexion en tant que modérateur et administrateur, gestion des produits et/ou des clients.

2. Diagrammes de cas d’utilisation, de séquences et de classes UML

a. Les diagrammes de cas d’utilisation

Les diagrammes de cas d’utilisation (Use Cases en anglais) permettent de modéliser les processus et de décrire des cas concrets. Ils permettent de visualiser les acteurs, leur rôle et les actions qu’ils peuvent réaliser.

On peut identifier trois acteurs pour notre site de vente en ligne :

Les clients. Ils possèdent un profil, consultent les produits en ligne et passent des commandes.

Les gestionnaires. Ils mettent à jour les produits à vendre, en supprimant ou en ajoutant des produits à la vente. Ils peuvent modifier les prix de ces produits. Enfin, ils peuvent ajouter/supprimer des clients et gérer les détails des clients.

Les administrateurs. Ils peuvent également gérer des produits et des clients, mais aussi des modérateurs.

Voici trois diagrammes de scénarios possibles pour ces trois acteurs.

Diagramme de scénario : commander et payer

© ENI Editions - All rigths reserved - Moha Anisa

-

1 -

Le stéréotype <<include>> représente une dépendance entre plusieurs cas d’utilisation : un cas
Le stéréotype <<include>> représente une dépendance entre plusieurs cas d’utilisation : un cas

Le stéréotype <<include>> représente une dépendance entre plusieurs cas d’utilisation : un cas d’utilisation inclut obligatoirement un autre cas d’utilisation.

Diagramme de scénario : gérer les produits

Diagramme de scénario : gérer les produits - 2 - Diagramme de scénario : administrer le

-

2 -

Diagramme de scénario : administrer le site

© ENI Editions - All rigths reserved - Moha Anisa

Il existe de nombreux outils Open Source de modélisation UML, tel que StarUML, très facile
Il existe de nombreux outils Open Source de modélisation UML, tel que StarUML, très facile

Il existe de nombreux outils Open Source de modélisation UML, tel que StarUML, très facile d’utilisation et très léger, capable de faire du reverse engineering et de générer du code.

b. Les diagrammes de séquences

Grâce aux diagrammes de séquences, nous allons pouvoir modéliser de façon chronologique les différents échanges entre les objets.

Voici quelques exemples de diagrammes de séquences qui décrivent certains échanges de l’application :

Ajout d’un produit et paiement

Le diagramme suivant illustre le workflow possible d’un achat. Lorsqu’un client a créé son panier et effectué sa dernière sélection de produits, il peut vérifier le contenu de son panier afin d’y apporter quelques éventuelles modifications avant de passer commande et rentrer les détails pour le paiement.

de passer commande et rentrer les détails pour le paiement. Recherche d’un produit par critère Le

Recherche d’un produit par critère

Le client entre un critère de recherche tel que le nom d’un livre ou le nom d’un auteur pour filtrer les produits. L’écran est rafraîchi pour afficher la liste des résultats.

© ENI Editions - All rigths reserved - Moha Anisa

-

3 -

Suppression d’un produit Un client supprime un produit de son panier. Une fois supprimé, ce

Suppression d’un produit

Un client supprime un produit de son panier. Une fois supprimé, ce produit ne peut être récupéré. Le détail du panier est mis à jour et le montant total est recalculé quand un produit est supprimé.

montant total est recalculé quand un produit est supprimé. c. Les diagrammes de classes Cette section

c. Les diagrammes de classes

Cette section décrit les diagrammes de classes utilisés pour présenter les classes, les liens entre elles, ainsi que les interfaces de l’application.

Diagramme de classe du Client (Utilisateur)

-

4 -

© ENI Editions - All rigths reserved - Moha Anisa

Diagramme de classe Utilisateur Diagramme de classe du Produit Au fur et à mesure du

Diagramme de classe Utilisateur

Diagramme de classe Utilisateur Diagramme de classe du Produit Au fur et à mesure du développement

Diagramme de classe du Produit

de classe Utilisateur Diagramme de classe du Produit Au fur et à mesure du développement des

Au fur et à mesure du développement des classes de l’application, le modèle UML peut être amené à changer, et inversement, le code des classes peut changer si le modèle change aussi (reverse engineering).

Il y a donc une synchronisation qui s’effectue. C’est l’approche RoundTrip.

Elle est un peu à l’opposé de l’approche Model Driven, dans laquelle il faut suivre rigoureusement le modèle existant.

3. Architecture logicielle

Les phases de conception et de réalisation d’une application passent par la mise en place d’une architecture physique (dite également matérielle ou technique) et d’une architecture logicielle. L’architecture physique décrit les composants

© ENI Editions - All rigths reserved - Moha Anisa

-

5 -

physiques (postes de développement, serveurs, bases de données, équipements réseaux…) utilisés pour le développement et pour le déploiement. L’architecture logicielle décrit les solutions mises en place (frameworks, logiciels, design patterns…) ainsi que leurs interactions.

Pour réaliser la partie dynamique du site de vente en ligne, nous allons suivre un développement en couches applicatives que propose l’architecture logicielle de type 3­tiers. Le modèle d’architecture logicielle en couches présente plusieurs avantages, notamment une meilleure répartition des rôles (chaque couche a un rôle précis), une séparation des traitements, une réduction du couplage (la dépendance) entre les services pour fournir une plus grande souplesse au niveau de la maintenabilité et de l’évolution.

Voici un schéma des trois couches de l’application :

Voici un schéma des trois couches de l’application : Chaque couche ne communique qu’avec les couches

Chaque couche ne communique qu’avec les couches voisines. Il existe également un autre modèle d’architecture logicielle qui préconise le développement en cinq couches :

-

6 -

© ENI Editions - All rigths reserved - Moha Anisa

Dans ce modèle à cinq couches, la couche métier du modèle à trois couches se

Dans ce modèle à cinq couches, la couche métier du modèle à trois couches se retrouve dans la couche service. Une couche domaine est ajoutée pour encapsuler les interactions avec la base. Elle peut correspondre à un DAO implémenté dans un session bean avec des méthodes génériques d’accès en base. Un client du DAO peut être un autre session bean, de la couche services, dans lequel ce DAO est injecté. Un exemple d’un tel DAO générique est décrit dans le chapitre Les session beans.

La couche application, également appelée coordination, gère l’organisation des données à afficher.

a. La couche présentation

Cette couche prend en charge les interactions utilisateur. Elle contient les composants graphiques de l’interface homme­machine pour l’affichage et la saisie, avec un premier niveau de contrôle (champ obligatoire par exemple). Elle expose donc les données à l’utilisateur, interprète ses commandes et gère l’enchaînement des vues. Elle est souvent gérée par un framework qui facilite la gestion des échanges avec l’utilisateur et/ou la gestion de l’interface graphique. Dans l’application, elle est développée dans un premier temps avec des pages JSP avec Struts 2, puis dans un deuxième temps avec JSF 1.2 / JSF 2 et enfin avec Flex 3. Les éventuelles modifications faites dans cette couche ne devraient pas en théorie entraîner des modifications dans la couche métier.

Les accès aux session beans sont facilités par la création d’une classe qui centralise ces accès, conformément au design pattern Service locator (cf. chapitre Struts 2 ­ Création d’un Service locator). De plus, les appels aux entity beans se font via les session beans afin de ne pas exposer les entity beans à la couche présentation. C’est l’un des buts du design pattern Session Façade (décliné du design pattern Façade) qui permet de masquer la complexité des interactions avec les objets de la couche de persistance.

b. La couche métier / business

Cette couche prend en charge la logique métier. Elle contient la logique des traitements (des entités pourvues de méthodes) et constitue donc le cœur de l’application. Ces traitements sont sollicités soit par la couche présentation soit par d’autres traitements. Les différentes règles de gestion et de contrôle de l’application sont implémentées dans cette couche. Elle offre des services applicatifs et métiers à la couche présentation. Elle est développée avec les EJB 3 (session beans, message­driven beans). Les règles de sécurité d’accès aux services métier sont également définies dans cette couche.

Il est préférable d’encapsuler les accès à la base dans un stateless session bean dédié, conformément au design pattern DAO (Direct Access Object, voir le chapitre Les session beans ­ Objets DAO utilisateur et client).

c. La couche persistance

Cette couche prend en charge l’accès aux données. Elle est chargée de faire le mapping entre les objets de la

© ENI Editions - All rigths reserved - Moha Anisa

-

7 -

couche métier et les données en base. Pour effectuer des actions dans la base de données, les classes de la couche métier font appel aux classes de persistance qui sont basées sur les entity beans et la Java Persistence API (JPA) avec JPQL, le langage de requêtes objet.

L’ensemble est développé sous Java 6 et déployé sur le serveur d’application Open Source JBoss, l’un des premiers à implémenter les spécifications des EJB 3. On utilise la base de données Oracle 10g Express, l’annuaire LDAP ApacheDS et l’IDE Eclipse.

10g Express, l’annuaire LDAP ApacheDS et l’IDE Eclipse. Le couplage désigne le degré de dépendance entre

Le couplage désigne le degré de dépendance entre les couches. Un couplage lâche (loose coupling en anglais) est un principe général dans une architecture et qui préconise de réduire les dépendances entre les couches.

4. Définition de la base

Il est maintenant temps de définir le schéma des tables de la base Oracle.

L’approche choisie est de d’abord créer les tables puis d’écrire les EJB entity beans, comme décrit dans le chapitre suivant. Cependant JPA permet aussi la démarche inverse, en générant le schéma de bases de données à partir des EJB entity beans.

a. Description des tables

Utilisateur (Id, Email, Fax, Login, Nom, Password, Prenom, Telephone, Titre, Type_Util, Adresse_FK).

Un utilisateur peut être un client, un administrateur ou un gestionnaire. Il peut avoir une ou plusieurs adresses.

Adresse (Id, CodePostal, Departement, Numero, Pays, Rue, Ville).

L’adresse de résidence et de facturation du client.

Produit (Id, Description, Nom, Catalogue_FK).

Un produit appartient à un et un seul catalogue.

Catalogue (Id, Description, Nom).

Un catalogue contient un ensemble de produits.

Stock (Id, Quantite).

La quantité disponible d’un article donné.

Article (Id, Nom, Prix, Produit_FK, Stock_FK).

Un article fait partie d’un produit et est disponible dans une certaine quantité.

Par exemple, les articles ballon et sifflet font partie du produit Football qui provient du catalogue Sport.

Les articles raquette et balle font partie du produit Tennis qui provient également du catalogue Sport.

Commande (Id, Date_Expiration_CarteCredit, Date_Commande, Numero_CarteCredit, Type_CarteCredit, Adresse_Fk, Utilisateur_FK).

La commande d’un client.

LigneCommande (Id, Quantite, Article_FK).

La ligne de commande concerne un seul article et indique la quantité commandée.

-

8 -

© ENI Editions - All rigths reserved - Moha Anisa

LigneCommande_TJ (Commande_FK, LigneCommande_FK).

Table d’association qui fait le lien entre LigneCommande et Commande.

Voici le diagramme d’entités correspondant au schéma de la base de données :

correspondant au schéma de la base de données : Les clés claires et foncées correspondent respectivement

Les clés claires et foncées correspondent respectivement aux clés primaires et aux clés étrangères.

© ENI Editions - All rigths reserved - Moha Anisa

-

9 -

Introduction

Les EJB 3 simplifient grandement le développement grâce à l’utilisation d’un modèle de programmation basé sur les POJOs (Plain Oriented Java Objects) et les annotations.

Les entity beans de type CMP (version 2.1 des EJB), c’est­à­dire les entity beans dont la persistance est managée par le container, sont remplacés par la Java Persistance API (JPA, JSR­220). Celle­ci peut fonctionner en dehors d’un container EJB, comme vous allez le voir dans ce chapitre. Elle est incluse dans les spécifications des EJB 3.

La plupart des différentes manipulations nécessaires à la création des projets et à leurs configurations, ainsi que la création des classes, sont décrites dans Eclipse, l’outil IDE utilisé dans cet ouvrage.

© ENI Editions - All rigths reserved - Moha Anisa

-

1 -

Le mapping des classes et des tables

1. Le cycle de vie des objets persistants gérés par JPA

Un entity bean est un objet persistant qui est associé à un cycle de vie que le client contrôle et qu’il est donc important de reproduire ici pour en avoir une bonne compréhension :

de reproduire ici pour en avoir une bonne compréhension : On distingue quatre états principaux d’un

On distingue quatre états principaux d’un entity bean par rapport au contexte de persistance :

Nouveau (new) : c’est un bean qui vient d’être instancié. Il n’est associé à aucun contexte de persistance.

Managé (managed) : le bean est associé à un contexte de persistance. Par exemple suite à une recherche, une sauvegarde ou une modification.

Détaché (detached) : le bean est détaché du contexte de persistance. Il n’est plus synchronisé avec la base.

Supprimé (removed) : le bean est attaché au contexte de persistance mais est prévu pour être supprimé de la base.

Les méthodes de l’interface javax.persistence.EntityManager (API JPA) conduisant à ces états sont les suivantes :

refresh() : dans la mesure où les entités peuvent utiliser un cache pour stocker les données, cette méthode permet de mettre à jour les entités par rapport à la base. L’entité devient managée.

merge() : cette méthode synchronise une entité détachée avec le contexte de persistance. L’entité détachée devient alors managée.

persist() : l’entité est insérée en base. Elle devient du coup managée.

remove() : l’entité est supprimée de la base après commit. Ce qui la conduit à l’état supprimé.

flush() : cette méthode synchronise de façon explicite le contexte de persistance avec la base.

2. La création du projet Enterprise Application

© ENI Editions - All rigths reserved - Moha Anisa

-

1 -

Il faut commencer par créer un projet de type Enterprise Application. Ce projet est le projet central de l’application puisqu’il définit, via le fichier de configuration application.xml, le contenu de l’archive EAR qui sera produite à la compilation et utilisée pour le déploiement. Définissez d’abord le répertoire de développement dans Eclipse :

Cliquez sur File ­ Switch Workspace ­ Other et entrez le chemin du répertoire de travail (workspace), par exemple

K:\ENI\DeveloppementEJB3\workspaceGalileo.

par exemple K:\ENI\DeveloppementEJB3\workspaceGalileo . La suite consiste à créer un environnement de

La suite consiste à créer un environnement de développement puis un projet EJB à l’intérieur de cet environnement. Dans la vue de l’explorateur de projet, cliquez sur le triangle renversé pour afficher le menu de cette vue et lancer la fenêtre de création de l’environnement de développement. Appelez­le EnvironnementEJB3 par exemple.

Le projet Enterprise Application est déployé sous la forme d’une archive EAR. Il est composé de modules qui sont mappés vers d’autres projets Java EE.

Dans la perspective Java EE, allez dans le menu d’Eclipse : File ­ New ­ Other … ­ Java EE ­ Enterprise Application Project.

Nommez­le VenteEnLigne, laissez les paramètres par défaut et assurez­vous que le serveur d’application est JBoss (cliquez sur New pour le créer s’il n’est pas déjà présent dans la combo box).

s’il n’est pas déjà présent dans la combo box). Au moment de l’écriture de ce chapitre,

Au moment de l’écriture de ce chapitre, les adaptateurs de serveurs disponibles pour JBoss sont limités à la version 5. Cependant l’installation d’une version plus récente du serveur dans Eclipse reste possible avec l’utilisation d’un adaptateur de serveur d’une précédente version. La version de JBoss qui apparaît dans certains écrans est la 5 alors que celle utilisée est en fait la 6. Reportez­vous au chapitre Mise en place de l’environnement pour l’installation détaillée de JBoss.

-

2 -

© ENI Editions - All rigths reserved - Moha Anisa

Cliquez sur Next . Dans l’écran suivant, cliquez sur New Module et sélectionnez les quatre

Cliquez sur Next. Dans l’écran suivant, cliquez sur New Module et sélectionnez les quatre modules Java EE qui sont proposés : VenteEnLigneClient, VenteEnLigneEJB, VenteEnLigneWeb et VenteEnLigneConnector.

© ENI Editions - All rigths reserved - Moha Anisa

-

3 -

Cliquez sur Finish pour revenir à l’écran initial. Enfin cochez la case Generate application deployment

Cliquez sur Finish pour revenir à l’écran initial.

Cliquez sur Finish pour revenir à l’écran initial. Enfin cochez la case Generate application deployment descriptor

Enfin cochez la case Generate application deployment descriptor et cliquez sur Finish. Au final, quatre projets ont été créés. Ils seront packagés dans une archive EAR dont le contenu correspond au suivant, en conformité avec les règles Java EE :

-

4 -

META­INF/application.xml

archive WAR (Web Application aRchive)

archive JAR (Java Application aRchive)

© ENI Editions - All rigths reserved - Moha Anisa

3. Le projet VenteEnLigneEJB

Le projet VenteEnLigneEJB est destiné à contenir les EJB session beans, message­driven beans et les EJB Timers. Il contiendra également les entity beans et les interceptors.

a. Ajout des librairies requises au projet VenteEnLigneEJB

Pour pouvoir accéder à la base, il est nécessaire d’ajouter le driver JDBC Oracle dans le classpath du projet :

ojdbc14.jar. Le driver implémente le protocole de transfert des requêtes et de leurs résultats entre le client et la base, via l’API JDBC (Java DataBase Connectivity).

Celui­ci est disponible en téléchargement sur le site d’Oracle à l’adresse suivante :

http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/htdocs/jdbc_10201.html

Copiez cette librairie dans le répertoire server\default\lib de JBoss. Allez ensuite dans les propriétés du projet en faisant un clic droit sur le projet et en choisissant le menu Properties, puis cliquez dans Java Build Path pour sélectionner l’onglet Libraries.

Java Build Path pour sélectionner l’onglet Libraries . ■ Ajoutez le driver en cliquant sur le

Ajoutez le driver en cliquant sur le bouton Add JARs et sélectionnez ojdbc14.jar. Cliquez sur OK.

b. Création d’une datasource

Après avoir créé une base de données, il faut maintenant la rendre disponible dans Eclipse. Ceci s’effectue en deux étapes :

1 ­ Création de la définition du driver Oracle JDBC (ceci revient à faire pointer Eclipse vers la librairie Jar du driver JDBC).

2 ­ Création de la connexion Oracle JDBC.

Les captures d’écran et commentaires qui suivent vous aideront dans ce processus.

Cliquez sur Windows ­ Open Perspective ­ Other ­ Database Development.

© ENI Editions - All rigths reserved - Moha Anisa

-

5 -

■ Faites un clic droit sur le nœ ud Databases et sélectionnez New . ■

Faites un clic droit sur le nœ ud Databases et sélectionnez New.

clic droit sur le nœ ud Databases et sélectionnez New . ■ Choisissez Oracle comme type

Choisissez Oracle comme type de base de données et nommez­la BaseOracleVenteEnLigne.

-

6 -

© ENI Editions - All rigths reserved - Moha Anisa

■ Cliquez sur Next . Il faut maintenant spécifier un driver. Créez­le s’il n’existe pas

Cliquez sur Next. Il faut maintenant spécifier un driver. Créez­le s’il n’existe pas encore. Pour cela, cliquez sur le bouton New Driver Definition (à droite de la combobox de la liste des drivers).

© ENI Editions - All rigths reserved - Moha Anisa

-

7 -

■ Sélectionnez ensuite l’onglet Jar list et cliquez sur le bouton Edit Jar/Zip pour ajouter

Sélectionnez ensuite l’onglet Jar list et cliquez sur le bouton Edit Jar/Zip pour ajouter le driver ojdbc14.jar qui se trouve dans le répertoire K:\ENI\DeveloppementEJB3\jboss­6.0.0.M1\server\default\lib. Cliquez sur OK, spécifiez les propriétés de connexion JDBC (SID, Host, User name, Password) et testez la connection. Enfin cliquez sur Finish. La datasource Oracle est maintenant configurée dans Eclipse et prête à être utilisée dans les projets.

c. Ajout de la persistance JPA au projet EJB

Dans JBoss, il existe un lien fort entre le framework de persistance Hibernate et les EJB 3. En effet, initialement Hibernate est apparu, puis une spécification JPA sur la persistance est née, fortement inspirée d’Hibernate. Hibernate est devenu par la suite une implémentation de JPA. Les spécifications EJB 3 requièrent le support d’un modèle de persistance qui doit reposer sur l’API JPA (package javax.persistence). Un serveur d’application qui implémente les EJB 3 dans son container doit donc supporter JPA. JBoss EJB 3 a fait le choix d’utiliser l’implémentation d’Hibernate de JPA.

Il faut aussi savoir qu’Hibernate est composé de trois principaux modules :

Hibernate core (avec les fichiers de mapping hbm.xml) ;

Hibernate avec les annotations (l’annotation @Entity du package org.hibernate.annotations par exemple) ;

Hibernate EntityManager en tant que provider JPA.

-

8 -

© ENI Editions - All rigths reserved - Moha Anisa

De plus, Gavin King et Emmanuel Bernard, deux des principaux développeurs d’Hibernate, font partie du groupe d’experts en charge de la spécification JPA (JSR­220) et qui fait partie des EJB 3.

Eclipse facilite le développement des entity beans grâce à WTP (Web Tools Platform) qui est inclut dans la distribution. En effet cette plate­forme inclut des outils pour créer un projet JPA et faire du reverse engineering pour créer automatiquement les entity beans.

Cela correspond à l’approche Bottom­Up qui consiste à d’abord créer le schéma de la base de données puis à développer les entity beans.

de la base de données puis à développer les entity beans. Le projet Eclipse qui supporte

Le projet Eclipse qui supporte JPA s’appelle Dali. Il tire son nom du peintre espagnol Salvator Dali en référence à son tableau « La persistance de la mémoire ». Le projet Dali est en fait un sous­projet de WTP qui contient de nombreux outils pour faciliter le développement. Le site officiel est http://www.eclipse.org/webtools/dali/. La démarche entreprise ici est donc de d’abord créer le schéma des tables en base Oracle de façon manuelle, puis de générer les entités en utilisant l’outil Dali.

Maintenant que vous avez connecté Eclipse à la base Oracle, vous pouvez continuer et créer le projet EJB / JPA. Ce processus se fait également en deux étapes :

1 ­ Création de la définition du serveur d’application (ceci revient à faire pointer Eclipse vers l’installation de JBoss) si elle n’existe pas encore.

2 ­ Ajout de la persistance JPA au projet EJB.

Faites un clic droit sur le projet VenteEnLigneEJB et allez dans les propriétés du projet. Puis allez sur Project Facets et cochez la case Java Persistence.

sur Project Facets et cochez la case Java Persistence . Le projet EJB peut maintenant utiliser

Le projet EJB peut maintenant utiliser Dali pour générer les entities.

d. Génération des entities à partir des tables

Vous allez maintenant générer les entity beans à partir de la base, conformément à l’approche dite bottom­up. Pour cela faites un clic droit sur le projet EJB, cliquez sur Properties puis JPA. Décochez la case Annotated classes must be listed in persistence.xml et cochez Discover annotated classes automatically.

© ENI Editions - All rigths reserved - Moha Anisa

-

9 -

■ Cliquez sur OK . Faites de nouveau un clic droit sur le projet EJB,

Cliquez sur OK. Faites de nouveau un clic droit sur le projet EJB, cliquez sur JPA Tools, et Generate Entities. Choisissez la connexion et le schéma appropriés.

Entities . Choisissez la connexion et le schéma appropriés. - 10 - © ENI Editions -

- 10 -

© ENI Editions - All rigths reserved - Moha Anisa

Cliquez sur Next et nommez le package com.eni.dvtejb.metier.entities, décochez la case à cocher Synchronize Classes in persistence.xml, sélectionnez toutes les tables et cliquez sur Finish.

, sélectionnez toutes les tables et cliquez sur Finish . Les entity beans ont été automatiquement

Les entity beans ont été automatiquement générés.

© ENI Editions - All rigths reserved - Moha Anisa

-

11 -

Comme le montre le code généré, pour spécifier qu’une classe est une entité, il faut

Comme le montre le code généré, pour spécifier qu’une classe est une entité, il faut l’annoter avec l’annotation @Entity. Elle fait partie du package javax.persistence de l’API JPA. Ce package est constitué principalement d’annotations.

Voici le code de l’entity bean Adresse :

Entité Adresse :

package com.eni.dvtejb.metier.entities;

import java.io.Serializable; import java.math.BigDecimal; import java.util.Set; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany;

@Entity @SequenceGenerator(name="SeqAdresse", sequenceName="adresse_seq") public class Adresse implements Serializable {

@Id @ GeneratedValue(strategy = GenerationType.SEQUENCE, generator="SeqAdresse") private long adresseid;

private String rue;

private BigDecimal codepostal; private String ville; private String departement; private String pays;

@OneToMany(mappedBy="adresseFk") private Set<Client> clientCollection;

@OneToMany(mappedBy="adresseFk") private Set<Commande> commandeCollection;

- 12 -

© ENI Editions - All rigths reserved - Moha Anisa

private static final long serialVersionUID = 1L;

public Adresse() { super();

}

public long getAdresseid() { return this.adresseid;

}

public void setAdresseid(long adresseid) { this.adresseid = adresseid;

}

public String getRue() { return this.rue;

}

public void setRue(String rue) { this.rue = rue;

}

public BigDecimal getCodepostal() { return this.codepostal;

}

public void setCodepostal(BigDecimal codepostal) { this.codepostal = codepostal;

}

public String getVille() { return this.ville;

}

public void setVille(String ville) { this.ville = ville;

}

public String getDepartement() { return this.departement;

}

public void setDepartement(String departement) { this.departement = departement;

}

public String getPays() { return this.pays;

}

public void setPays(String pays) { this.pays = pays;

}

public Set<Client> getClientCollection() { return this.clientCollection;

}

public void setClientCollection(Set<Client> clientCollection) { this.clientCollection = clientCollection;

}

public Set<Commande> getCommandeCollection() { return this.commandeCollection;

}

public void setCommandeCollection(Set<Commande> commandeCollection) { this.commandeCollection = commandeCollection;

}

© ENI Editions - All rigths reserved - Moha Anisa

-

13 -

}

Par défaut, la table associée à un bean entity porte le même nom que la classe. Tous les entity beans doivent avoir un constructeur public ou protected sans arguments. Les types valides des champs persistants sont les suivants :

types primitives ;

des classes sérializables ;

java.util.Collection ou java.util.Set.

L’entité Catalogue contient une relation OneToMany avec l’entité Produit. En effet, un catalogue contient plusieurs produits et un produit appartient à un et un seul catalogue.

Entité Catalogue :

package com.eni.dvtejb.metier.entities;

import java.io.Serializable; import java.util.Set; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany;

@Entity public class Catalogue implements Serializable { @Id private long catalogueid;

private String description; private String nom;

@OneToMany(mappedBy="catalogueFk") private Set<Produit> produitCollection;

private static final long serialVersionUID = 1L;

public Catalogue() { super();

}

public long getCatalogueid() { return this.catalogueid;

}

public void setCatalogueid(long catalogueid) { this.catalogueid = catalogueid;

}

public String getDescription() { return this.description;

}

public void setDescription(String description) { this.description = description;

}

public String getNom() { return this.nom;

}

public void setNom(String nom) { this.nom = nom;

}

public Set<Produit> getProduitCollection() { return this.produitCollection;

- 14 -

© ENI Editions - All rigths reserved - Moha Anisa

}

public void setProduitCollection(Set<Produit> produitCollection) { this.produitCollection = produitCollection;

}

}

L’examen des classes générées montre que les relations entre les classes ont été préservées. C’est notamment le cas de l’entité Produit qui a une relation bidirectionnelle avec l’entité Catalogue.

Entité Produit :

package com.eni.dvtejb.metier.entities;

import java.io.Serializable; import java.util.Set; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany;

@Entity public class Produit implements Serializable { @Id private long produitid;

private String description; private String nom;

@ManyToOne @JoinColumn(name="CATALOGUE_FK") private Catalogue catalogueFk;

@OneToMany(mappedBy="produitFk") private Set<Article> articleCollection;

private static final long serialVersionUID = 1L;

public Produit() { super();

}

public long getProduitid() { return this.produitid;

}

public void setProduiti(long produitid) { this.produitid = produitid;

}

public String getDescription() { return this.description;

}

public void setDescription(String description) { this.description = description;

}

public String getNom() { return this.nom;

}

public void setNom(String nom) { this.nom = nom;

}

public Catalogue getCatalogueFk() {

© ENI Editions - All rigths reserved - Moha Anisa

-

15 -

return this.catalogueFk;

}

public void setCatalogueFk(Catalogue catalogueFk) { this.catalogueFk = catalogueFk;

}

public Set<Article> getArticleCollection() { return this.articleCollection;

}

public void setArticleCollection(Set<Article> articleCollection) { this.articleCollection = articleCollection;

}

}

Le mapping des classes avec la base de données fait usage des annotations définies dans le package javax.persistence. Les annotations peuvent être définies soit au niveau des champs, soit au niveau des getters. Il est cependant possible de faire un mapping via des fichiers XML. Dans ce cas ces fichiers doivent être déclarés soit dans un fichier orm.xml placé dans le répertoire META_INF, soit dans un fichier de configuration persistence.xml.

La table d’association LIGNECOMMANDE_TJ est traduite en relation ManyToMany entre l’entité Commande et l’entité LigneCommande. Elle est indiquée à l’aide de l’annotation @JoinTable.

Il existe d’autres annotations et attributs d’annotations qu’il est utile de connaître et que vous pouvez ajouter aux classes générées. Ainsi l’annotation @ManyToMany possède plusieurs attributs tel que cascade, fetch et mappedBy. Ces attributs seront détaillés par la suite.

L’annotation @Table peut être ajoutée au niveau du nom de l’entité pour spécifier la table à mapper. Par défaut, le nom est celui de l’entité. On peut également y spécifier le schéma (Oracle) ou le catalog (MySql).

L’entité Commande représente une commande d’un certain nombre d’articles. L’entité Lignecommande représente la commande pour un seul article. En terme d’objets, une commande contient donc une liste de lignes de commandes et chaque ligne de commande contient une référence à un article :

ligne de commande contient une référence à un article : Voici les entités Commande et Lignecommande

Voici les entités Commande et Lignecommande générées par le plugin Dali.

Entité Commande :

package com.eni.dvtejb.metier.entities ;

import java.io.Serializable ; import java.util.Date ; import java.util.Set ;

import javax.persistence.Column ; import javax.persistence.Entity ; import javax.persistence.Id ; import javax.persistence.JoinColumn ; import javax.persistence.JoinTable ; import javax.persistence.ManyToMany ; import javax.persistence.ManyToOne ;

@Entity public class Commande implements Serializable { @Id private long commandeid ;

private Date datecommande ;

@Column(name="TYPE_CARTECREDIT") private String typeCartecredit;

@Column(name="NUMERO_CARTECREDIT") private String numeroCartecredit;

- 16 -

© ENI Editions - All rigths reserved - Moha Anisa

@Column(name="DATE_EXPIRATION_CARTECREDIT") private Date dateExpirationCartecredit ;

@ManyToOne @JoinColumn(name= "ADRESSE_FK") private Adresse adresseFk ;

@ManyToOne @JoinColumn(name= "CLIENT_FK") private Client clientFk ;

@ManyToMany @JoinTable(name= "LIGNECOMMANDE_TJ", joinColumns=@JoinColumn(name= "COMMANDE_FK"), inverseJoinColumns=@JoinColumn(name= "LIGNECOMMANDE_FK")) private Set<Lignecommande> lignecommandeCollection;

private static final long serialVersionUID = 1L;

public Commande() { super();

}

public long getCommandeid() { return this.commandeid;

}

public void setCommandeid(long commandeid) { this.commandeid = commandeid;

}

 

public Date getDatecommande() { return this.datecommande;

}

 

public void setDatecommande(Date datecommande) { this.datecommande = datecommande;

}

public String getTypeCartecredit() { return this.typeCartecredit;

}

public void setTypeCartecredit(String typeCartecredit) { this.typeCartecredit = typeCartecredit;

}

public String getNumeroCartecredit() { return this.numeroCartecredit;

}

public void setNumeroCartecredit(String numeroCartecredit) { this.numeroCartecredit = numeroCartecredit;

}

public Date getDateExpirationCartecredit() { return this.dateExpirationCartecredit;

}

public void setDateExpirationCartecredit(Date dateExpirationCartecredit) { this.dateExpirationCartecredit = dateExpirationCartecredit;

}

public Adresse getAdresseFk() { return this.adresseFk;

}

© ENI Editions - All rigths reserved - Moha Anisa

-

17 -

public void setAdresseFk(Adresse adresseFk) { this.adresseFk = adresseFk;

}

public Client getClientFk() { return this.clientFk;

}

public void setClientFk(Client clientFk) { this.clientFk = clientFk;

}

public Set<Lignecommande> getLignecommandeCollection() { return this.lignecommandeCollection;

}

public void setLignecommandeCollection(Set<Lignecommande> lignecommandeCollection) { this.lignecommandeCollection = lignecommandeCollection ;

}

}

Entité LigneCommande :

package com.eni.dvtejb.metier.entities ;

import java.io.Serializable ; import java.util.Set ;

import javax.persistence.Entity ; import javax.persistence.Id ; import javax.persistence.JoinColumn ; import javax.persistence.ManyToMany ; import javax.persistence.ManyToOne ;

@Entity public class Lignecommande implements Serializable { @Id private long lignecommandeid ;

private int quantite ;

@ManyToOne @JoinColumn(name= "ARTICLE_FK") private Article articleFk ;

@ManyToMany(mappedBy="lignecommandeCollection") private Set<Commande> commandeCollection;

private static final long serialVersionUID = 1L;

public Lignecommande() { super();

}

public long getLignecommandeid() { return this.lignecommandeid;

}

public void setLignecommandeid(long lignecommandeid) { this.lignecommandeid = lignecommandeid;

}

public int getQuantite() { return this.quantite;

}

public void setQuantite(int quantite) {

- 18 -

© ENI Editions - All rigths reserved - Moha Anisa

this.quantite = quantite ;

}

public Article getArticleFk() { return this.articleFk;

}

public void setArticleFk(Article articleFk) { this.articleFk = articleFk;

}

public Set<Commande> getCommandeCollection() { return this.commandeCollection;

}

public void setCommandeCollection(Set<Commande> commandeCollection) { this.commandeCollection = commandeCollection ;

}

}

L’annotation @Id indique que l’attribut lignecommandeid correspond à la clé primaire de la table. Chaque entité DOIT posséder une clé primaire. L’annotation peut être écrite sur l’attribut ou sur une des propriétés de l’attribut (getter / setter). Il est possible de spécifier la stratégie de génération de la clé primaire via l’annotation @GeneratedValue. Si la valeur est AUTO, cela signifie qu’on laisse le fournisseur JPA choisir la stratégie.

Les stratégies de génération de la clé primaire sont définies à l’aide d’une énumération GenerationType :

public enum GenerationType { TABLE, SEQUENCE, IDENTITY, AUTO ;

}

La stratégie SEQUENCE est la stratégie choisie dans l’application :

@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="SeqLigneCommande") private long lignecommandeid;

La stratégie choisie doit prendre en compte le type de bases de données. Ainsi la stratégie IDENTITY est supportée par MySQL, DB2, SQL Server mais pas par Oracle qui peut cependant simuler des colonnes IDENTITY avec des séquences.

Les séquences des tables CATALOGUE, UTILISATEUR, ADRESSE, COMMANDE, LIGNECOMMANDE, ARTICLE sont créées de façon manuelle en base.

create sequence catalogue_seq minvalue 15 start with 15 increment by 1

create sequence utilisateur_seq minvalue 15 start with 15 increment by 1

create sequence adresse_seq minvalue 15 start with 15 increment by 1

create sequence commande_seq minvalue 15 start with 15 increment by 1

create sequence lignecommande_seq minvalue 15

© ENI Editions - All rigths reserved - Moha Anisa

-

19 -

start with 15 increment by 1

create sequence article_seq minvalue 0 start with 25 increment by 1

Ces séquences sont incrémentées de un à chaque fois. Pour indiquer à un entity bean qu’il doit utiliser la séquence, utilisez l’annotation @SequenceGenerator au niveau de la classe. L’attribut sequenceName a pour valeur le nom de la séquence en base.

L’entité Client possède une relation ManyToOne avec l’entité Adresse et une relation OneToMany avec l’entité Commande.

Entité Utilisateur

package com.eni.dvtejb.metier.entities;

import java.io.Serializable; import java.math.BigDecimal; import java.util.Set; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany;

@Entity @SequenceGenerator(name="SeqUtilisateur", sequenceName="utilisateur_seq") public class Utilisateur implements Serializable {

@Id

@ GeneratedValue(strategy = GenerationType.SEQUENCE, generator=" SeqUtilisateur ") private long utilisateurid;

private String email; private BigDecimal fax; private String login; private String nom; private String password; private String prenom; private BigDecimal telephone; private String titre;

@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn(name="ADRESSE_FK") private Adresse adresseFk;

@OneToMany(mappedBy="clientFk") private Set<Commande> commandeCollection;

private static final long serialVersionUID = 1L;

public Client() { super();

}

public long getUtilisateurid() { return this.utilisateurid;

}

public void setUtilisateurid(long utilisateurid) { this. utilisateurid = utilisateurid;

}

public String getEmail() {

- 20 -

© ENI Editions - All rigths reserved - Moha Anisa

return this.email;

}

public void setEmail(String email) { this.email = email;

}

public BigDecimal getFax() { return this.fax;

}

public void setFax(BigDecimal fax) { this.fax = fax;

}

public String getLogin() { return this.login;

}

public void setLogin(String login) { this.login = login;

}

public String getNom() { return this.nom;

}

public void setNom(String nom) { this.nom = nom;

}

public String getPassword() { return this.password;

}

public void setPassword(String password) { this.password = password;

}

public String getPrenom() { return this.prenom;

}

public void setPrenom(String prenom) { this.prenom = prenom;

}

public BigDecimal getTelephone() { return this.telephone;

}

public void setTelephone(BigDecimal telephone) { this.telephone = telephone;

}

public String getTitre() { return this.titre;

}

public void setTitre(String titre) { this.titre = titre;

}

public Adresse getAdresseFk() { return this.adresseFk;

}

public void setAdresseFk(Adresse adresseFk) { this.adresseFk = adresseFk;

© ENI Editions - All rigths reserved - Moha Anisa

-

21 -

}

public Set<Commande> getCommandeCollection() { return this.commandeCollection;

}

public void setCommandeCollection(Set<Commande> commandeCollection) { this.commandeCollection = commandeCollection;

}

}

Un produit est composé de plusieurs articles et un article peut faire partie de plusieurs commandes.

Entité Article

package com.eni.dvtejb.metier.entities;

import java.io.Serializable; import java.util.Set; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany;

@Entity @SequenceGenerator(name="SeqArticle", sequenceName="article_seq") public class Article implements Serializable {

@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="SeqArticle") private long articleid;

private String nom;

private double prix;

@ManyToOne @JoinColumn(name="PRODUIT_FK") private Produit produitFk;

@OneToMany(mappedBy="articleFk") private Set<Lignecommande> lignecommandeCollection;

private static final long serialVersionUID = 1L;

public Article() { super();

}

public long getArticleid() { return this.articleid;

}

public void setArticleid(long articleid) { this.articleid = articleid;

}

public String getNom() { return this.nom;

}

public void setNom(String nom) { this.nom = nom;

}

- 22 -

© ENI Editions - All rigths reserved - Moha Anisa

public double getPrix() { return this.prix;

}

public void setPrix(double prix) { this.prix = prix;

}

public Produit getProduitFk() { return this.produitFk;

}

public void setProduitFk(Produit produitFk) { this.produitFk = produitFk;

}

public Set<Lignecommande> getLignecommandeCollection() { return this.lignecommandeCollection;

}

public void setLignecommandeCollection(Set<Lignecommande> lignecommandeCollection) { this.lignecommandeCollection = lignecommandeCollection;

}

}

e. Définition d’une source de données dans JBoss

L’application va se connecter à la base Oracle en utilisant JBoss. Il est alors nécessaire de spécifier un nom JNDI pour la source de données (data source).

1 ­ Cela consiste d’abord à copier la librairie ojdbc14.jar du driver JDBC dans le répertoire lib de JBoss :

K:\ENI\DeveloppementEJB3\ jboss­6.0.0.M1\server\default\lib.

2 ­ Puis à créer un fichier de configuration oracle­ds.xml de la base Oracle dans le répertoire deploy.

Un exemple d’un tel fichier se trouve dans le répertoire K:\ENI\DeveloppementEJB3\ jboss­6.0.0.M1 \docs\examples\jca. Ce fichier mappe une connexion au nom JNDI OracleDS. Il sera déployé et enregistré dans le service de nommage JNDI. La base Oracle remplace ainsi la base Hypersonic SQL qui est la base intégrée dans JBoss.

Voici un exemple de configuration possible du fichier deploy/oracle­ds.xml :

<?xml version="1.0" encoding="UTF-8"?>

<datasources>

<local-tx-datasource>

<jndi-name>OracleDS</jndi-name>

<connection-url>jdbc:oracle:thin:@localhost:1521:XE</connection-

url>

<driver-class>oracle.jdbc.driver.OracleDriver</driver-class>

<user-name>hr</user-name>

<password>toto</password>

<exception-sorter-class-

name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter</exce

ption-sorter-class-name>

<metadata>

<type-mapping>Oracle11</type-mapping>

</metadata>

</local-tx-datasource>

</datasources>

f. Le fichier persistence.xml

© ENI Editions - All rigths reserved - Moha Anisa

-

23 -

L’API de persistance JPA requiert la présence d’un fichier XML nommé persistence.xml, à l’intérieur du répertoire META­INF. Ce fichier XML inclut une liste de classes qui seront managées par l’API de persistance. Créez ce fichier persistence.xml dans le répertoire META­INF à l’intérieur du projet VenteEnligneEJB. Ce fichier définit une ou plusieurs unités de persistance et fait le lien entre les entity beans (POJOs) et la connexion à la base de données. Il est utilisé par la classe javax.persistence.Persistence pour créer l’EntityManagerFactory.

<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/persistence

http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

<persistence-unit name="VenteEnLigneClient" transaction- type="RESOURCE_LOCAL">

<provider>org.hibernate.ejb.HibernatePersistence</provider>

<class>com.eni.dvtejb.metier.entities.Client</class>

<class>com.eni.dvtejb.metier.entities.Adresse</class>

<class>com.eni.dvtejb.metier.entities.Commande</class>

<class>com.eni.dvtejb.metier.entities.Lignecommande</class>

<class>com.eni.dvtejb.metier.entities.Article</class>

<class>com.eni.dvtejb.metier.entities.Produit</class>

<class>com.eni.dvtejb.metier.entities.Catalogue</class>

<properties> <property name="hibernate.connection.driver_class" value="oracle.jdbc.driver.OracleDriver" />

<property name="hibernate.connection.username" value="hr" /> <property name="hibernate.connection.password" value="toto" /> <property name="hibernate.connection.url" value="jdbc:oracle:thin:@localhost:1521/XE" />

<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle9Dialect" /> <property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.show_sql" value="true" /> </properties>

/persistence-unit>

</persistence>

Dans ce fichier, une première unité de persistance appelée VenteEnLigne est définie.

Une unité de persistance définit un ensemble de classes (entities) d’une application qui sont mappées à une base de données unique. Plusieurs unités de persistance peuvent exister dans le même fichier de persistance. Ensuite il faut définir le type de transaction qui peut être JTA (les transactions sont gérées par la Java Transaction API) ou RESOURCE_LOCAL (les transactions sont gérées par l’application). Enfin il faut spécifier les paramètres de connexion à la base et Hibernate en tant que provider JPA.

connexion à la base et Hibernate en tant que provider JPA. Notez la différence d’orthographe :

Notez la différence d’orthographe : persistance (avec un e) en anglais et persistance (avec un a) en français.

Il est possible de procéder de manière inverse en créant d’abord les entity beans, puis en créant les tables de façon automatique, grâce à Hibernate qui est une implémentation de JPA.

La propriété hibernate.hbm2ddl.auto signifie qu’Hibernate va automatiquement créer les tables et séquences de la base de données pendant le redéploiement. La valeur create­drop signifie que les tables et séquences seront supprimées et recrées à la volée. Par conséquent, toutes les données de la base seront perdues à chaque fois que l’application est redéployée.

La valeur update signifie qu’Hibernate créera automatiquement les tables si elles n’existent pas.

- 24 -

automatiquement les tables si elles n’existent pas. - 24 - Pour pouvoir vérifier que les données

Pour pouvoir vérifier que les données sont bien insérées en base, il faut s’assurer que la valeur de la propriété hibernate.hbm2ddl.auto est à create et non create­drop afin de ne pas supprimer les tables en fin de test : <property name="hibernate.hbm2ddl.auto" value="update" />.

© ENI Editions - All rigths reserved - Moha Anisa

4. Création d’un client hors container avec JUnit

Les EJB 3.0 facilitent les tests grâce au modèle POJO. Il suffit d’invoquer les services de persistance à travers une classe de service ou bien directement à l’intérieur du client. Pour tester les entity beans, vous pouvez créer un client en utilisant le framework Junit. Ce framework Open Source, très utilisé pour les tests unitaires en langage Java, permet de vérifier si le comportement de chaque méthode de chaque classe Java est bien celui désiré. Ce client fonctionne hors container, c’est­à­dire que le serveur n’a pas besoin d’être démarré.

Dans le projet VenteEnLigneClient, créez le package com.eni.dvtejb.tests, faites un clic droit, New ­ Other ­ Java ­ JUnit ­ Junit Test Case.

droit, New ­ Other ­ Java ­ JUnit ­ Junit Test Case . ■ Cliquez sur

Cliquez sur Next, cochez New Junit 4 Test, entrez le nom de classe PersistenceHorsContainerTest, cochez setUp () et tearDown().

© ENI Editions - All rigths reserved - Moha Anisa

-

25 -

■ Terminez la création de cette classe de test en cliquant sur Finish puis OK

Terminez la création de cette classe de test en cliquant sur Finish puis OK sur l’écran suivant pour ajouter Junit 4 dans le build path. La classe PersistenceHorsContainerTest étend la classe TestCase du framework JUnit :

package com.eni.dvtejb.client;

import java.math.BigDecimal; import java.util.Date; import java.util.logging.Logger;

import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence;

import junit.framework.TestCase;

import com.eni.dvtejb.metier.entities.Adresse; import com.eni.dvtejb.metier.entities.Utilisateur; import com.eni.dvtejb.metier.entities.Commande;

public class PersistenceHorsContainerTest extends TestCase {

private static Logger logger = Logger.getLogger(PersistenceHorsContainerTest.class.getName()); private EntityManagerFactory emFactory; private EntityManager em;

public PersistenceHorsContainerTest(String testName) {

- 26 -

© ENI Editions - All rigths reserved - Moha Anisa

super(testName);

}

@Override protected void setUp() throws Exception { super.setUp();

try { logger.info("JPA EntityManager pour tests unitaires "); emFactory = Persistence.createEntityManagerFactory("VenteEnLigneClientJavaSE"); em = emFactory.createEntityManager(); } catch (Exception ex) { ex.printStackTrace(); fail("Exception pendant l’instantiation du JPA EntityManager.");

}

}

@Override protected void tearDown() throws Exception { super.tearDown(); logger.info("Fermeture de la couche Hibernate JPA."); if (em != null) { em.close();

}

if (emFactory != null) { emFactory.close();

}

}

public void testPersistence() { try { logger.info("debut de testPersistence()"); em.getTransaction().begin(); logger.info("em.getTransaction().begin()");

Utilisateur client = new Utilisateur (); client.setNom("renard"); client.setPrenom("lulu"); BigDecimal fax = new BigDecimal("1115333"); client.setFax(fax); client.setLogin("renard"); client.setPassword("lulu"); BigDecimal telephone = new BigDecimal("1115333"); client.setTelephone(telephone); client.setTitre("Mr"); client.setEmail("renard@lulu.com"); logger.info("avant persist"); em.persist(client); logger.info("apres persist");

assertTrue(em.contains(client));

Commande commande = new Commande(); commande.setTypeCartecredit("Visa");

Date aujourdhui = new Date(); long t = aujourdhui.getTime(); java.sql.Date aujourdhuiSQL = new java.sql.Date(t); commande.setDatecommande(aujourdhuiSQL);

// Clé étrangère commande.setClientFk(client);

java.sql.Date expirationDate = java.sql.Date.valueOf( "2010-01-31" ); commande.setDateExpirationCartecredit( expirationDate);

commande.setNumeroCartecredit("4123654787651234");

© ENI Editions - All rigths reserved - Moha Anisa

-

27 -

em.persist(commande);

assertTrue(em.contains(commande));

Adresse adresse = new Adresse(); BigDecimal codepostal = new BigDecimal("75000"); adresse.setCodepostal(codepostal); adresse.setDepartement("Paris"); adresse.setVille("Paris"); adresse.setPays("France"); adresse.setRue("Vaugirard"); BigDecimal numero = new BigDecimal("230"); adresse.setNumero(numero);

em.persist(adresse);

em.remove(adresse);

assertFalse(em.contains(adresse));

em.getTransaction().commit(); logger.info("fin de testPersistence()"); } catch (Exception ex) { em.getTransaction().rollback(); ex.printStackTrace(); fail("Exception pendant le test testPersistence");

}

}

}

Il est nécessaire d’ajouter le fichier de persistance persistence.xml dans le répertoire META­INF du projet VenteEnLigneClient. C’est lui qui va faire le lien entre la base de données et les entity beans.

<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/persistence

http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

<persistence-unit name="VenteEnLigneClientJavaSE" transaction-type="RESOURCE_LOCAL">

<provider>org.hibernate.ejb.HibernatePersistence</provider>

<class>com.eni.dvtejb.metier.entities.Utilisateur</class>

<class>com.eni.dvtejb.metier.entities.Adresse</class>

<class>com.eni.dvtejb.metier.entities.Commande</class>

<class>com.eni.dvtejb.metier.entities.Lignecommande</class>

<class>com.eni.dvtejb.metier.entities.Stock</class>

<class>com.eni.dvtejb.metier.entities.Article</class>

<class>com.eni.dvtejb.metier.entities.Produit</class>

<class>com.eni.dvtejb.metier.entities.Catalogue</class>

<properties> <property name="hibernate.connection.driver_class" value="oracle.jdbc.driver.OracleDriver" />

<property name="hibernate.connection.username" value="hr" /> <property name="hibernate.connection.password" value="toto" /> <property name="hibernate.connection.url" value="jdbc:oracle:thin:@localhost:1521/XE" />

<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle9Dialect" /> <property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.show_sql" value="true" /> </properties>

</persistence-unit>

</persistence>

- 28 -

© ENI Editions - All rigths reserved - Moha Anisa

Le fichier persistence.xml peut contenir plusieurs unités de persistance. Dans cet exemple, la classe cliente utilise l’unité de persistance VenteEnLigneClientJavaSE.

L’élément <class> spécifie les classes persistantes managées. Le type de transaction choisi est RESOURCE_LOCAL. Comme spécifié dans l’API JPA 1.0, dans un environnement hors container, c’est­à­dire Java SE, c’est la valeur par défaut du type de transaction. En revanche, dans un environnement Java EE, donc avec un serveur d’application, la valeur par défaut du type de transaction est JTA.

Le type de transaction RESOURCE_LOCAL signifie que les transactions sont gérées de façon explicite par l’application.

Il faut également récupérer les librairies cglib (http://cglib.sourceforge.net/)

asm

(http://forge.objectweb.org/projects/asm). Installez­les dans les librairies du projet VenteEnLigneClient (Java Build Path).

Dans cet exemple, les entités Client et Commande sont ici persistées en base. Exécutez ce test en faisant un clic droit sur la classe PersistenceHorsContainerTest.java ­ Run as ­ Junit Test et vérifiez que les données sont bien présentes dans les tables UTILISATEUR et COMMANDE.

et

présentes dans les tables UTILISATEUR et COMMANDE . et Conclusion Les entity beans développés fonctionnent

Conclusion

Les entity beans développés fonctionnent parfaitement en dehors d’un container EJB. Il n’a pas été nécessaire de déployer le projet sur le serveur. Il suffit de créer des POJO (Plain Oriented Java Objects), de spécifier comment les mapper avec les tables et les colonnes et d’utiliser un objet manager pour invoquer les services de persistance.

© ENI Editions - All rigths reserved - Moha Anisa

-

29 -

L’interface EntityManager

La persistance des entity beans est assurée par l’interface javax.persistence.EntityManager qui fournit des méthodes pour :

Créer, lire, mettre à jour et supprimer des entités (CRUD).

Sélectionner des entités à travers EJB QL ou des requêtes SQL (Structured Query Language).

Synchroniser les états des entités avec la base de données.

Acquérir des verrous sur les entités.

Une instance d’EntityManager est associée à un contexte de persistance. Un contexte de persistance est un ensemble d’instances d’entités dans lequel il existe une instance unique d’entité pour chacune de ces entités persistantes.

L’interface javax.persistence.EntityManager :

package javax.persistence;

public interface EntityManager { public void persist(Object entity); public <T> T find(Class <T> entityClass, Object primaryKey); public <T> T getReference(Class <T> entityClass, Object primaryKey); public <T> T merge(T entity); public void remove(Object entity); public void lock(Object entity, LockModeType lockMode);

public void refresh(Object entity); public boolean contains(Object entity); public void clear( );

public void joinTransaction( ); public void flush( ); public FlushModeType getFlushMode( ); public void setFlushMode(FlushModeType type);

public Query createQuery(String queryString); public Query createNamedQuery(String name); public Query createNativeQuery(String sqlString); public Query createNativeQuery(String sqlString, String resultSetMapping); public Query createNativeQuery(String sqlString, Class resultClass);

public Object getDelegate( );

public void close( ); public boolean isOpen( );

}

Un EntityManager peut s’obtenir de trois façons :

1 ­ Dans une application Java SE, une factory EntityManagerFactory est utilisée pour créer un EntityManager.

private EntityManager emf = Persistence.createEntityManagerFactory("VenteEnLigneClient") ; private EntityManager em = emf.createEntityManager() ;

2 ­ Dans un environnement JAVA EE, un EntityManager peut être injecté dans un bean à l’aide de l’annotation @PersistenceContext.

PersistenceContext("VenteEnLigneClient") private EntityManager em ;

3 ­ Dans Java SE ou JAVA EE, via un lookup JNDI :

© ENI Editions - All rigths reserved - Moha Anisa

-

1 -

@Resource SessionContext ctx ; EntityManager em = (EntityManager)ctx.lookup("VenteEnLigneClient") ;

La méthode persist() permet de sauvegarder l’entité en base de données. Il ne faut l’appeler que pour les entités nouvellement créées.

Un objet qui n’est pas créé par le container est non managé. Pour le rendre managé, il faut appeler la méthode merge () pour « merger » cet objet non managé au contexte de persistance.

La méthode find() permet de faire des recherches sur les entités. Dans l’application, elle est utile pour rechercher un client ou un article.

-

2 -

© ENI Editions - All rigths reserved - Moha Anisa

La gestion des transactions

Le principal but des transactions est de s’assurer que toutes les opérations sont sauvées, ou qu’aucune opération n’est sauvée en base. Les transactions doivent respecter les propriétés référencées via l’acronyme ACID (Atomicité, Cohérence, Isolation, Durabilité) :

Atomicité : toutes les opérations d’une transaction doivent se dérouler avec succès. Sinon la transaction échoue.

Cohérence : lors d’une transaction, seules des données valides sont insérées, créées, modifiées ou supprimées en base. Par exemple, supprimer un utilisateur en base implique de supprimer également son adresse.

Isolation : plusieurs transactions qui se déroulent en même temps ne doivent pas avoir d’impacts entre elles.

Durabilité : les résultats des transactions commitées sont permanents. Ils doivent survivre à un crash ou à un redémarrage du système.

L’un des avantages des EJB est la possibilité pour le développeur de ne pas avoir à définir les limites des transactions (begin, commit, rollback). C’est le mode managé (ou CMT : Container­Managed Transaction) : le container gère les transactions et le développeur peut spécifier les méthodes qui sont engagées dans une transaction.

À l’inverse, dans un mode non managé (ou BMT : Bean­Managed Transaction), le développeur gère les transactions de bout en bout.

La JTA (Java Transaction API) est le composant central des EJB pour la gestion des transactions par le container. Elle inclue l’interface javax.transaction.UserTransaction qui permet au développeur de spécifier les limites des transactions lorsqu’il décide de développer en mode managé (par le container) mais souhaite avoir un contrôle sur les transactions.

La Java Persistence API peut être utilisée dans un environnement où la JTA n’est pas supportée. Dans ce cas la gestion des transactions passe par une interface alternative : EntityTransaction. Cette interface est utilisée pour contrôler les transactions d’entity managers en mode resource­local (c’est­à­dire hors container).

La Java Persistence API n’a pas besoin d’être utilisée dans un container qui supporte la JTA. En fait la méthode EntityManager.getTransaction(), qui renvoie un objet EntityTransaction, ne devrait pas être appelée à partir d’un EntityManager avec un type de transaction JTA. Autrement dit, il ne faut pas utiliser la méthode getTransaction() si dans le fichier persistence.xml l’attribut transaction­type est défini comme JTA.

Ceci impacte les entity managers :

Si l’entity manager est obtenu avec la méthode createEntityManager(), de l’interface EntityManagerFactory, alors les transactions sont managées par l’application (le développeur). Ce qui signifie qu’il est nécessaire d’appeler la méthode EntityManager.joinTransaction() pour participer à une transaction.

Si l’entity manager est obtenu par injection, alors les transactions sont managées par le container et l’entity manager est déjà dans une transaction.

Une application EJB 3 peut fonctionner en mode non managé. Dans ce cas l’interface EntityManagerFactory assure la connexion à la base et le développeur défini les limites des transactions.

On distingue deux modes de démarcation des transactions dans les EJB : les transactions managées par le container et les transactions managées par le développeur du bean.

et les transactions managées par le développeur du bean. Java EE supporte les transactions distribuées (des

Java EE supporte les transactions distribuées (des mises à jour peuvent se faire sur plusieurs bases de données) mais ne supporte pas les transactions imbriquées (une transaction ne peut démarrer à l’intérieur d’une autre transaction).

1. Les transactions managées par le container (mode déclaratif)

Dans ce mode, le container démarre et commite les transactions automatiquement. On parle également de mode CMT (Container­Managed Transaction). Dans ce mode, il existe 6 attributs applicables au niveau des méthodes et des classes pour gérer les transactions et leurs propagations. Les attributs qui sont spécifiés au niveau des méthodes doivent prendre en compte l’attribut de la classe dans laquelle elles sont définies. L’annotation qui sert à spécifier le

© ENI Editions - All rigths reserved - Moha Anisa

-

1 -

type de transaction au niveau d’une méthode est @TransactionAttribute :

@TransactionAttribute(value=XXX) public void nomMethode(){

}

où le type de transaction XXX peut être l’un des suivants :

Mandatory : cet attribut indique qu’une méthode doit obligatoirement être appelée dans une transaction. Sinon une exception est générée.

Required : cet attribut requiert une transaction. S’il n’y a pas de transaction alors une nouvelle transaction est créée. C’est la valeur par défaut d’une méthode dans le mode déclaratif.

Requires_New : cet attribut requiert la création d’une nouvelle transaction. S’il existe déjà une transaction, celle­ci est suspendue.

Supports : cet attribut indique qu’une transaction est supportée si elle existe mais elle n’est pas obligatoire.

Not_Supported : les transactions ne sont pas supportées.

Never : c’est l’inverse de l’attribut mandatory. La méthode ne doit jamais être exécutée dans une transaction. S’il en existe une, une exception est levée.

L’annotation @TransactionAttribute peut s’appliquer aussi bien à des méthodes qu’à des classes.

2. Les transactions managées par le développeur (mode programmatique)

Dans le mode programmatique, appelé également BMT (Bean­Managed Transaction), le container fournit encore la transaction mais le développeur peut démarrer lui­même une transaction (begin), la terminer (commit) ou revenir en arrière (rollback). L’annotation à utiliser dans ce cas au niveau de la classe est la suivante :

@TransactionManagement(TransactionManagementType.BEAN)

Seuls les sessions et message­driven beans peuvent utiliser le mode transactionnel BEAN. Par défaut, la valeur de l’attribut TransactionManagement est CONTAINER. Le mode CONTAINER est donc le mode par défaut des beans.

Dans le mode BEAN, l’interface javax.transaction.UserTransaction est utilisée pour démarrer et commiter une transaction. Les méthodes de cette interface sont :

void begin() : création d’une nouvelle transaction.

void commit() : terminer la transaction.

int getStatus() : obtenir le status d’une transaction.

void rollback() : rollbacker la transaction.

void setRollbackOnly() : la seule issue possible de la transaction est un rollback.

void setTransactionTimeout(int secondes) : modifier le timeout de la transaction.

3. Transactions et exceptions

Quand des exceptions se produisent, les comportements des clients des beans varient en fonction des types d’exceptions. Il existe deux types d’exceptions dans Java en général :

Checked exceptions : ce sont des exceptions attendues et vérifiées par le compilateur. Elles doivent être

-

2 -

© ENI Editions - All rigths reserved - Moha Anisa

catchées dans un bloc try­catch ou bien être présentes dans les clauses throws des signatures des méthodes qui peuvent lancer de telles exceptions. Elles dérivent de la classe java.lang.Exception.

Unchecked exceptions : le compilateur ne les vérifie pas. Elles sont détectées à l’exécution et dérivent des classes java.lang.Error (par exemple OutofMemoryError) et java.lang.RuntimeException. La clause throws est optionnelle.

Dans les EJB, les exceptions sont distinguées suivant deux catégories :

System exceptions : ce sont des exceptions inattendues, par exemple une erreur de connexion à la base. Elles correspondent aux exceptions qui sont des classes filles de java.lang.RuntimeException et java.rmi.RemoteException. Parmi ces exceptions se trouvent javax.ejb.EJBException, javax.ejb.EJBAccessException et java.lang.NullPointerException. Elles sont loggées par le serveur.

Application exceptions : ce sont des checked et unchecked exceptions. Elles sont attendues et donc gérées par le client.

La table suivante décrit l’impact d’une exception sur une transaction suivant les types d’exceptions rencontrées et les types de gestion des transactions :

 

Application exceptions

System exceptions

Transaction managée par le développeur

La transaction continue (sauf si le bean appelle la méthode setRollbackOnly()).

Rollback automatique. L’instance du bean est détruite.

Transaction managée par le container

La transaction continue.

Rollback automatique. L’instance du bean est détruite.

Pour définir une exception de type Application exception, l’API EJB 3 met à notre disposition l’annotation @javax.ejb.ApplicationException. L’attribut rollback permet au développeur de décider si la transaction doit être rollbackée (true) ou non (false, valeur par défaut) :

import javax.ejb.ApplicationException;

@ApplicationException(rollback=true) public class MonException extends Exception {

}

La transaction est automatiquement rollbackée lorsque l’exception MonException est levée.

4. Création d’un client web

Il est commun d’avoir dans la même archive EAR (Enterprise Application Archive) un module EJB et un module Web. Dans ce cas, le module Web fonctionne en tant que client du module EJB.

Dans cet exemple, vous allez écrire un simple module Web constitué d’une page JSP qui appelle une servlet. Une servlet est une classe qui traite et répond à des requêtes HTTP. Cette classe étend la classe javax.servlet.GenericServlet ou la classe javax.servlet.http.HttpServlet. La servlet réalise la connexion avec le module EJB et affiche dans la console les messages de l’EJB.

Il faut rendre les EJB visibles au client Web, autrement dit il faut lier le projet Web au projet EJB. Pour cela, faites un clic droit sur le projet, allez dans les propriétés puis dans Java EE Module Dependencies. Cochez la check box du module VenteEnLigneEJB.jar.

© ENI Editions - All rigths reserved - Moha Anisa

-

3 -

■ Cliquez sur OK . Le module EJB est désormais visible dans le projet Web.

Cliquez sur OK. Le module EJB est désormais visible dans le projet Web. Toujours au niveau du projet VenteEnLigneWeb, faites un clic droit sur WebContent ­ New ­ JSP. Créez une JSP index.jsp. À l’intérieur de cette JSP, appelez la Servlet TestTransactionBMT, qu’il faudra écrire par la suite.

Le code de la servlet TestTransactionBMT.java est le suivant :

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"

pageEncoding="ISO-8859-1"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Client EJB</title> </head> <body>

<h1>TestTransactionBMT</h1>

<a href="TestTransactionBMT">Cliquez ici pour appeler la servlet TestTransactionBMT</a>

</body>

</html>

Pour créer la servlet TestServlet dans le package, faites un clic droit sur Java Resources : src ­ New ­ Servlet.

-

4 -

© ENI Editions - All rigths reserved - Moha Anisa

Le code de la servlet TestTransactionBMT.java est le suivant : package com.eni.dvtejb.web; import java.io.IOException;

Le code de la servlet TestTransactionBMT.java est le suivant :

package com.eni.dvtejb.web; import java.io.IOException; import java.math.BigDecimal;

import javax.annotation.Resource; import javax.ejb.TransactionManagement; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;

import com.eni.dvtejb.metier.entities.Adresse; import com.eni.dvtejb.metier.entities.Utilisateur;

@TransactionManagement(javax.ejb.TransactionManagementType.BEAN) public class TestTransactionBMT extends javax.servlet.http.HttpServlet { static final long serialVersionUID = 1L;

@Resource private javax.transaction.UserTransaction userTx;

private EntityManagerFactory emFactory; private EntityManager em;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try {

emFactory = Persistence.createEntityManagerFactory("VenteEnLigneClientJavaEE"); em = emFactory.createEntityManager();

userTx.begin();

© ENI Editions - All rigths reserved - Moha Anisa

-

5 -

em.joinTransaction();

//Ajout d’une adresse

Adresse adresse = new Adresse(); BigDecimal codePostal = new BigDecimal("75110"); adresse.setCodepostal(codePostal); adresse.setDepartement("Paris"); BigDecimal numero = new BigDecimal("45"); adresse.setNumero(numero); adresse.setPays("France"); adresse.setRue("Lafayette"); adresse.setVille("Paris");

em.persist(adresse);

//Ajout d’un client

Utilisateur client = new Utilisateur ();

client.setEmail("client1@super.com");

BigDecimal fax = new BigDecimal("1115333"); client.setFax(fax); client.setLogin("azerty"); client.setPassword("qwerty"); client.setNom("Dabet"); client.setPrenom("Jean"); BigDecimal telephone = new BigDecimal("4567899"); client.setTelephone(telephone); client.setTitre("Mr"); client.setAdresseFk(adresse);

em.persist(client);

userTx.commit();

} catch (Exception e) { e.printStackTrace();

}

}

}

5. Création du fichier persistence.xml dans le projet VenteEnLigneClient

L’unité de persistance utilisée cette fois­ci est VenteEnLigneModuleEJB. Le fichier de persistance utilisé est présent dans le projet VenteEnLigneEJB dans le répertoire /META­INF/.

<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/persistence

http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">

<persistence-unit name="VenteEnLigneModuleEJB" transaction-type="JTA">

<provider>org.hibernate.ejb.HibernatePersistence</provider>

<jta-data-source>java:/OracleDS</jta-data-source>

<class>com.eni.dvtejb.metier.entities.Utilisateur</class>

<class>com.eni.dvtejb.metier.entities.Adresse</class>

<class>com.eni.dvtejb.metier.entities.Commande</class>

<class>com.eni.dvtejb.metier.entities.Lignecommande</class>

<class>com.eni.dvtejb.metier.entities.Article</class>

<class>com.eni.dvtejb.metier.entities.Produit</class>

<class>com.eni.dvtejb.metier.entities.Catalogue</class>

<properties>

-

6 -

© ENI Editions - All rigths reserved - Moha Anisa

<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/> <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle9Dialect" /> <property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.show_sql" value="true" /> </properties>

</persistence-unit>

</persistence>

Il est nécessaire d’ajouter une balise <jta­data­source>.

Elle fait référence au nom JNDI de la data source (Oracle), qui provient du nom JNDI du fichier deploy/oracle­ds.xml, et qui est utilisée par le container.

6. Contenu du descripteur de déploiement web.xml

Lorsque le serveur web reçoit une requête, il détermine la classe de la servlet à appeler grâce à un fichier de configuration. Ce fichier de configuration est un descripteur de déploiement de l’application web et s’appelle web.xml. Il réside dans le répertoire /WEB­INF.

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID"

version="2.5">

<display-name>VenteEnLigneWeb</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <servlet> <description></description> <display-name>TestTransactionBMT</display-name> <servlet-name> TestTransactionBMT </servlet-name> <servlet-class>com.eni.dvtejb.web.TestTransactionBMT </servlet-class> </servlet> <servlet-mapping> <servlet-name> TestTransactionBMT </servlet-name> <url-pattern>/TestTransactionBMT </url-pattern> </servlet-mapping> </web-app>

Ce fichier web.xml déclare une servlet nommée TestTransactionBMT et la mappe à l’URL dont le chemin est /TestTransactionBMT.

Le tag <welcome­file­list> désigne une liste de fichiers que le serveur vérifie et affiche si l’utilisateur entre une URL qui n’est pas mappée à une servlet et qui correspond à un chemin de répertoire.

Si vous changez le fichier web.xml, vous devez arrêter et démarrer le serveur pour voir les changements.

© ENI Editions - All rigths reserved - Moha Anisa

-

7 -

Le mapping des relations

Les relations entre les tables sont définies grâce aux annotations suivantes :

@OneToOne, @OneToMany, @ManyToOne et @ManyToMany

Ces relations peuvent être unidirectionnelles ou bidirectionnelles.

Dans une relation unidirectionnelle, une seule entité possède un champ relationnel ou une propriété qui fait référence à l’autre entité. L’entité A référence l’entité B, mais l’entité B ne fait pas référence à l’entité A.

Dans une relation bidirectionnelle, chaque entité a un champ relationnel ou une propriété qui fait référence à l’autre entité.

Les stratégies d’implémentation pour chacune de ces relations sont diverses et leurs choix dépendent principalement des performances.

Dans une relation entre deux tables, on fait la distinction entre la table qui possède la relation et la table inverse. La table qui possède la relation est habituellement celle qui possède la clé étrangère.

1. Les relations OneToOne

Unidirectionnelle

C’est le cas par exemple de la table Utilisateur avec la table Adresse.

par exemple de la table Utilisateur avec la table Adresse. Le choix ici a été de

Le choix ici a été de déclarer une clé étrangère dans la table UTILISATEUR, c’est­à­dire la table qui possède la relation. Cette clé étrangère pointe sur la clé primaire de la table ADRESSE.

Au niveau des entités, la relation est reflétée dans les classes de la façon suivante :

@Entity public class Client implements Serializable {

@JoinColumn (name="ADRESSE_FK") @OneToOne private Adresse adresseFk;

}

@Entity public class Adresse implements Serializable {

}

Bidirectionnelle

Dans le cas d’une relation bidirectionnelle, il faut ajouter dans la classe inverse l’attribut mappedBy au champ qui fait référence à la classe qui possède la relation.

qui fait référence à la classe qui possède la relation. @Entity public class Commande implements Serializable

@Entity public class Commande implements Serializable {

@OneToOne (mappedBy="commande") private Compte compte;

© ENI Editions - All rigths reserved - Moha Anisa

-

1 -

}

@Entity public class Facture implements Serializable {

@OneToOne private Commande commande;

}

2. Les relations OneToMany et ManyToOne

Il y a deux façons de mapper des relations one­to­many, soit avec une table de jointure (liaison unidirectionnelle), soit en définissant une colonne en tant que clé étrangère, au niveau de la classe côté many (liaison bidirectionnelle).

L’entité Utilisateur possède une relation many­to­one avec l’entité Adresse. L’annotation @ManyToOne est utilisée dans l’entité qui représente la partie « plusieurs » de la relation.

Entité Utilisateur

@ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn(name="ADRESSE_FK") private Adresse adresseFk;

Entité Adresse

@OneToMany(mappedBy="adresseFk") private Set<Utilisateur> clientCollection;

L’annotation @ManyToOne possède les attributs FETCH et CASCADE :

Attribut FETCH

Cet attribut déclare la façon dont les données sont chargées depuis la base. En spécifiant la valeur EAGER à l’attribut FETCH, le container charge en mémoire l’entité Adresse et il est ainsi possible d’y accéder directement par le getter getAdresseFk() sur une instance de l’entité Utilisateur.

L’autre valeur possible est LAZY (chargement retardé). Dans ce cas, l’entité Adresse est chargée quand elle est accédée. Les modes de chargement des entités sont donc de type EAGER ou LAZY. Ce dernier étant le mode par défaut. La différence entre ces deux modes peut être expliquée par le code de l’entité A suivante :

@Entity public class A implements Serializable {

@Id private long id;

@Column(name="B") private B b; // B est une entité

@Column(name="C") private C c; // C est une autre entité

}

Dans le mode LAZY, au chargement d’une entité dans le contexte JPA (suite à une requête par exemple), seuls les champs de l’entité A sont chargés en mémoire. Les entités B et C ne seront chargées que lors de leur première utilisation. Alors que dans le mode EAGER, les champs de l’entité A sont chargés ainsi que les entités B et C. Toutes les trois sont immédiatement chargées. En général, le mode EAGER (chargement immédiat) est adapté aux applications Web à partir du moment où les données des tables en base ne sont pas trop importantes et en prenant en compte également la mémoire disponible sur le serveur.

Attribut CASCADE

Les valeurs possibles de cet attribut sont : PERSIST, REMOVE, REFRESH, MERGE, ALL.

-

2 -

© ENI Editions - All rigths reserved - Moha Anisa

Cet attribut permet de spécifier le niveau de propagation souhaité sur les entités liées à une entité dite cible. C’est donc une persistance en cascade.

Ainsi, l’entité ADRESSE sera mise à jour (MERGE) quand l’entité UTILISATEUR sera mise à jour.

3. Les relations ManyToMany

C’est le cas de la relation entre une Commande et une Ligne de commande :

Entité Commande

@ManyToMany @JoinTable(name="LIGNECOMMANDE_TJ", joinColumns=@JoinColumn(name="COMMANDE_FK"), inverseJoinColumns=@JoinColumn(name="LIGNECOMMANDE_FK")) private Set<Lignecommande> lignecommandeCollection;

Entité LigneCommande

@ManyToMany(mappedBy="lignecommandeCollection") private Set<Commande> commandeCollection;

Voici un tableau récapitulatif des relations présentes dans l’application :

Relation

Partie qui contient la relation

Multiplicité

Type de Fetch

Utilisateur Adresse

Utilisateur

ManyToOne

EAGER

Utilisateur Commande

Commande

OneToMany

LAZY

Article Produit

Article

ManyToOne

LAZY

Article Stock

Article

OneToOne

LAZY

Article LigneCommande

LigneCommande

OneToMany

 

Produit Catalogue

Produit

ManyToOne

LAZY

Commande Adresse

Commande

ManyToOne

LAZY

Commande Utilisateur

Commande

ManyToOne

LAZY

Commande LigneCommande

Aucune (table de jointure)

ManyToMany

LAZY

© ENI Editions - All rigths reserved - Moha Anisa

-

3 -

Les stratégies d’héritage

Il existe trois stratégies d’héritage possibles pour mapper des classes à des tables. Chaque stratégie présente des avantages et des inconvénients.

1. Une table unique

Dans cette stratégie, toutes les classes sont mappées à une seule table. C’est la stratégie par défaut et c’est celle qui a été choisi pour mapper les entités Client, Administrateur et Gestionnaire.

La table Utilisateur possède une colonne type_util qui joue le rôle de discriminateur.

Ce discriminateur permet de mapper la table vers trois sous­classes de la classe Utilisateur grâce à ce discriminateur. Les valeurs possibles sont C, A et G.

Modifiez l’accesseur de la classe Utilisateur pour la rendre abstraite et ajoutez les annotations @Inheritance et @DiscriminatorColumn :

@Entity @SequenceGenerator(name="SeqUtilisateur", sequenceName="utilisateur_seq") @Table(name="UTILISATEUR", schema="HR") @Inheritance(strategy=InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name="TYPE_UTIL", discriminatorType=DiscriminatorType.STRING, length=1) abstract class Utilisateur implements Serializable {

}

Le champ discriminatoire étant déclaré dans cette classe abstraite, il faut supprimer le champ type_util et ses accesseurs (getter et setter) de la classe Utilisateur.

Créez ensuite les trois sous­classes qui correspondent aux entités Client, Administrateur et Gestionnaire et annotez­ les en spécifiant la valeur de la colonne discriminante à l’aide de l’annotation @DiscriminatorValue. La stratégie d’héritage (SINGLE_TABLE) est décrite dans l’attribut strategy de l’annotation @Inheritance.

Entité Client :

package com.eni.dvtejb.metier.entities;

import javax.persistence.DiscriminatorValue; import javax.persistence.Entity;

@Entity @DiscriminatorValue(value="C") public class Client extends Utilisateur {

}

Entité Administrateur :

package com.eni.dvtejb.metier.entities;

import javax.persistence.DiscriminatorValue; import javax.persistence.Entity;

@Entity @DiscriminatorValue(value="A") public class Administrateur extends Utilisateur {

}

Entité Gestionnaire :

© ENI Editions - All rigths reserved - Moha Anisa

-

1 -

package com.eni.dvtejb.metier.entities;

import javax.persistence.DiscriminatorValue; import javax.persistence.Entity;

@Entity @DiscriminatorValue(value="G") public class Gestionnaire extends Utilisateur{

}

Modifiez également les deux fichiers persistence.xml (dans les projets VenteEnLigneClient et VenteEnLigneEJB) pour y ajouter les trois entity beans précédemment créés :

<persistence-unit name="VenteEnLigneClientTest" transaction-type="JTA">

<class>com.eni.dvtejb.metier.entities.Utilisateur</class>

<class>com.eni.dvtejb.metier.entities.Adresse</class>

<class>com.eni.dvtejb.metier.entities.Commande</class>

<class>com.eni.dvtejb.metier.entities.Lignecommande</class>

<class>com.eni.dvtejb.metier.entities.Article</class>

<class>com.eni.dvtejb.metier.entities.Produit</class>

<class>com.eni.dvtejb.metier.entities.Catalogue</class>

<class>com.eni.dvtejb.metier.entities.Client</class>

<class>com.eni.dvtejb.metier.entities.Administrateur</class>

<class>com.eni.dvtejb.metier.entities.Gestionnaire</class>

</persistence-unit>

2. Une table par classe

Dans cette stratégie, chaque classe est mappée à une table séparée. C’est le cas des autres tables du modèle. Cette stratégie est déclarée avec l’annotation @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)

Entité Adresse :

package com.eni.dvtejb.metier.entities;

import java.io.Serializable; import java.math.BigDecimal; import java.util.Set;

import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.OneToMany; import javax.persistence.SequenceGenerator;

@Entity @SequenceGenerator(name="SeqAdresse", sequenceName="adresse_seq") @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) public class Adresse implements Serializable {

}

3. Une table par classe fille

Une table unique contient tous les champs communs qui se trouvent dans une classe mère, alors que les classes filles avec leurs champs spécifiques sont mappées sur des tables séparées.

-

2 -

© ENI Editions - All rigths reserved - Moha Anisa

Les tables des classes filles sont reliées à la table de la classe mère par des clés primaires. Une colonne discriminante est aussi utilisée dans cette stratégie. Elle détermine les valeurs des clés primaires.

L’annotation de cette stratégie est la suivante : @Inheritance(strategy=InheritanceType.JOINED)

© ENI Editions - All rigths reserved - Moha Anisa

-

3 -

Le langage JPQL

Le langage JPQL (Java Persistence Query Language) est une extension d’EJB QL (Enterprise JavaBean Query Language, une nouveauté introduite dans les spécifications 2.0), proche de la syntaxe SQL mais orienté objet. En effet, les requêtes JPQL opèrent sur des classes et des objets persistés (entités) au lieu de tables et colonnes. Elles renvoient des entités JPA.

JPQL fait partie de la spécification JPA (JSR 220) et améliore EJB QL en fournissant plus d’options :

les projections,

les requêtes paramétrées,

les sous­requêtes,

les requêtes dynamiques,

les clauses GROUP BY et HAVING,

l’opérateur JOIN,

DELETE et UPDATE,

SQL natif.

L’interface EntityManager vue précédemment fournit la méthode createQuery() pour créer des requêtes. Cette méthode retourne une instance Query.

Les requêtes sont exécutées quand les méthodes getSingleResult(), getResultList() ou executeUpdate() de l’interface javax.persistence.Query sont appelées.

Pour exécuter une requête de suppression (DELETE) ou de mise à jour (UPDATE), il faut utiliser la méthode executeUpdate(). Elle retourne le nombre d’objets modifiés.

@PersistenceContext em;

Query query = em.createQuery("DELETE FROM Article a"); int nbArticlesSupprimes = q.executeUpdate();

La méthode getSingleResult() est à utiliser lorsque le résultat attendu de la requête retourne 0 ou 1 ligne. Elle retourne un objet unique. Si plus d’une ligne est retournée, alors une exception est générée.

@PersistenceContext em;

Query query = em.createQuery("SELECT a FROM Article a WHERE a.nom = :valeur"); q.setParameter("valeur", articleNom); Article art = q.getSingleResult();

Les requêtes suivantes utilisent les entités Client, Catalogue, Produit, Article, Commande, Adresse.

Client, Catalogue, Produit, Article, Commande, Adresse. Les opérations d’insertion INSERT ne sont pas supportées

Les opérations d’insertion INSERT ne sont pas supportées par JPQL. De nouvelles lignes peuvent être insérées en base en utilisant la méthode EntityManager.persist().

Pour faciliter l’écriture de requêtes JPQL, vous pouvez facilement mettre en place un requêteur sous la forme d’une page JSP à l’intérieur de l’application Web VenteEnLigneWeb.

Pour cela créez la classe /VenteEnLigneWeb/src/com/eni/dvtejb/web/EntityManagerSingleton.java suivante :

package com.eni.dvtejb.web;

import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence;

© ENI Editions - All rigths reserved - Moha Anisa

-

1 -

public class EntityManagerSingleton {

private static EntityManager entityManager = null;

public static EntityManager getInstance() { if ( entityManager == null) { EntityManagerFactory entityManagerFactory; entityManagerFactory = Persistence.createEntityManagerFactory("VenteEnLigneClientJavaSE"); entityManager = entityManagerFactory.createEntityManager(); return entityManager;

}

else return entityManager;

}

}

Le rôle de cette classe est de fournir une instance unique de la classe EntityManager, conformément au design pattern Singleton. L’unité de persistance VenteEnLigneClientJavaSE a un type de transaction qui est RESOURCE_LOCAL.

Puis créez la JSP /VenteEnLigneWeb/WebContent/requeteur.jsp suivante :

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"

pageEncoding="ISO-8859-1"

import="javax.persistence.*,java.util.*,com.eni.dvtejb.web.*"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Requêteur</title> </head> <body> <center> <h1>Requêteur JPQL</h1> <form action="requeteur.jsp" method="post"> <b>Tapez la requête : </b> <br/> <textarea name="requete" rows="10"

cols="70">${param.requete}</textarea><br/>

<input type="submit" value="Lancer la requête JPQL"/> </form>

<p/>

<%

String requete = request.getParameter("requete");

if ( requete == null || requete.length() == return;

}

0) {

EntityManager em = EntityManagerSingleton.getInstance(); List liste = null; try { Query req = em.createQuery(requete); liste = req.getResultList();

}

catch(Exception ex) { out.println("<p/>Erreur : " + ex.getMessage()); return;

}

out.println("<h2>Resultat </h2><table border=’2’");

for (Object obj : liste) { out.println("<tr>"); if (obj instanceof Object[]) { for( Object o : (Object[]) obj) out.println( "<td>" + o + "</td>");

}

else { out.println("<td>" + obj.toString() + "</td>");

-

2 -

© ENI Editions - All rigths reserved - Moha Anisa

}

out.println("</tr>");

}

%>

</center>

</body>

</html>

Le résultat de la requête JPQL s’affiche sur la même page :

de la requête JPQL s’affiche sur la même page : 1. La clause SELECT et les

1. La clause SELECT et les requêtes dynamiques

Récupération de tous les clients :

@PersistenceContext em;

public List findTousClients() { Query requete = em.createQuery("SELECT c FROM Client c"); return requete.getResultList();

}

Cette requête peut également s’écrire sans le mot­clé SELECT :

Query requete = em.createQuery("FROM Client");

Récupération des clients dont le nom est Bock :

@PersistenceContext em;

Query requete = em.createQuery("SELECT c FROM Client c WHERE nom = ’Bock’"); List<Client> resultats = requete.getResultList(); for (Client p : resultats) { System.out.println(p.getPrenom());

© ENI Editions - All rigths reserved - Moha Anisa

-

3 -

}

Récupération des clients dont les noms commencent par la lettre B :

@PersistenceContext em;

Query reqLettre = em.createQuery("SELECT c FROM Client c WHERE nom LIKE ’B%’"); List<Client> resultatsreqLettre = reqLettre.getResultList(); for (Client p : resultatsreqLettre) { System.out.println(p.getNom());

}

Liste des types de cartes de crédit :

@PersistenceContext em;

Query reqDistinct = em.createQuery("SELECT DISTINCT co.typeCartecredit FROM Commande co " ); List<String> typesCarteCredit = reqDistinct.getResultList(); for (String typeCarteCredit : typesCarteCredit) { System.out.println(typeCarteCredit);

}

Cette requête renvoie une liste de chaînes de caractères représentant les types de carte de crédit trouvés dans la table COMMANDE :

@PersistenceContext em;

Query reqJoin = em.createQuery("SELECT a FROM Article a, IN(a.produitFk) prod WHERE prod.nom = ’Foot’" ); List<Article> resultatsreqJoin = reqJoin.getResultList(); for (Article a : resultatsreqJoin) { System.out.println(a.getNom());

}

2. La clause SELECT NEW

La clause SELECT NEW représente une utilisation avancée de JPQL. C’est une expression JPQL sur un constructeur. Elle renvoie des instances d’une classe qui correspondent aux données retournées par la requête. Elle est particulièrement adaptée aux objets de type DTO (Data Transfer Object). Son utilisation est détaillée dans le chapitre Développement d’un client avec JSF 2.

La requête suivante retourne la liste des noms des articles avec les descriptions des catalogues et produits correspondants :

public List<MagasinDTO> findBoutique(){ String requete = " SELECT NEW

com.eni.dvtejb.metier.dtos.DescriptionDTO(c.description, p.description, a.nom) " +

" FROM Catalogue c, Produit p, Article a" +

" WHERE c.catalogueid = p.catalogueFk" +

" AND p.produitid = a.produitFk";

return entityManager.createQuery(requete).getResultList();

}

Chaque ligne retournée par la requête est stockée dans une instance de la classe DescriptionDTO.

3. L’opérateur JOIN

Utilisation de l’opérateur JOIN pour récupérer la liste des articles du produit Foot :

L’opérateur JOIN remplace l’opérateur IN de la clause FROM des EJB 2. L’opérateur IN est toujours supporté.

@PersistenceContext em;

-

4 -

© ENI Editions - All rigths reserved - Moha Anisa

Query reqJoin = em.createQuery("SELECT a FROM Article a JOIN a.produitFk prod WHERE prod.nom = ’Foot’" ); List<Article> resultatsreqJoin = reqJoin.getResultList(); for (Article a : resultatsreqJoin) { System.out.println(a.getNom());

}

L’opérateur JOIN permet de faire le produit cartésien des lignes des tables Article et Produit.

Utilisation de l’opérateur INNER JOIN pour récupérer les noms des clients qui sont des hommes et dont l’adresse a un code postal > 77000 :

@PersistenceContext em;

Query reqInnerJoin = em.createQuery("SELECT c FROM Client c INNER JOIN c.adresseFk ad WHERE c.titre = ’Mr’ AND ad.codepostal > 77000" ); List<Client> clients = reqInnerJoin.getResultList(); for (Client client : clients) { System.out.println(client.getNom());

}

L’opérateur JOIN est équivalent à l’opérateur INNER JOIN. En effet une jointure est une jointure interne par défaut.

En effet une jointure est une jointure interne par défaut. Les mots­clés ne sont pas case­sensitives.

Les mots­clés ne sont pas case­sensitives. En revanche les noms des variables sont case­sensitives. Vous obtiendrez une erreur si vous tentez de faire une requête sur l’entité client avec un c minuscule au lieu d’un C majuscule.

Liste de toutes les commandes issues des clients dont l’id est supérieur à 2 :

@PersistenceContext em;

Query reqLeftJoin = em.createQuery("SELECT co FROM Commande co LEFT JOIN co.clientFk c WHERE c.clientid > 2" ); List<Commande> commandes = reqLeftJoin.getResultList(); for (Commande commande : commandes) { System.out.println(commande.getCommandeid());

}

L’opérateur LEFT JOIN est similaire à l’opérateur LEFT OUTER JOIN.

Utilisation d’une sous­requête pour sélectionner les commandes du client dont le nom est Gatersa :

@PersistenceContext em;

Query reqSousReq = em.createQuery("SELECT DISTINCT co FROM Commande co WHERE EXISTS " + " (SELECT cl FROM co.clientFk cl WHERE cl.nom =’Gatersa’ )"); List<Commande> listeCommandes = reqSousReq.getResultList(); for (Commande commande : listeCommandes) { System.out.println(commande.getCommandeid());

}

Les sous­requêtes sont autorisées dans les clauses HAVING et WHERE.

4. Les paramètres

Les paramètres des requêtes peuvent être nommés ou numérotés. Ils sont indiqués selon deux notations :

avec des points d’interrogation suivis de chiffres pour préciser leur position :

@PersistenceContext em;

Query reqPosition = em.createQuery("SELECT c.prenom FROM Client c

WHERE c.nom = ?1 AND c.titre

reqPosition.setParameter(1, "Duc");

= ?2 ");

© ENI Editions - All rigths reserved - Moha Anisa

-

5 -

reqPosition.setParameter(2, "Mr"); List<String> listePrenomsPt = reqPosition.getResultList(); for (String prenom : listePrenomsPt) { System.out.println(prenom);

}

Leur position commence à partir de 1.

avec deux points suivis du nom du paramètre :

@PersistenceContext em;

Query reqDeuxPoints= em.createQuery("SELECT c.prenom FROM Client c WHERE c.nom = :nomChoisi "); reqDeuxPoints.setParameter("nomChoisi", "Duc"); List<String> listePrenoms = reqDeuxPoints.getResultList(); for (String prenom : listePrenoms) { System.out.println(prenom);

}

5. Les agrégateurs HAVING et GROUP BY

Sélection des titres des clients et de leur nombre en utilisant les agrégateurs HAVING et GROUP BY :

@PersistenceContext em;

Query reqGroup = em.createQuery("SELECT c.titre, count(c) FROM Client c GROUP BY c.titre HAVING COUNT(c)> 0"); List resultatsGroup = reqGroup.getResultList(); for (Iterator it = resultatsGroup.iterator(); it.hasNext();) { Object[] obj = (Object[]) it.next(); for (int i=0;i<obj.length;i++)

{

System.out.println(obj[i]);

}

}

6. Les requêtes nommées

Il est possible de définir des requêtes de façon statique avec les named queries, c’est­à­dire les requêtes nommées. Elles sont définies soit dans un fichier XML soit avec des annotations au niveau des entity beans.

Recherche des noms des clients qui ont pour prénom "Jules" :

Via des annotations :

Modifiez l’entity

@javax.persistence.NamedQuery :

bean

Client pour

lui

ajouter

une

requête nommée

avec l’annotation

@Entity @NamedQuery(name="Client.findByPrenom", query=" SELECT c FROM Client c WHERE c.prenom = :lePrenom") public class Client implements Serializable {

}

Ensuite depuis un stateful session bean par exemple (les session beans sont détaillés dans le chapitre suivant) vous pouvez appeler cette requête nommée de la façon suivante :

public List findByPrenom(String prenom){ log.debug("recherche par prenom"); Query q = em.createNamedQuery("Client.findByPrenom"); q.setParameter("lePrenom", prenom);

-

6 -

© ENI Editions - All rigths reserved - Moha Anisa

List<Client> resultatsNamedQuery = q.getResultList(); return resultatsNamedQuery;

}

Pour insérer plusieurs requêtes, utilisez l’annotation @javax.persistence.NamedQueries.

utilisez l’annotation @javax.persistence.NamedQueries. Veillez à ne pas mettre d’espace à l’intérieur de

Veillez à ne pas mettre d’espace à l’intérieur de l’argument de la méthode createNamedQuery(), entre les guillemets. Sinon une exception IllegalArgumentException sera générée. En effet la méthode ne supprime pas les espaces.

Via un mapping XML dans le fichier orm.xml :

Insérez la requête dans le fichier /META­INF/orm.xml du projet VenteEnLigneEJB :

Contenu du fichier orm.xml :

<?xml version="1.0" encoding="UTF-8"?> <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" version="1.0">

<named-query name="Client.findByPrenom "> <query> SELECT c FROM Client c WHERE c.prenom = :lePrenom</query> </named-query>

</entity-mappings>

Ce nom de requête doit être unique, il ne doit pas entrer en conflit avec d’autres requêtes dans ce fichier ou des requêtes définies au niveau des annotations.

7. Les requêtes natives

La Java Persistence API offre la possibilité d’exécuter des requêtes en SQL natif, c’est­à­dire Oracle SQL si la base de données est Oracle, par l’intermédiaire de la méthode createNativeQuery() de l’interface EntityManager. Dans ce cas, les requêtes manipulent des tables, et non plus des objets.

Pour insérer un nouveau catalogue dans la table Catalogue, la séquence suivante est mise en place dans Oracle :

create sequence catalogue_seq minvalue 1 start with 1 increment by 1

La requête d’insertion peut alors être la suivante :

public static final String INSERE_CATALOGUE = "INSERT INTO catalogue (catalogueid, description, nom) VALUES (catalogue_seq.nextval, ?1, ?2)";

Query reqNative = em.createNativeQuery(INSERE_CATALOGUE); reqNative.setParameter(1,"Pour les litteraires"); reqNative.setParameter(2, "Littérature");

EntityTransaction tx = em.getTransaction(); tx.begin(); reqNative.executeUpdate(); tx.commit();

8. Les requêtes polymorphiques

JPA supporte les requêtes polymorphiques : un SELECT sur l’entité Utilisateur ramène les lignes de l’entité Utilisateur ainsi que les lignes des classes filles (Client, Administrateur, Gestionnaire).

© ENI Editions - All rigths reserved - Moha Anisa

-

7 -

La requête suivante renvoie tous les noms des utilisateurs :

Query reqPoly = em.createQuery("SELECT u.prenom FROM Utilisateur u"); List<String> resultatsPoly = reqPoly.getResultList(); for (String prenom : resultatsPoly) { System.out.println(prenom);

}

-

8 -

© ENI Editions - All rigths reserved - Moha Anisa

Introduction

Les EJB sont des composants accessibles localement (même JVM) ou à distance (différentes JVM) via un lookup JNDI. Une référence à un composant EJB est similaire à un objet proxy. Chaque méthode de l’objet référencé est routée vers la même méthode du composant EJB d’origine.

Si le composant EJB est défini de façon locale, le routage est fait directement via reflection et les paramètres et le résultat sont passés par référence. Si le composant EJB est défini de façon distante (remote), le routage est fait via rpc et les paramètres et le résultat sont passés par valeur et sont sérialisés.

Les session beans réalisent des actions et constituent généralement le point d’entrée des clients des entity beans. Ils sont divisés en deux types : les stateful session beans (beans avec états) et les stateless session beans (beans sans états). Les session beans vont contenir la logique métier pour pouvoir rechercher des produits, supprimer des articles, ajouter un client, etc.

© ENI Editions - All rigths reserved - Moha Anisa

-

1 -

Connexion et maintien de l’état avec un stateful session bean

Le cycle de vie d’un session bean est managé par le container et fournit des méthodes callbacks qui permettent au développeur d’agir en fonction de certains évènements. Par exemple, si vous voulez ajouter une certaine logique après la création d’un EJB par le container, il suffit simplement de coder une méthode dans la classe de l’EJB et d’annoter cette méthode avec l’annotation @PostConstruct. Ou encore si vous voulez déclencher une action juste avant la destruction d’un EJB, il suffit de coder une méthode dans la classe de l’EJB et de l’annoter avec l’annotation @PreDestroy.

Le schéma suivant décrit le cycle de vie d’un stateful session bean :

décrit le cycle de vie d’un stateful session bean : 1. Méthodes callbacks En ce qui

1. Méthodes callbacks

En ce qui concerne les session beans, les méthodes callback, c’est­à­dire les méthodes qui seront automatiquement appelées, sont annotées avec les annotations suivantes :

<portée> void @PostConstruct : appelée après que le container ait créé une instance de la classe bean.

<portée> void @PrePassivate : appelée juste avant que le bean soit passivé.

<portée> void @PostActivate : appelée juste après l’activation du bean.

<portée> void @PreDestroy : appelée juste après la fin de chaque méthode @Remove et avant la destruction du bean.

Ces méthodes ne sont pas obligatoires.

destruction du bean. Ces méthodes ne sont pas obligatoires. Il est toujours possible d’utiliser le descripteur

Il est toujours possible d’utiliser le descripteur de déploiement ejb­jar.xml. Il faut noter qu’en cas d’utilisation mixte (annotations et fichier ejb­jar.xml), les entrées du descripteur de déploiement écrasent les annotations.

Un stateful session bean conserve dans le cache l’état d’un client, tout au long des requêtes qu’il a exécuté. Il existe une instance par client.

2. Interfaces distantes et locales

Un session bean (stateless et stateful) est constitué d’une ou deux interfaces qui peuvent être locales ou distantes et d’une classe bean.

Un bean qui implémente une interface locale est plus restrictif : il n’est accessible que par un bean ou un client qui est packagé dans la même archive EAR alors qu’un bean qui implémente une interface distante est accessible par tout

© ENI Editions - All rigths reserved - Moha Anisa

-

1 -

bean ou client qui est packagé dans la même archive EAR ou bien dans d’autres archives EAR.

Si les EJB sont invoqués à distance, c’est­à­dire à partir d’un autre serveur que celui sur lequel ils sont déployés, alors ils doivent implémenter des interfaces distantes.

Implémenter les deux interfaces (distante et locale) lors de l’écriture d’un session bean peut représenter un avantage dans la mesure où celui­ci est ainsi prêt à servir les deux types d’appel.

La distinction entre les deux types d’interface se fait également suivant la localisation de la JVM : les interfaces locales sont accessibles par des clients qui s’exécutent sur la même JVM uniquement alors que les interfaces distances sont accessibles par des clients qui s’exécutent sur la même JVM et sur une autre JVM. Si l’on prévoit de rendre disponibles les session beans pour des clients qui s’exécutent sur d’autres JVMs, il est donc préférable d’utiliser les interfaces distantes. Il faut tout de même savoir que les performances ne sont pas les mêmes : en effet les paramètres d’une interface locale sont passés par référence (et ne peuvent donc être accédés que sur la même JVM) alors que les paramètres d’une interface distante sont passés par valeur, ce qui implique de la sérialisation et donc une plus grande latence au niveau du réseau.

Le schéma suivant illustre cette distinction :

du réseau. Le schéma suivant illustre cette distinction : Les interfaces locales et distantes sont annotées

Les interfaces locales et distantes sont annotées respectivement avec les annotations @javax.ejb.Local et @javax.ejb.Remote. Mais ces annotations sont optionnelles et ne sont donc pas requises au niveau des interfaces. Par défaut, une interface qui n’est pas annotée est considérée comme une interface locale. Au niveau du bean, pour spécifier quelles interfaces celui­ci implémente, vous pouvez utiliser ces mêmes annotations en ajoutant comme attribut(s) le ou les interface(s).

Le bean n’a pas besoin d’implémenter l’interface javax.ejb.SessionBean. Vous pouvez tout simplement l’annoter avec @javax.ejb.Stateful.

Le panier d’achat peut être représenté par un stateful session bean puisqu’il doit connaître les articles qu’il contient entre chaque requête.

Créez maintenant ce stateful session bean.

-

2 -

© ENI Editions - All rigths reserved - Moha Anisa

Faites un clic droit sur le nom du projet VenteEnLigneEJB ­ New ­ Session Bean. Entrez com.eni.dvtejb.metier.sessions pour le nom du package et PanierBean pour le nom de la classe, sélectionnez les types Stateful et Remote, Local et cliquez sur Finish.

Stateful et Remote , Local et cliquez sur Finish . ■ Cliquez sur Next puis Finish

Cliquez sur Next puis Finish.

Le wizard a créé le bean PanierBean, les business interfaces remote PanierBeanRemote et locale PanierBeanLocal. Ces business interfaces sont en fait des POJI (Plain Oriented Java Interfaces).

L’interface PanierBeanRemote contient les méthodes suivantes :

package com.eni.dvtejb.metier.sessions; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.List;

import com.eni.dvtejb.metier.entities.Article; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.entities.Commande; import com.eni.dvtejb.metier.entities.Utilisateur; import com.eni.dvtejb.metier.services.ArticlePanier;

public interface PanierBeanRemote { public Commande genererCommande(Article[] listArticles, Client client, BigDecimal[] quantites); public void ajouterArticle(Article article, BigDecimal quantite); public Collection<String> getProduits(); public Article findById(long id); ArrayList<ArticlePanier> getPanier(); public double getMontantTotal(); public void viderPanier(); public void supprimerArticle(ArticlePanier articlePanier); public List afficherHistoCommandes(Utilisateur u);

© ENI Editions - All rigths reserved - Moha Anisa

-

3 -

public void commander(Client client, ArrayList<ArticlePanier>

articlesPanier, String numCC, String typeCC, java.sql.Date expirationDate);

}

Le bean PanierBean implémente toutes les méthodes de cette interface. Pour un premier test, voici le code de la méthode genererCommande(). Cette méthode n’est pas utilisée par la suite dans l’application, elle n’est utilisée que dans le test pour ce chapitre.

package com.eni.dvtejb.metier.sessions;

import java.io.Serializable; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set;

import javax.annotation.PostConstruct; import javax.ejb.Remote; import javax.ejb.Stateful; import javax.interceptor.Interceptors; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query;

import org.jboss.logging.Logger;

import com.eni.dvtejb.metier.entities.Adresse; import com.eni.dvtejb.metier.entities.Article; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.entities.Commande; import com.eni.dvtejb.metier.entities.Lignecommande; import com.eni.dvtejb.metier.entities.Utilisateur; import com.eni.dvtejb.metier.services.ArticlePanier; import com.eni.dvtjeb.metier.interceptors.CommanderInterceptor;

@Stateful @Remote (PanierBeanRemote.class) public class PanierBean implements PanierBeanRemote, Serializable {

private static final Logger log = Logger.getLogger(PanierBean.class);

@PersistenceContext(unitName="VenteEnLigneModuleEJB") EntityManager em;

private static final long serialVersionUID = 1L;

private ArrayList<ArticlePanier> articles;

// Initialisation de la liste d’articles @PostConstruct public void creation(){ articles = new ArrayList<ArticlePanier>();

}

/**

* Constructeur par défaut. */ public PanierBean() {

}

public Commande genererCommande(Article[] listArticles, Client client, BigDecimal[] quantites) {

// A chaque article (prix, nom) est associé une ligne de commande (quantite) List<Lignecommande> ligneCommandes = new

-

4 -

© ENI Editions - All rigths reserved - Moha Anisa

ArrayList<Lignecommande> (listArticles.length);

//

entityManager.getTransaction().begin();

for (int i=0;i<listArticles.length;i++){

Lignecommande lignecommande = new Lignecommande(); lignecommande.setArticleFk(listArticles[i]); lignecommande.setQuantite(quantites[i]); ligneCommandes.add(lignecommande);

}

// Une commande est composée de 1 à N lignes de commande Commande commande = new Commande(); commande.setUtilisateurFk(client);

Date aujourdhui = new Date(); long t = aujourdhui.getTime(); java.sql.Date aujourdhuiSQL = new java.sql.Date(t); commande.setDatecommande( aujourdhuiSQL);

Set<Lignecommande> set = new HashSet<Lignecommande>(); set.addAll( ligneCommandes );

commande.setLignecommandeCollection(set);

return commande;

}

// Implémentation du reste des méthodes

}

La sérialisation est utilisée pour pouvoir conserver l’état du bean (passivation, activation) mais il n’est pas nécessaire d’implémenter l’interface Serializable. En effet l’interface javax.ejb.Session hérite de l’interface javax.ejb.EnterpriseBean qui hérite déjà de java.io.Serializable. L’unité de persistance VenteEnLigneModuleEJB est définie dans le fichier persistence.xml du projet VenteEnLigneEJB.

3. Création d’un client web

Le client peut être la même page JSP index.jsp du projet VenteEnLigneWeb. Il suffit d’ajouter un lien qui appelle la Servlet TestServlet, qu’il reste à écrire par la suite.

Voici le code de la JSP index.jsp modifée :

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"

pageEncoding="ISO-8859-1"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Client EJB</title> </head> <body>

<h1>Module Web utilisé en tant que client EJB</h1> <a href="TestServlet">Cliquez ici pour appeler le session bean via la servlet TestServlet</a>

<h1>TestTransactionBMT</h1>

<a href="TestTransactionBMT ">Cliquez ici pour appeler la servlet TestTransactionBMT</a>

</body>

</html>

a. Modification du fichier jboss­service.xml

© ENI Editions - All rigths reserved - Moha Anisa

-

5 -

Le choix peut être fait de déclarer toutes les méthodes dans une interface distante. L’interface locale créée par Eclipse peut alors être supprimée. Il est donc tout à fait possible de déclarer des méthodes d’une interface distante et de les faire tourner sur un serveur d’application en local.

Pour pouvoir invoquer les EJB à distance, il est nécessaire de modifier la valeur de l’attribut CallByValue dans le fichier jboss­service.xml. Celui­ci se trouve dans le répertoire default/conf/ de JBoss. Changez la valeur à true.

<mbean code="org.jboss.naming.NamingService" name="jboss:service=Naming" xmbean-dd="resource:xmdesc/NamingService-xmbean.xml"> <!-- The call by value mode. true if all lookups are unmarshalled using the caller’s TCL, false if in VM lookups return the value by reference. --> <attribute name="CallByValue">true</attribute> <!-- The listening port for the bootstrap JNP service. Set this to -1 to run the NamingService without the JNP invoker listening port.

--> <attribute name="Port">1099</attribute> <!-- The bootstrap JNP server bind address. This also sets the default RMI service bind address. Empty == all addresses

--> <attribute name="BindAddress">${jboss.bind.address}</attribute> <!-- The port of the RMI naming service, 0 == anonymous --> <attribute name="RmiPort">1098</attribute>

<!-- The RMI service bind address. Empty == all addresses --> <attribute name="RmiBindAddress">${jboss.bind.address}</attribute> <!-- The thread pool service used to control the bootstrap lookups --> <depends optional-attribute-name="LookupPool" proxy-type="attribute">jboss.system:service=ThreadPool</depends> <!-- An example of using the unifed invoker as the transport. <depends optional-attribute-name="InvokerProxyFactory"

proxy-

type="attribute">jboss:service=proxyFactory,type=unified,target=Nami

ng</depends>

--> <depends optional-attribute-name="Naming" proxy-type="attribute">jboss:service=NamingBeanImpl</depends> </mbean>

Pour créer la servlet TestServlet dans le package com.eni.dvtejb.web, faites un clic droit sur Java Resources : src ­ New ­ Servlet.

-

6 -

© ENI Editions - All rigths reserved - Moha Anisa

Voici le code de la servlet TestServlet.java : package com.eni.dvdejb.web ; import java.io.IOException; import

Voici le code de la servlet TestServlet.java :

package com.eni.dvdejb.web ;

import java.io.IOException; import java.math.BigDecimal;

import javax.naming.InitialContext; import javax.naming.NamingException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;

import com.eni.dvtejb.metier.entities.Article; import com.eni.dvtejb.metier.entities.Client; import com.eni.dvtejb.metier.entities.Commande; import com.eni.dvtejb.metier.sessions.PanierBeanRemote;

public class TestServlet extends javax.servlet.http.HttpServlet { static final long serialVersionUID = 1L; private static Logger logger = Logger.getLogger(TestServlet.class.getName());

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { InitialContext initialContext = new InitialContext(); PanierBeanRemote panier = (PanierBeanRemote) initialContext.lookup("VenteEnLigne/PanierBean/remote");

logger.info ("-->> lookup effectué");

logger.info (" ----------- Debut -----------"); Article article1 = new Article();

article1.setNom("gants");

article1.setPrix(22);

Article article2 = new Article();

article2.setNom("chaussures");

article2.setPrix(44);

© ENI Editions - All rigths reserved - Moha Anisa

-

7 -

Article[] listArticles = new Article[2]; listArticles[0] = article1; listArticles[1] = article2;

Client client = new Client();

client.setEmail("client1@super.com");

BigDecimal fax = new BigDecimal("1115333"); client.setFax(fax); client.setLogin("azerty"); client.setPassword("qwerty"); client.setNom("Dabet"); client.setPrenom("Jean"); BigDecimal telephone = new BigDecimal("4567899"); client.setTelephone(telephone); client.setTitre("Mr");

Commande commande = panier. genererCommande (listArticles, client, new BigDecimal[]{new BigDecimal("3"), new

BigDecimal("5")});

System.out.println(commande.getCommandeid()); logger.info (commande.getClientFk().getNom()); logger.info (commande.getClientFk().getPrenom()); logger.info (" ----------- Fin -----------");

} catch (NamingException e) { e.printStackTrace();

}

}

}

b. Modification du descripteur de déploiement web.xml :

Il reste une dernière chose à faire pour pouvoir lancer le test : le mapping de la servlet TestServlet avec l’URL /TestServlet.

<servlet>

<description></description>

<display-name>TestServlet</display-name>

<servlet-name>TestServlet</servlet-name>

<servlet-class>com.eni.dvtejb.web.TestServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>TestServlet</servlet-name>

<url-pattern>/TestServlet</url-pattern>

</servlet-mapping>

L’appel de cette servlet permet de vérifier que l’appel de différents entity beans via un stateful session bean s’effectue correctement. La consultation des logs dans la console des logs Eclipse permet de voir que le déroulement de la classe de test s’est déroulé correctement.

4. Accès aux ressources avec le service de nommage JNDI

Le serveur d’application crée des variables dans le service de nommage JNDI (Java Naming Directory Interface). Le client passe par l’API JNDI pour y accéder. Ces variables sont stockées dans un InitialContext. La méthode lookup() retourne un Object qui doit être casté dans le type approprié.

Dans la version JAVA EE 5, le nommage JNDI par défaut des EJB n’est pas standardisé. Il est spécifique à chaque serveur. Dans JBoss, le nommage JNDI est fait selon les règles suivantes :

dans le cas d’un package JAR : <nom_classe>/<local ou remote> ;

dans le cas d’un package EAR : <nom_package_ear>/<nom_classeBean>/<local ou remote>.

-

8 -

© ENI Editions - All rigths reserved - Moha Anisa

L’archive générée étant de type EAR, le lookup de la ressource JNDI dans la servlet TestServlet.java se fait donc de la façon suivante :

PanierBeanRemote panier = (PanierBeanRemote) initialContext.lookup("VenteEnLigne/PanierBean/remote");

Redémarrez le serveur pour que les modifications soient prises en compte. L’archive VenteEnLigne.ear est déployée vers le répertoire K:\ENI\DeveloppementEJB3\jboss­6.0.0.M1\server\default\deploy.

Le contenu de l’archive VenteEnLigne.ear est le suivant :

Jar -tf VenteEnLigne.ear

META-INF/ META-INF/MANIFEST.MF META-INF/application.xml VenteEnLigneClient.jar VenteEnLigneConnector.rar VenteEnLigneEJB.jar VenteEnLigneWeb.war

VenteEnLigneWebJSF2.war

VenteEnLigneWebStruts2.war

VenteEnLigneFlexGranite.war

Le fichier MANIFEST.MF est un fichier qui peut contenir des informations sur les fichiers qui sont packagés dans une archive. Il peut par exemple contenir le nom de la classe qui constitue le point d’entrée (la classe qui contient la méthode public static void main(String[] args)) ou encore le numéro de version de l’archive.

Il sert également au déploiement d’une application, de manière à ajouter dans le classpath les librairies JAR dont dépendent d’autres librairies JAR, à l’intérieur d’une application EAR par exemple. La ligne suivante du fichier MANIFEST.MF ajoute les librairies projet1.jar et projet2.jar (séparées par des espaces) dans le classpath de l’application qui le contient :

Manifest-Version: 1.0 Class-Path: projet1.jar projet2.jar

Le fichier de déploiement META­INF/application.xml généré par Eclipse est le suivant :

<?xml version="1.0" encoding="UTF-8"?> <application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:application="http://java.sun.com/xml/ns/javaee/application_5.x

sd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

http://java.sun.com/xml/ns/javaee/application_5.xsd"

id="Application_ID" version="5"> <display-name> VenteEnLigne</display-name> <module> <ejb>VenteEnLigneEJB.jar</ejb> </module> <module> <web> <web-uri>VenteEnLigneWeb.war</web-uri> <context-root>VenteEnLigneWeb</context-root> </web> </module> <module> <java>VenteEnLigneClient.jar</java> </module> <module> <web>

<web-uri>VenteEnLigneWebStruts2.war</web-uri>

<context-root>VenteEnLigneWebStruts2</context-root>

</web> </module> <module> <web>

<web-uri>VenteEnLigneWebJSF2.war</web-uri>

<context-root>VenteEnLigneWebJSF2</context-root>

</web>

© ENI Editions - All rigths reserved - Moha Anisa

-

9 -

</module>

<module>

<web>

<web-uri>VenteEnLigneFlexGranite.war</web-uri>

<context-root>VenteEnLigneFlexGranite</context-root>