Systèmes d`information répartis

publicité
TD5 Initiation aux EJB3 avec Eclipse
Ecriture d’un ejb entity
Nous allons prendre ici l’exemple d’une gestion de stock très simplifiée. Nous allons gérer de manière persistante une
classe produit, c’est-à-dire que nous allons enregistrer les produits dans une base de données. Pour cela nous allons
créer un ejb entité lié à une table de la base de données.
1 Préparation du TD
1.1 MySQL
Pour simplifier la manipulation de MySQL, installez Wamp Server (présent dans l’archive) ou EasyPHP. Si vous avez déjà
installé soit mySQL soit une distribution comme Wampserver, ne réinstallez rien. Vérifiez que vous avez une base qui
s’appelle test_entite et qui ne contient pas de table « produit ». Dans le cas contraire, créez la base mais pas la table.
1.2 Le JDBC
La deuxième étape consiste à installer le connecteur JDBC pour MySQL et à créer la datasource vers la base test.
 Décompressez mysql-connector-java-5.1.19.zip sur le bureau
 Copiez uniquement le JAR mysql-connector-java-5.1.19-bin.jar dans le répertoire : C:\serveurs\jboss-as7.1.1.Final\modules\com\mysql\main (Créez les répertoires mysql et main).
 Créez un fichier module.xml dans ce même répertoire avec le contenu suivant :
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="com.mysql">
<resources>
<resource-root path="mysql-connector-java-5.1.19-bin.jar"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="javax.transaction.api"/>
<module name="javax.servlet.api" optional="true"/>
</dependencies>
</module>


Ouvriez avec Notepad++ le fichier C:\serveurs\jboss-as-7.1.1.Final\standalone\configuration\standalone.xml
Rajoutez la balise datasource suivante dans la balise datasources
<datasource jndi-name="java:jboss/datasources/testDS" pool-name="testDS" enabled="true" usejava-context="true">
<connection-url>jdbc:mysql://localhost:3306/test_entite</connection-url>
<driver>mysql</driver>
<security>
<user-name>root</user-name>
<password></password>
</security>
</datasource>

Rajoutez la balise driver suivante dans la balise drivers
<driver name="mysql" module="com.mysql">
<xa-datasource-class>com.mysql.jdbc.Driver</xa-datasource-class>
</driver>
Vous venez d’installer le JDBC comme un module de JBoss et de créer une datasource qui permet à JBoss
d’exploiter votre base test. Au prochain démarrage de JBoss, le driver sera automatiquement chargé.
ASI TD 5 – Anne Lapujade
1
2 L’EJB entité
2.1 La classe de l’ejb
Dans le package hw du projetEJB déjà existant, ajoutez une classe Produit avec le code suivant :
package hw;
import java.io.Serializable;
import
import
import
import
import
import
javax.persistence.Entity;
javax.persistence.GeneratedValue;
javax.persistence.GenerationType;
javax.persistence.Id;
javax.persistence.NamedQueries;
javax.persistence.NamedQuery;
@Entity
@NamedQueries ({
@NamedQuery(name="findAllProducts", query="SELECT p FROM Produit p ORDER BY p.quantiteEnStock"),
})
public class Produit implements Serializable {
@Id
@GeneratedValue (strategy=GenerationType.AUTO)
private int id;
private String libelle;
private int quantiteEnStock;
public Produit() {}
public Produit(String libelle, int quantiteEnStock) {
this.libelle = libelle;
this.quantiteEnStock = quantiteEnStock;
}
public int getId() {return id;}
public void setId(int nid){id=nid;}
public String getLibelle() {return libelle;}
public void setLibelle(String libelle) {this.libelle = libelle;}
public int getQuantiteEnStock() {return quantiteEnStock;}
public void setQuantiteEnStock(int quantiteEnStock){this.quantiteEnStock = quantiteEnStock;}
@Override
public String toString() {
return "Produit n°"+id+" - "+libelle+" - quantite disponible : "+ quantiteEnStock;
}
}
Seules les annotations @Entity et @Id distinguent cette classe d'une classe non persistante.
2.2 L’ejb façade
Dans tous les systèmes d'information, la base de données est complexe et faite de plusieurs tables interconnectées par
des clés étrangères. De plus, un processus métier fait couramment appel à plusieurs ejb. Par exemple, pour réserver une
chambre pour un client, il faut utiliser les ejb des hôtels et des chambres pour vérifier la réservation puis l'ejb des
réservations pour rajouter la réservation mais aussi l'ejb du client pour ajouter ou rechercher le client. La gestion de ces
processus complexe doit être isolée des ejb entités qui sont chacun dédiés à la gestion d'une table. Pour cela, une
solution est de créer un ejb dit façade qui se charge des processus métiers. Cet ejb ne gère pas de la persistance mais
synchronise les appels aux ejb persistants, il s'agit donc d'un ejb session sans état.
Les EJB3 entity ne sont pas accessibles directement par le client mais uniquement par un EJB session qui lui peut-être
remote et donc accessible de l’extérieur.
Toujours dans le package hw du ProjetEJB, créez comme précédemment un EJB session remote sans état. Le code sera
le suivant :
package hw;
import java.util.List;
import javax.ejb.Remote;
@Remote
public interface GestionDeStockRemote {
public Produit ajouter(Produit produit);
public Produit rechercherProduit(int id);
public List<Produit> listerTousLesProduits();
}
ASI TD 5 – Anne Lapujade
2
package hw;
import java.util.List;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Stateless
@LocalBean
public class GestionDeStock implements GestionDeStockRemote {
public GestionDeStock() {}
@PersistenceContext
EntityManager em;
@Override
public Produit ajouter(Produit produit) {
em.persist(produit);
return produit;
}
@Override
public List<Produit> listerTousLesProduits() {
return em.createNamedQuery("findAllProducts").getResultList();
}
@Override
public Produit rechercherProduit(int id) {
return em.find(Produit.class, id);
}
}
3 Spécification de la source de données
La spécification EJB3 standardise l'utilisation d'un fichier, nommé persistence.xml, qui permet de préciser des
paramètres techniques liés au mapping objet-relationnel, par exemple le nom de la 'DataSource' à utiliser pour se
connecter à la base de données.
Pour tester notre EJB entity, nous devons définir le fichier persistence.xml et lier la DataSource que nous avons défini en
début d’exercice. D'autre part, l'implémentation EJB3 de JBoss, qui se base sur Hibernate, est en mesure de créer
automatiquement la structure relationnelle correspondant à notre EJB. Dans le projet EJB/ejbModule, dans le sousrépertoire META-INF créer le fichier persistence.xml
Copier le contenu suivant :
<?xml version="1.0" encoding="UTF-8"?>
<persistence 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"
version="1.0">
<persistence-unit name="contactUnit" transaction-type="JTA">
<jta-data-source>java:jboss/datasources/testDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
(Le nom 'IntroEJB3' est purement arbitraire, le nom de la DataSource est celui défini par JBoss, la valeur 'update' de la
propriété 'hibernate.hbm2ddl.auto' indique que nous souhaitons qu'Hibernate crée les tables automatiquement et les
mette à jour si nécessaire).
ASI TD 5 – Anne Lapujade
3
Démarrez le serveur JBoss. Vérifiez que le déploiement s’est bien passé. En fin de lancement du serveur ProjetEJB.jar
se déploie. La console indique si l’opération s’est bien déroulée :
20:34:59,981 INFO [org.jboss.as.server.deployment] (MSC service thread 1-16) JBAS015876: Starting
deployment of "ProjetEJB.jar"
20:35:00,061 INFO [org.jboss.as.jpa] (MSC service thread 1-9) JBAS011401: Read persistence.xml
for testDS
20:35:00,151 INFO
[org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service
thread 1-6) JNDI bindings for session bean named GestionDeStock in deployment unit deployment
"ProjetEJB.jar" are as follows:
java:global/ProjetEJB/GestionDeStock!hw.GestionDeStock
java:app/ProjetEJB/GestionDeStock!hw.GestionDeStock
java:module/GestionDeStock!hw.GestionDeStock
java:global/ProjetEJB/GestionDeStock!hw.GestionDeStockRemote
java:app/ProjetEJB/GestionDeStock!hw.GestionDeStockRemote
java:module/GestionDeStock!hw.GestionDeStockRemote
java:jboss/exported/ProjetEJB/GestionDeStock!hw.GestionDeStockRemote
20:35:00,151 INFO
[org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service
thread 1-6) JNDI bindings for session bean named Hello in deployment unit deployment
"ProjetEJB.jar" are as follows:
java:global/ProjetEJB/Hello!hw.HelloRemote
java:app/ProjetEJB/Hello!hw.HelloRemote
java:module/Hello!hw.HelloRemote
java:jboss/exported/ProjetEJB/Hello!hw.HelloRemote
java:global/ProjetEJB/Hello!hw.Hello
java:app/ProjetEJB/Hello!hw.Hello
java:module/Hello!hw.Hello
20:35:00,322 INFO [org.jboss.as.jpa] (MSC service thread 1-2) JBAS011402: Starting Persistence
Unit Service 'ProjetEJB.jar#testDS'
20:35:00,723 INFO [org.hibernate.annotations.common.Version] (MSC service thread 1-2)
HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
20:35:00,743 INFO [org.hibernate.Version] (MSC service thread 1-2) HHH000412: Hibernate Core
{4.0.1.Final}
20:35:00,743 INFO [org.hibernate.cfg.Environment] (MSC service thread 1-2) HHH000206:
hibernate.properties not found
20:35:00,743 INFO [org.hibernate.cfg.Environment] (MSC service thread 1-2) HHH000021: Bytecode
provider name : javassist
20:35:00,803 INFO [org.hibernate.ejb.Ejb3Configuration] (MSC service thread 1-2) HHH000204:
Processing PersistenceUnitInfo [
name: testDS
...]
20:35:01,123 INFO [org.hibernate.service.jdbc.connections.internal.ConnectionProviderInitiator]
(MSC service thread 1-2) HHH000130: Instantiating explicit connection provider:
org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider
20:35:01,563 INFO [org.hibernate.dialect.Dialect] (MSC service thread 1-2) HHH000400: Using
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
20:35:01,584 INFO [org.hibernate.engine.transaction.internal.TransactionFactoryInitiator] (MSC
service thread 1-2) HHH000268: Transaction strategy:
org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory
20:35:01,594 INFO [org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory] (MSC service thread
1-2) HHH000397: Using ASTQueryTranslatorFactory
20:35:01,784 INFO [org.hibernate.validator.util.Version] (MSC service thread 1-2) Hibernate
Validator 4.2.0.Final
20:35:02,744 INFO [org.hibernate.tool.hbm2ddl.SchemaUpdate] (MSC service thread 1-2) HHH000228:
Running hbm2ddl schema update
20:35:02,744 INFO [org.hibernate.tool.hbm2ddl.SchemaUpdate] (MSC service thread 1-2) HHH000102:
Fetching database metadata
20:35:02,765 INFO [org.hibernate.tool.hbm2ddl.SchemaUpdate] (MSC service thread 1-2) HHH000396:
Updating schema
20:35:02,765 INFO [java.sql.DatabaseMetaData] (MSC service thread 1-2) HHH000262: Table not
found: Produit
20:35:02,765 INFO [java.sql.DatabaseMetaData] (MSC service thread 1-2) HHH000262: Table not
found: Produit
20:35:02,775 INFO [java.sql.DatabaseMetaData] (MSC service thread 1-2) HHH000262: Table not
found: hibernate_sequence
20:35:02,815 INFO [org.hibernate.tool.hbm2ddl.SchemaUpdate] (MSC service thread 1-2) HHH000232:
Schema update complete
20:35:03,305 INFO [org.jboss.as.server] (DeploymentScanner-threads - 2) JBAS018559: Deployed
"ProjetEJB.jar"
ASI TD 5 – Anne Lapujade
4
Démarrez phpmyadmin. Si tout s’est déroulé normalement, JBoss a créé deux tables :
 La table produit
 Hibernate_sequence qui permet à JBoss de gérer l’incrémentation des clés primaires.
4 Création de l’application cliente
Dans le package client du ProjetEJBClient ajoutez une classe GestionDeStockClient avec le code source suivant :
package client;
import hw.GestionDeStock;
import hw.Produit;
import java.util.Iterator;
import java.util.List;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class GestionDeStockClient {
public static void main(String[] args) {
try{
GestionDeStockRemote stock= lookupRemoteStatelessGestionDeStock();
Produit p = stock.ajouter(new Produit("Tomate", 100));
// Petit affichage pour noter que l'objet récupéré à l'issue de l'ajout dispose bien de sa clé
primaire auto générée
System.out.println(p.getId()+" - "+p.getLibelle());
p=stock.ajouter(new Produit("Pomme de terre", 5680));
p=stock.ajouter(new Produit("Orange", 23));
p=stock.ajouter(new Produit("Carotte", 115));
p=stock.ajouter(new Produit("Muscadet", 48));
List<Produit> produits = stock.listerTousLesProduits();
for (Iterator iter = produits.iterator(); iter.hasNext();) {
Produit eachProduit = (Produit) iter.next();
System.out.println(eachProduit);
}
}
p = stock.rechercherProduit(1);
System.out.println("Produit recherché : ");
System.out.println(p.getId()+" - "+p.getLibelle());
} catch (NamingException e) {e.printStackTrace();}
// Connexion au serveur et lookup du bean
private static GestionDeStockRemote lookupRemoteStatelessGestionDeStock() throws NamingException {
GestionDeStockRemote remote=null;
try {
Properties jndiProps = new Properties();
jndiProps.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
InitialContext ctx = new InitialContext(jndiProps);
remote = (GestionDeStockRemote) ctx.lookup("ejb:/ProjetEJB/GestionDeStock!hw.GestionDeStockRemote");
} catch (Exception e) {
e.printStackTrace();
}
return remote;
}
}
Un clic droit sur cette classe puis Run As Java Application et allez vérifier que les données ont bien été rajoutées dans la
base.
Notez que vous avez deux consoles. La console côté client affiche :
ASI TD 5 – Anne Lapujade
5
1 - Tomate
Produit n°3 - Orange - quantite disponible : 23
Produit n°5 - Muscadet - quantite disponible : 48
Produit n°1 - Tomate - quantite disponible : 100
Produit n°4 - Carotte - quantite disponible : 115
Produit n°2 - Pomme de terre - quantite disponible : 5680
Produit recherché :
1 - Tomate
Si vous cliquez de nouveau sur l’icône de la console, vous ouvrez la console côté serveur. JBoss indique toutes les
opérations qui ont été réalisées par l’ORM :
20:44:18,591 INFO
update
[stdout] (EJB default - 1) Hibernate: select next_val as id_val from hibernate_sequence for
20:44:18,601 INFO
next_val=?
[stdout] (EJB default - 1) Hibernate: update hibernate_sequence set next_val= ? where
20:44:18,742 INFO
values (?, ?, ?)
[stdout] (EJB default - 1) Hibernate: insert into Produit (libelle, quantiteEnStock, id)
20:44:18,872 INFO
update
[stdout] (EJB default - 2) Hibernate: select next_val as id_val from hibernate_sequence for
20:44:18,872 INFO
next_val=?
[stdout] (EJB default - 2) Hibernate: update hibernate_sequence set next_val= ? where
20:44:18,903 INFO
values (?, ?, ?)
[stdout] (EJB default - 2) Hibernate: insert into Produit (libelle, quantiteEnStock, id)
20:44:18,913 INFO
update
[stdout] (EJB default - 3) Hibernate: select next_val as id_val from hibernate_sequence for
20:44:18,913 INFO
next_val=?
[stdout] (EJB default - 3) Hibernate: update hibernate_sequence set next_val= ? where
20:44:18,923 INFO
values (?, ?, ?)
[stdout] (EJB default - 3) Hibernate: insert into Produit (libelle, quantiteEnStock, id)
20:44:18,933 INFO
update
[stdout] (EJB default - 4) Hibernate: select next_val as id_val from hibernate_sequence for
20:44:18,933 INFO
next_val=?
[stdout] (EJB default - 4) Hibernate: update hibernate_sequence set next_val= ? where
20:44:18,933 INFO
values (?, ?, ?)
[stdout] (EJB default - 4) Hibernate: insert into Produit (libelle, quantiteEnStock, id)
20:44:18,953 INFO
update
[stdout] (EJB default - 5) Hibernate: select next_val as id_val from hibernate_sequence for
20:44:18,953 INFO
next_val=?
[stdout] (EJB default - 5) Hibernate: update hibernate_sequence set next_val= ? where
20:44:18,953 INFO
values (?, ?, ?)
[stdout] (EJB default - 5) Hibernate: insert into Produit (libelle, quantiteEnStock, id)
20:44:19,053 INFO [stdout] (EJB default - 6) Hibernate: select produit0_.id as id0_, produit0_.libelle as
libelle0_, produit0_.quantiteEnStock as quantite3_0_ from Produit produit0_ order by produit0_.quantiteEnStock
20:44:19,113 INFO [stdout] (EJB default - 7) Hibernate: select produit0_.id as id0_0_, produit0_.libelle as
libelle0_0_, produit0_.quantiteEnStock as quantite3_0_0_ from Produit produit0_ where produit0_.id=?
ASI TD 5 – Anne Lapujade
6
Téléchargement